那位先生

个人站

前世五百次回眸,才能换得今生的一次擦肩而过。


线程的状态和内存模型

1.基本概念

多线程:这个程序(一个进程)运行时产生了不止一个线程。
并行:多个CPU实例或者多台机器同时执行一段处理逻辑,是真正的同时。
并发:通过CPU调度算法,让用户看上去同时执行,实际上并不是真正的同时。
同步:通过人为的控制和调度,保证共享和可变的资源的多线程访问时线程安全的。

2.线程的状态

多线程状态
1.调用join()或sleep()方法,sleep()时间结束或被打断,join中断,IO完成,都会回到Runnable状态。
2.调用wait(),使该线程处于等待池,直到notify()或者notifyAll(),线程会释放到锁定池,释放同步锁后会进入到Runnable状态。 3.对running状态的线程加同步锁,会使其进入等待池/锁定池。

3.显示锁

1.lock和ReentrantLock

public interface Lock {
    void lock();
    void lockInterruptibly() throws InterruptedException;
    boolean tryLock();
    boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
    void unlock();
    Condition newCondition();
}

内置锁局限性:无法中断一个正在等待获取锁的线程、无法在请求获取锁时无限地等待下去。。。 与内置锁不同的是,Lock提供了一种无条件的、可轮询的、定时的以及可中断的操作,所有加锁和解锁的操作都是显示的。
ReentrantLock实现了Lock,并提供了与synchronized相同的互斥性和内存可见性,在获取reentrantLock时进入同步代码块,在释放reeentrantLock时 退出同步代码块,并且和synchronized提供重入锁。

Lock lock = new ReentrantLock();
...
lock.lock();
try{
    //更新对象状态
    //捕获异常,并在必要时恢复不变性条件
}finally{
    lock.unlock();
}

注意:可重入锁必须在finally中释放锁。而且当且仅当内置锁不能满足需求时(比如:可定时的、可轮询的与可中断的锁获取操作,公平队列以及非块结构 的锁)才会考虑使用ReentrantLock。

2.读-写锁

互斥是一种保守的加锁策略,可以避免”写/写”冲突,”读/写”冲突,但也避免了”读/读”冲突,但大多数情况都是读读比较多,因此如何能放宽加锁需求,允许 “读/读”不加锁将会极大的提高性能。”读/写”锁就解决了这个问题,一个资源可以被多个读操作同时访问,或者被一个写操作访问,但两者不能同时进行。

public interface ReadWriteLock {
    //返回读锁
    Lock readLock();
    //返回写锁
    Lock writeLock();
}
//主要实现
ReadWriteLock readWriteLock = new ReentrantReadWriteLock();

要获取由ReadWriteLock保护的数据,首先要获得读锁,要修改则要先获得写锁。
读取锁和写入锁之间的交互方式:

  • 公平性选择:支持公平与非公平的的锁获取方式,吞吐量非公平优先于公平。
  • 可重入:读线程获取锁后,可以再次获取读锁,写锁也一样。
  • 可降级:写线程获得写锁后,还可以再次获得读锁,在释放写锁后此时线程是读锁状态,这就是降级。

原理:ReentrantReadWriteLock是由一个基于AQS的同步器Sync构成,而后扩展出ReadLock(共享锁),WriteLock(排他锁)所组成。
读写锁的实现原理

4.原子变量

与锁相比,volatile变量时一种更轻量级的同步机制,因为在使用这些变量的时候不会发生上下文切换或线程调度等工作。然而它也有局限性: 不能用于构建原子的复合操作,因此当一个变量依赖其它的变量时,或者当变量的新值依赖旧值时就不能用volatile变量。
原子变量相当于一种泛化的volatile变量,能够支持原子的和有条件的读-写-改操作。比如,AtomicInteger表示一个int类型的值,并提供get、set方法和 原子的compareAndSet方法,原子的添加、递增和递减等方法。
共有12个原子变量类,分4组:标量类、更新器类、数组类和复合变量类。常见的就是标量类:AtomicInteger、AtomicLong、AtomicReference、AtomicBoolean。
原理:比较并交换(CAS):包含3个操作数,需要读写的内存位置V,进行比较的值A,拟写入的新值B。当且仅当如果V的值等于A,则用B来更新V的值,否则 不执行任何操作。

5.死锁

1.死锁的原因

死锁:多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放,而该资源又被其他线程锁定,从而导致每一个线程都得等其它线程释放其锁定的资源, 造成了所有线程都无法正常结束。
死锁产生原因:系统资源的竞争、进程推进顺序非法。
死锁产生的四个必要条件:

  • 互斥使用,即当资源被一个线程使用(占有)时,别的线程不能使用
  • 不可剥夺,资源请求者不能强制从资源占有者手中夺取资源,资源只能由资源占有者主动释放。
  • 请求和保持,即当资源请求者在请求其他的资源的同时保持对原有资源的占有。
  • 循环等待,即存在一个等待队列:P1占有P2的资源,P2占有P3的资源,P3占有P1的资源。这样就形成了一个等待环路。

2.如何避免死锁

  • 加锁顺序(线程按照一定的顺序加锁)
  • 加锁时限(线程尝试获取锁的时候加上一定的时限,超过时限则放弃对该锁的请求,并释放自己占有的锁)
  • 死锁检测

参考链接

6.JAVA内存模型

浅析java内存模型

取消

感谢您的支持,我会继续努力的!

扫码支持
扫码支持
你说多少就多少

比五毛钱特效专业哦