Lock与synchronized 的区别

1、ReentrantLock 拥有Synchronized相同的并发性和内存语义,此外还多了 锁投票,定时锁等候和中断锁等候

     线程A和B都要获取对象O的锁定,假设A获取了对象O锁,B将等待A释放对O的锁定,

     如果使用 synchronized ,如果A不释放,B将一直等下去,不能被中断

     如果 使用ReentrantLock,如果A不释放,可以使B在等待了足够长的时间以后,中断等待,而干别的事情

 

    ReentrantLock获取锁定与三种方式:
    a)  lock(), 如果获取了锁立即返回,如果别的线程持有锁,当前线程则一直处于休眠状态,直到获取锁

    b) tryLock(), 如果获取了锁立即返回true,如果别的线程正持有锁,立即返回false;

    c)tryLock(long timeout,TimeUnit unit),   如果获取了锁定立即返回true,如果别的线程正持有锁,会等待参数给定的时间,在等待的过程中,如果获取了锁定,就返回true,如果等待超时,返回false;

    d) lockInterruptibly:如果获取了锁定立即返回,如果没有获取锁定,当前线程处于休眠状态,直到或者锁定,或者当前线程被别的线程中断

 

2、synchronized是在JVM层面上实现的,不但可以通过一些监控工具监控synchronized的锁定,而且在代码执行时出现异常,JVM会自动释放锁定,但是使用Lock则不行,lock是通过代码实现的,要保证锁定一定会被释放,就必须将unLock()放到finally{}中

 

3、在资源竞争不是很激烈的情况下,Synchronized的性能要优于ReetrantLock,但是在资源竞争很激烈的情况下,Synchronized的性能会下降几十倍,但是ReetrantLock的性能能维持常态;


处处都好?

看起来 ReentrantLock 无论在哪方面都比 synchronized 好 —— 所有 synchronized 能做的,它都能做,它拥有与 synchronized 相同的内存和并发性语义,还拥有 synchronized 所没有的特性,在负荷下还拥有更好的性能。那么,我们是不是应当忘记 synchronized ,不再把它当作已经已经得到优化的好主意呢?或者甚至用ReentrantLock 重写我们现有的 synchronized 代码?实际上,几本 Java 编程方面介绍性的书籍在它们多线程的章节中就采用了这种方法,完全用 Lock 来做示例,只把 synchronized 当作历史。但我觉得这是把好事做得太过了。

不要抛弃 synchronized

虽然 ReentrantLock 是个非常动人的实现,相对 synchronized 来说,它有一些重要的优势,但是我认为急于把 synchronized 视若敝屣,绝对是个严重的错误。 java.util.concurrent.lock 中的锁定类是用于高级用户和高级情况的工具 。一般来说,除非您对 Lock 的某个高级特性有明确的需要,或者有明确的证据(而不是仅仅是怀疑)表明在特定情况下,同步已经成为可伸缩性的瓶颈,否则还是应当继续使用 synchronized。

为什么我在一个显然“更好的”实现的使用上主张保守呢?因为对于 java.util.concurrent.lock 中的锁定类来说,synchronized 仍然有一些优势。比如,在使用 synchronized 的时候,不能忘记释放锁;在退出synchronized 块时,JVM 会为您做这件事。您很容易忘记用 finally 块释放锁,这对程序非常有害。您的程序能够通过测试,但会在实际工作中出现死锁,那时会很难指出原因(这也是为什么根本不让初级开发人员使用Lock 的一个好理由。)

另一个原因是因为,当 JVM 用 synchronized 管理锁定请求和释放时,JVM 在生成线程转储时能够包括锁定信息。这些对调试非常有价值,因为它们能标识死锁或者其他异常行为的来源。 Lock 类只是普通的类,JVM 不知道具体哪个线程拥有 Lock 对象。而且,几乎每个开发人员都熟悉 synchronized,它可以在 JVM 的所有版本中工作。在 JDK 5.0 成为标准(从现在开始可能需要两年)之前,使用 Lock 类将意味着要利用的特性不是每个 JVM 都有的,而且不是每个开发人员都熟悉的。

什么时候选择用 ReentrantLock 代替 synchronized

既然如此,我们什么时候才应该使用 ReentrantLock 呢?答案非常简单 —— 在确实需要一些 synchronized 所没有的特性的时候,比如时间锁等候、可中断锁等候、无块结构锁、多个条件变量或者锁投票。 ReentrantLock还具有可伸缩性的好处,应当在高度争用的情况下使用它,但是请记住,大多数 synchronized 块几乎从来没有出现过争用,所以可以把高度争用放在一边。我建议用 synchronized 开发,直到确实证明 synchronized 不合适,而不要仅仅是假设如果使用 ReentrantLock “性能会更好”。请记住,这些是供高级用户使用的高级工具。(而且,真正的高级用户喜欢选择能够找到的最简单工具,直到他们认为简单的工具不适用为止。)。一如既往,首先要把事情做好,然后再考虑是不是有必要做得更快。

Lock 框架是同步的兼容替代品,它提供了 synchronized 没有提供的许多特性,它的实现在争用下提供了更好的性能。但是,这些明显存在的好处,还不足以成为用 ReentrantLock 代替 synchronized 的理由。相反,应当根据您是否 需要 ReentrantLock 的能力来作出选择。大多数情况下,您不应当选择它 —— synchronized 工作得很好,可以在所有 JVM 上工作,更多的开发人员了解它,而且不太容易出错。只有在真正需要 Lock 的时候才用它。在这些情况下,您会很高兴拥有这款工具。



各种同步方法性能比较(synchronized,ReentrantLock,Atomic)参考http://zzhonghe.iteye.com/blog/826162

5.0的多线程任务包对于同步的性能方面有了很大的改进,在原有synchronized关键字的基础上,又增加了ReentrantLock,以及各种Atomic类。了解其性能的优劣程度,有助与我们在特定的情形下做出正确的选择。 

总体的结论先摆出来:  

synchronized: 
在资源竞争不是很激烈的情况下,偶尔会有同步的情形下,synchronized是很合适的。原因在于,编译程序通常会尽可能的进行优化synchronize,另外可读性非常好,不管用没用过5.0多线程包的程序员都能理解。 

ReentrantLock: 
ReentrantLock提供了多样化的同步,比如有时间限制的同步,可以被Interrupt的同步(synchronized的同步是不能Interrupt的)等。在资源竞争不激烈的情形下,性能稍微比synchronized差点点。但是当同步非常激烈的时候,synchronized的性能一下子能下降好几十倍。而ReentrantLock确还能维持常态。 

Atomic: 
和上面的类似,不激烈情况下,性能比synchronized略逊,而激烈的时候,也能维持常态。激烈的时候,Atomic的性能会优于ReentrantLock一倍左右。但是其有一个缺点,就是只能同步一个值,一段代码中只能出现一个Atomic的变量,多于一个同步无效。因为他不能在多个Atomic之间同步。 


所以,我们写同步的时候,优先考虑synchronized,如果有特殊需要,再进一步优化。ReentrantLock和Atomic如果用的不好,不仅不能提高性能,还可能带来灾难。 

先贴测试结果:再贴代码(Atomic测试代码不准确,一个同步中只能有1个Actomic,这里用了2个,但是这里的测试只看速度) 
========================== 
round:100000 thread:5 
Sync = 35301694 
Lock = 56255753 
Atom = 43467535 
========================== 
round:200000 thread:10 
Sync = 110514604 
Lock = 204235455 
Atom = 170535361 
========================== 
round:300000 thread:15 
Sync = 253123791 
Lock = 448577123 
Atom = 362797227 
========================== 
round:400000 thread:20 
Sync = 16562148262 
Lock = 846454786 
Atom = 667947183 
========================== 
round:500000 thread:25 
Sync = 26932301731 
Lock = 1273354016 
Atom = 982564544 


Java代码   收藏代码
  1. package test.thread;  
  2.   
  3. import static java.lang.System.out;  
  4.   
  5. import java.util.Random;  
  6. import java.util.concurrent.BrokenBarrierException;  
  7. import java.util.concurrent.CyclicBarrier;  
  8. import java.util.concurrent.ExecutorService;  
  9. import java.util.concurrent.Executors;  
  10. import java.util.concurrent.atomic.AtomicInteger;  
  11. import java.util.concurrent.atomic.AtomicLong;  
  12. import java.util.concurrent.locks.ReentrantLock;  
  13.   
  14. public class TestSyncMethods {  
  15.       
  16.     public static void test(int round,int threadNum,CyclicBarrier cyclicBarrier){  
  17.         new SyncTest("Sync",round,threadNum,cyclicBarrier).testTime();  
  18.         new LockTest("Lock",round,threadNum,cyclicBarrier).testTime();  
  19.         new AtomicTest("Atom",round,threadNum,cyclicBarrier).testTime();  
  20.     }  
  21.   
  22.     public static void main(String args[]){  
  23.           
  24.         for(int i=0;i<5;i++){  
  25.             int round=100000*(i+1);  
  26.             int threadNum=5*(i+1);  
  27.             CyclicBarrier cb=new CyclicBarrier(threadNum*2+1);  
  28.             out.println("==========================");  
  29.             out.println("round:"+round+" thread:"+threadNum);  
  30.             test(round,threadNum,cb);  
  31.               
  32.         }  
  33.     }  
  34. }  
  35.   
  36. class SyncTest extends TestTemplate{  
  37.     public SyncTest(String _id,int _round,int _threadNum,CyclicBarrier _cb){  
  38.         super( _id, _round, _threadNum, _cb);  
  39.     }  
  40.     @Override  
  41.     /** 
  42.      * synchronized关键字不在方法签名里面,所以不涉及重载问题 
  43.      */  
  44.     synchronized long  getValue() {  
  45.         return super.countValue;  
  46.     }  
  47.     @Override  
  48.     synchronized void  sumValue() {  
  49.         super.countValue+=preInit[index++%round];  
  50.     }  
  51. }  
  52.   
  53.   
  54. class LockTest extends TestTemplate{  
  55.     ReentrantLock lock=new ReentrantLock();  
  56.     public LockTest(String _id,int _round,int _threadNum,CyclicBarrier _cb){  
  57.         super( _id, _round, _threadNum, _cb);  
  58.     }  
  59.     /** 
  60.      * synchronized关键字不在方法签名里面,所以不涉及重载问题 
  61.      */  
  62.     @Override  
  63.     long getValue() {  
  64.         try{  
  65.             lock.lock();  
  66.             return super.countValue;  
  67.         }finally{  
  68.             lock.unlock();  
  69.         }  
  70.     }  
  71.     @Override  
  72.     void sumValue() {  
  73.         try{  
  74.             lock.lock();  
  75.             super.countValue+=preInit[index++%round];  
  76.         }finally{  
  77.             lock.unlock();  
  78.         }  
  79.     }  
  80. }  
  81.   
  82.   
  83. class AtomicTest extends TestTemplate{  
  84.     public AtomicTest(String _id,int _round,int _threadNum,CyclicBarrier _cb){  
  85.         super( _id, _round, _threadNum, _cb);  
  86.     }  
  87.     @Override  
  88.     /** 
  89.      * synchronized关键字不在方法签名里面,所以不涉及重载问题 
  90.      */  
  91.     long  getValue() {  
  92.         return super.countValueAtmoic.get();  
  93.     }  
  94.     @Override  
  95.     void  sumValue() {  
  96.         super.countValueAtmoic.addAndGet(super.preInit[indexAtomic.get()%round]);  
  97.     }  
  98. }  
  99. abstract class TestTemplate{  
  100.     private String id;  
  101.     protected int round;  
  102.     private int threadNum;  
  103.     protected long countValue;  
  104.     protected AtomicLong countValueAtmoic=new AtomicLong(0);  
  105.     protected int[] preInit;  
  106.     protected int index;  
  107.     protected AtomicInteger indexAtomic=new AtomicInteger(0);  
  108.     Random r=new Random(47);  
  109.     //任务栅栏,同批任务,先到达wait的任务挂起,一直等到全部任务到达制定的wait地点后,才能全部唤醒,继续执行  
  110.     private CyclicBarrier cb;  
  111.     public TestTemplate(String _id,int _round,int _threadNum,CyclicBarrier _cb){  
  112.         this.id=_id;  
  113.         this.round=_round;  
  114.         this.threadNum=_threadNum;  
  115.         cb=_cb;  
  116.         preInit=new int[round];  
  117.         for(int i=0;i<preInit.length;i++){  
  118.             preInit[i]=r.nextInt(100);  
  119.         }  
  120.     }  
  121.       
  122.     abstract void sumValue();  
  123.     /* 
  124.      * 对long的操作是非原子的,原子操作只针对32位 
  125.      * long是64位,底层操作的时候分2个32位读写,因此不是线程安全 
  126.      */  
  127.     abstract long getValue();  
  128.   
  129.     public void testTime(){  
  130.         ExecutorService se=Executors.newCachedThreadPool();  
  131.         long start=System.nanoTime();  
  132.         //同时开启2*ThreadNum个数的读写线程  
  133.         for(int i=0;i<threadNum;i++){  
  134.             se.execute(new Runnable(){  
  135.                 public void run() {  
  136.                     for(int i=0;i<round;i++){  
  137.                         sumValue();  
  138.                     }  
  139.   
  140.                     //每个线程执行完同步方法后就等待  
  141.                     try {  
  142.                         cb.await();  
  143.                     } catch (InterruptedException e) {  
  144.                         // TODO Auto-generated catch block  
  145.                         e.printStackTrace();  
  146.                     } catch (BrokenBarrierException e) {  
  147.                         // TODO Auto-generated catch block  
  148.                         e.printStackTrace();  
  149.                     }  
  150.   
  151.   
  152.                 }  
  153.             });  
  154.             se.execute(new Runnable(){  
  155.                 public void run() {  
  156.   
  157.                     getValue();  
  158.                     try {  
  159.                         //每个线程执行完同步方法后就等待  
  160.                         cb.await();  
  161.                     } catch (InterruptedException e) {  
  162.                         // TODO Auto-generated catch block  
  163.                         e.printStackTrace();  
  164.                     } catch (BrokenBarrierException e) {  
  165.                         // TODO Auto-generated catch block  
  166.                         e.printStackTrace();  
  167.                     }  
  168.   
  169.                 }  
  170.             });  
  171.         }  
  172.           
  173.         try {  
  174.             //当前统计线程也wait,所以CyclicBarrier的初始值是threadNum*2+1  
  175.             cb.await();  
  176.         } catch (InterruptedException e) {  
  177.             // TODO Auto-generated catch block  
  178.             e.printStackTrace();  
  179.         } catch (BrokenBarrierException e) {  
  180.             // TODO Auto-generated catch block  
  181.             e.printStackTrace();  
  182.         }  
  183.         //所有线程执行完成之后,才会跑到这一步  
  184.         long duration=System.nanoTime()-start;  
  185.         out.println(id+" = "+duration);  
  186.           
  187.     }  
  188.   
  189. }  



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值