synchronized关键字的底层原理

synchronized是互斥锁,同一时刻只有一个线程能持有对象锁,synchronized基于monitor实现,修饰代码块时会使用monitorenter和monitorexit指令,当一个线程执行monitorenter指令时,会尝试获取对象的锁,如果成功,则执行代码块,执行完毕后释放锁,如果失败,则阻塞线程,直到锁被释放。monitor中由waitset,entrylist,owner组成。owner中存储获取对象锁的线程,只有一个线程在owner里面,entrylist中存放的是没有抢到锁的线程,当owner里面没有线程的时候entrylist里面的线程会进行争抢,最后抢到锁的线程进入owner,其他线程仍然在entrylist处于阻塞状态。waitset中存放的是调用了wait方法的线程。

synchronized和lock区别

synchronized是Java关键字,由C++实现,lock是java.util.concurrent包下的类。使用synchronized时退出同步代码块锁会自动释放,使用lock时需手动调用unlock释放锁。
二者都属于悲观锁,但是lock提供了比synchronized更高级的功能,比如可中断锁、超时锁、条件变量等。
在没有竞争时synchronized做了很多优化,在竞争激烈时lock会有更优秀的性能。

JMM(Java内存模型)

JMM是Java的内存模型,定义了共享内存中多线程程序读写操作的行为规范,通过规则来规范内存读写操作从而保证指令正确性。
JMM把内存分成两块,一块是私有线程的工作区域(工作内存),一块是所有线程的共享区域(主内存)。
线程和线程之间相互隔离,交互需要通过主内存实现。

CAS

乐观锁和悲观锁

乐观锁顾名思义,心态比较乐观,觉得运行时不会有冲突,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是如果有冲突,就只能重试,直到成功为止。CAS(Compare And Swap)就是一种乐观锁的实现。
悲观锁,心态比较悲观,认为运行时会有冲突,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁。synchronized就是悲观锁的一种实现。

CAS的原理

CAS主要在无锁状态下保证线程操作的原子性。使用自旋锁,线程没有阻塞,效率高。底层调用Unsafe方法。

volatile关键字

1.volatile可以保证线程的可见性,阻止编译器优化的发生,让一个线程对共享变量的修改对另一个线程可见。
2.volatile可以阻止指令的重排序,在读,写变量时加入屏障,阻止其他读写操作越过屏障。

AQS

工作机制

AQS是多线程中的队列同步器,ReentrantLock和Semaphore都是基于AQS实现的。AQS的内部有一个FIFO的队列,队列中存储的是排队的线程。在AQS中还有一个state属性,用来标识共享资源的状态。默认为0(无锁)如果队列中有一个线程将state设置为1(加锁),则其他线程只能阻塞等待。在对state进行修改时,需要使用CAS来保证原子性。且AQS可以实现公平锁,也可以实现非公平锁。取决于新线程是否和队列的第一个线程抢资源。抢就是非公平锁,直接排到队列末尾就是公平锁。

ReentrantLock

ReetrantLock主要基于CAS+AQS实现,支持公平锁和非公平锁。构造方法可以指定公平锁和非公平锁。公平参数默认为true时为公平锁。公平锁的效率没有非公平锁高。

实现原理

线程抢到锁后使用CAS修改state为1,让exclusiveOwnerThread属性指向当前线程,获取锁成功。若修改状态失败,则会进入双向队列等待。head指向队列头,tail指向队列未。当exclusiveOwenrThread为null时,则会唤醒双向队列中的等待线程。

多线程死锁

产生条件

互斥条件:资源不能被多线程共享,一次只能由一个线程使用。若一个线程已经占用了某个资源,其他线程必须等待。
不可抢占条件:资源只能由拥有者释放,不能被其他线程强行抢占。
请求与保持条件:线程因请求资源而阻塞时,对已获得的资源保持不放。
循环等待条件:若资源的拥有线程在等待时,发生了资源的再次请求,则产生循环等待。

死锁预防

互斥死锁无法改变
1.破坏不可抢占条件:若占用资源的线程进一步申请其他资源,则必须释放当前资源。
2.破坏请求与保持条件:一次性申请所有资源。
3.破坏循环等待条件:对资源进行排序,强制资源按顺序申请。

死锁判断

出现死锁可以使用jdk自带的工具来判断 : jps 和 jstack

Java中保证多线程执行安全(Java并发三大特性)

原子性 线程在操作中不可暂停也不可中断,要么不执行,要么完成。
要保证其可见性需要使用synchronized加锁。
可见性 一个线程对共享变量的修改,其他线程立即可以看到。
要保证可见性可以用volatile关键字。
有序性 程序执行的顺序按照代码的先后顺序执行。
synchronized或者volatile都可以保证多线程之间操作的有序性。

ThreadLocal