java的两种同步方式, Synchronized与ReentrantLock的区别

  java在编写多线程程序时,为了保证线程安全,需要对数据同步,经常用到两种同步方式就是Synchronized和重入锁ReentrantLock。

相似点:

       这两种同步方式有很多相似之处,它们都是加锁方式同步,而且都是阻塞式的同步,也就是说当如果一个线程获得了对象锁,进入了同步块,其他访问该同步块的线程都必须阻塞在同步块外面等待,而进行线程阻塞和唤醒的代价是比较高的(操作系统需要在用户态与内核态之间来回切换,代价很高,不过可以通过对锁优化进行改善)。

区别:

      这两种方式最大区别就是对于Synchronized来说,它是java语言的关键字,是原生语法层面的互斥,需要jvm实现。而ReentrantLock它是JDK 1.5之后提供的API层面的互斥锁,需要lock()和unlock()方法配合try/finally语句块来完成。

1.Synchronized

    Synchronized进过编译,会在同步块的前后分别形成monitorenter和monitorexit这个两个字节码指令。在执行monitorenter指令时,首先要尝试获取对象锁。如果这个对象没被锁定,或者当前线程已经拥有了那个对象锁,把锁的计算器加1,相应的,在执行monitorexit指令时会将锁计算器就减1,当计算器为0时,锁就被释放了。如果获取对象锁失败,那当前线程就要阻塞,直到对象锁被另一个线程释放为止。

[java] view plain copy
  1. public class SynDemo{  
  2.   
  3.     public static void main(String[] arg){  
  4.         Runnable t1=new MyThread();  
  5.         new Thread(t1,"t1").start();  
  6.         new Thread(t1,"t2").start();  
  7.     }  
  8.   
  9. }  
  10. class MyThread implements Runnable {  
  11.   
  12.     @Override  
  13.     public void run() {  
  14.         synchronized (this) {  
  15.             for(int i=0;i<10;i++)  
  16.                 System.out.println(Thread.currentThread().getName()+":"+i);  
  17.         }  
  18.           
  19.     }  
  20.   
  21. }  
查看字节码指令:



2.ReentrantLock

   由于ReentrantLock是java.util.concurrent包下提供的一套互斥锁,相比Synchronized,ReentrantLock类提供了一些高级功能,主要有以下3项:

        1.等待可中断,持有锁的线程长期不释放的时候,正在等待的线程可以选择放弃等待,这相当于Synchronized来说可以避免出现死锁的情况。

        2.公平锁,多个线程等待同一个锁时,必须按照申请锁的时间顺序获得锁,Synchronized锁非公平锁,ReentrantLock默认的构造函数是创建的非公平锁,可以通过参数true设为公平锁,但公平锁表现的性能不是很好。

        3.锁绑定多个条件,一个ReentrantLock对象可以同时绑定对个对象。

ReentrantLock的用法如下:

[java] view plain copy
  1. public class SynDemo{  
  2.   
  3.     public static void main(String[] arg){  
  4.         Runnable t1=new MyThread();  
  5.         new Thread(t1,"t1").start();  
  6.         new Thread(t1,"t2").start();  
  7.     }  
  8.   
  9. }  
  10. class MyThread implements Runnable {  
  11.   
  12.     private Lock lock=new ReentrantLock();  
  13.     public void run() {  
  14.             lock.lock();  
  15.             try{  
  16.                 for(int i=0;i<5;i++)  
  17.                     System.out.println(Thread.currentThread().getName()+":"+i);  
  18.             }finally{  
  19.                 lock.unlock();  
  20.             }  
  21.     }  
  22.   
  23. }  


二者的区别

区别一:API层面

synchronized使用

synchronized既可以修饰方法,也可以修饰代码块。 
synchronized修饰方法时,如下所示:

//synchronized修饰一个方法时,这个方法叫同步方法。
public synchronized void test() {
//方法体``

}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5

synchroized修饰代码块时,包含两部分:锁对象的引用和这个锁保护的代码块。如下所示:

synchronized(Object) {
//括号中表示需要锁的对象.
//线程执行的时候会对Object上锁
}
 
 
  • 1
  • 2
  • 3
  • 4

ReentrantLock使用

public class test(){
    private Lock lock = new ReentrantLock();
    public void testMethod()
    {
        try
        {
            lock.lock();
            ```
            //省略

        }
        finally
        {
            lock.unlock();
        }
    }
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

区别二:等待可中断

引用周志明的《深入理解Java虚拟机》Page 392

等待可中断是指当持有锁的线程长期不释放锁的时候,正在等待的线程可以选择放弃等待,改为处理其他事情。可等待特性对处理执行时间非常长的同步快很有帮助。

具体来说,假如业务代码中有两个线程,Thread1 Thread2。假设 Thread1 获取了对象object的锁,Thread2将等待Thread1释放object的锁。

  • 使用synchronized。如果Thread1不释放,Thread2将一直等待,不能被中断。synchronized也可以说是Java提供的原子性内置锁机制。内部锁扮演了互斥锁(mutual exclusion lock ,mutex)的角色,一个线程引用锁的时候,别的线程阻塞等待。

  • 使用ReentrantLock。如果Thread1不释放,Thread2等待了很长时间以后,可以中断等待,转而去做别的事情。

区别三:公平锁

引用周志明的《深入理解Java虚拟机》Page 392

公平锁是指多个线程在等待同一个锁时,必须按照申请的时间顺序来依次获得锁;而非公平锁则不能保证这一点。非公平锁在锁被释放时,任何一个等待锁的线程都有机会获得锁。 
synchronized的锁是非公平锁,ReentrantLock默认情况下也是非公平锁,但可以通过带布尔值的构造函数要求使用公平锁。

区别四:锁绑定多个条件

ReentrantLock可以同时绑定多个Condition对象,只需多次调用newCondition方法即可。 
synchronized中,锁对象的wait()和notify()或notifyAll()方法可以实现一个隐含的条件。但如果要和多于一个的条件关联的时候,就不得不额外添加一个锁。

区别五:性能

JDK 1.5中,synchronized还有很大的优化余地。JDK 1.6 中加入了很多针对锁的优化措施,synchronized与ReentrantLock性能方面基本持平。虚拟机在未来的改进中更偏向于原生的synchronized。

补充:关于synchronized关键字

  1. Java中每个对象都有一个锁(lock)或者叫做监视器(monitor)。
  2. ReentrantLock和synchronized持有的对象监视器不同。
  3. 如果某个synchronized方法是static的,那么当线程方法改方法时,它锁的并不是synchronized方法所在的对象,而是synchronized方法所在对象所对应的Class对象,因为Java中不管一个类有多少对象,这些对象会对应唯一一个Class对象。因此当线程分别访问同一个类的两个对象的两个static,synchronized方法时,是顺序执行的,亦即一个线程先执行,完毕之后,另一个才开始执行。
  4. synchronized 方法是一种粗粒度的并发控制,某一时刻,只能有一个线程执行synchronized方法;synchronized块则是一种细粒度的并发控制。只会将块中代码同步,位于方法内,synchronized块之外的代码是可以被多个线程同时访问的。
  5. synchronized关键字经过编译之后,会在同步块的前后分别形成monitorenter和monitorexit两个字节码指令,操作对象均为锁的计数器。 
    6.相同点:都是可重入的。可重入值的是同一个线程多次试图获取它所占的锁,请求会成功。当释放的时候,直到冲入次数清零,锁才释放。



          对ReentrantLock的源码分析这有一篇很好的文章http://www.blogjava.net/zhanglongsr/articles/356782.html
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: synchronized和reentrantlock都是Java用于实现线程同步的机制,它们的主要区别如下: 1. 的获取方式不同:synchronized是隐式,即在进入同步代码或方法时自动获取,退出时自动释放;而reentrantlock是显式,需要手动获取和释放。 2. 的可重入性不同:synchronized是可重入,即同一个线程可以多次获取同一把,而不会造成死;而reentrantlock也是可重入,但需要手动实现。 3. 的公平性不同:synchronized是非公平,即线程获取的顺序是不确定的;而reentrantlock可以是公平或非公平,可以通过构造函数指定。 4. 的灵活性不同:reentrantlock提供了更多的灵活性,比如可以实现公平、可、超时等功能,而synchronized则不支持这些功能。 总之,synchronizedJava最基本的同步机制,使用简单,但功能相对较少;而reentrantlock则提供了更多的功能和灵活性,但使用相对复杂。在实际开发,应根据具体情况选择合适的同步机制。 ### 回答2: synchronized和ReentrantLock都是用于实现多线程同步的机制。它们的主要区别如下: 1. 使用方式synchronizedJava提供的关键字,可以直接在方法或者代码上使用,而ReentrantLock是一个类,需要显式地创建实例。 2. 的获取与释放:synchronized是隐式地获取和释放的,当一个线程获取到后,其他线程必须等待的释放才能获得执行权限。而ReentrantLock使用lock()方法来获取,并用unlock()方法来释放,这样更加灵活,可以解的线程不一定是获取的线程。 3. 可重入性:ReentrantLock是可重入,同一个线程可以多次获得同一个,而synchronized是非重入,即同一个线程在获取到之后再次获取会导致死。 4. 条件变量:ReentrantLock提供了 Condition 接口来实现线程间的等待和唤醒机制,而synchronized不提供类似的功能。 5. 性能:在竞争不激烈的情况下,synchronized的性能是很高的。但在竞争激烈的情况下,ReentrantLock的性能会更好,因为它提供了公平和非公平的选择,以及可重入特性的支持,能够更好地控制线程的执行顺序和调度。 综上所述,synchronized是一种简单易用的机制,适合在简单的同步场景使用。而ReentrantLock则更加灵活、功能更强大,适合在复杂的多线程同步场景使用。 ### 回答3: synchronized和ReentrantLockJava两种用于实现线程同步方式。 1. 原理不同:synchronizedJava的关键字,通过使用内置的监视器(monitor)来实现线程同步;而ReentrantLock是一个类,它使用独占(exclusive lock)实现线程同步。 2. 的获取情况不同:synchronized是隐式获取和释放,当一个线程进入synchronized代码,它会自动获取,执行完后自动释放;而ReentrantLock则需要显式地调用lock()方法来获取,而且在使用完后必须调用unlock()方法手动释放。 3. 的可重入性不同:ReentrantLock支持可重入性,允许线程多次获取同一个,避免了自身被阻塞的情况;而synchronized只能一次性获取,如果一个线程已经拥有了,则再次尝试获取时会被阻塞。 4. 的公平性不同:ReentrantLock提供了公平和非公平的选择,可以通过构造函数来决定;而synchronized只能是非公平。 5. 的可断性不同:ReentrantLock可以响应断,当一个线程等待获取时,可以通过调用interrupt()方法来断等待;而synchronized不能响应断,如果一个线程在等待时被断,它会继续等待而不会断。 总的来说,synchronized是更加简单、易于使用的线程同步方式,适用于大多数场景;而ReentrantLock则提供了更多的灵活性和功能,适用于复杂的线程同步需求。但是,由于ReentrantLock需要手动获取和释放,务必要注意及时释放,防止出现死等问题。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值