synchronized与ReentrantLock、Volatile的区别

1. 先进行以下名词解释

  • 死锁:两个或多个线程都在等待获取对方所持有的锁的现象。
    比如:线程1持有锁A,尝试获取锁B;而线程2持有锁B,尝试获取锁A。线程1和线程2想要获取的锁都在对方那里,并且双方都没有释放所持有的锁,从而一直阻塞,造成死锁。
  • 可见性:当一个线程修改了线程共享变量的值,其他线程能立即感知这个修改。
  • 原子性:多线程并发情况下,不会出现被修改的情况,即一个操作中就是cpu,不可以中途暂停再调度。要么不执行,如果执行就执行到底(不允许被打断)
  • 可重入锁:自己可以再次获取自己的内部锁。
    比如:一个线程获得了某个对象的锁,并且还没有释放,当这个线程再次想要获取这个对象的锁的时候还是可以获取的,如果不可锁重入的话,就会造成死锁。
  • 公平锁:先等待的线程先获得锁。

2. Synchronized可重入的实现原理

每一个Synchronized可重入锁都关联一个线程持有者和一个计数器。当计数器为0时,表示该锁可以被任何线程获取,一旦有线程成功获取该锁,计数器为1。如果其他线程也申请该锁,则会进入等待状态,直到持有该锁的线程释放锁;但是如果持有锁的线程再次申请该锁,程序计数器会加1(前提是该锁是可重入锁,否则进入等待状态)。当退出一次Synchronized方法或代码块时,计数器减1,直至计数器为0,线程释放锁。

3. Synchronized与ReentrantLock的区别

  • 两者都是可重入锁
  • Synchronized是依赖于JVM实现的;而ReentrantLock是依赖于JDK实现的,是API层面的,需要Lock()和unLock()方法配合try/finally语句块来完成。
  • ReentrantLock比Synchronized多了三项功能:等待可中断,可实现公平锁,可实现选择性通知(锁可绑定多个条件)。
    关于上述三项功能的解释:
    等待可中断是指线程可以选择放弃等待,改为处理其他事情,ReenTrantLock通过lock.lockInterruptibly()来实现的。
    Synchronized只能是非公平锁。ReenTrantLock默认情况是非公平的,可以通过 ReentrantLock(boolean fair)构造方法来指定是公平锁还是非公平锁。
    Synchronized关键字与wait()和notify/notifyAll()方法相结合可以实现等待/通知机制,但被通知的线程是由JVM随机选择。Synchronized相当于整个Lock对象中只有一个单一的condition对象,所有的线程都注册在它一个对象身上。线程开始notifyAll()时,需要通知所有等待的线程,没有选择权,会造成很大的效率问题。而ReenTrantLock可以借助Condition对象实现选择性通知,具有更好的灵活性。比如:可以在一个Lock对象里创建多个Condition实例(即对象监视器),线程对像可以注册在指定的Condition中,从而可以有选择的进行线程通知,即使使用signalAll()方法也只会唤醒注册在该Condition实例中所有等待的线程。

4. Volatile的介绍

Volatile使变量在多个线程间可见。一般,线程通过工作内存写入(读取)主内存中的变量,而Volatile将强制在主内存中写入(读取)变量。

5. Synchronized和Volatile的区别

  • volatile是线程同步的轻量级实现,所以volatile的性能比Synchronized好。
  • Volatile只能修饰变量,而Synchronized可以修饰方法以及代码块。
  • 多线程访问Volatile不会发生线程组塞,而Synchronized会出现阻塞。
  • Volatile可以保证数据的可见性,但不能保证原子性;而Synchronized既可以保证原子性,也可以间接保证可见性(因为它会将私有内存和公共内存中的数据做同步)。
  • Volatile解决的是变量在多个线程之间的可见性,而Synchronized解决的是多个线程之间访问资源的同步性(只有获取锁的线程执行,其他线程等待)。

你可能感兴趣的:(java_多线程)