Discuz! Board

 找回密码
 立即注册
搜索
热搜: 活动 交友 discuz
查看: 1765|回复: 0
打印 上一主题 下一主题

对比synchronized与java.util.concurrent.locks.Lock 的异同

[复制链接]

697

主题

1142

帖子

4086

积分

认证用户组

Rank: 5Rank: 5

积分
4086
跳转到指定楼层
楼主
发表于 2019-4-27 16:54:10 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

简要答案:

   1.Lock能完成几乎所有synchronized的功能,并有一些后者不具备的功能,如锁投票、定时锁等候、可中断锁等候等

   2.synchronized 是Java 语言层面的,是内置的关键字;Lock 则是JDK 5中出现的一个包,在使用时,synchronized 同步的代码块可以由JVM自动释放;Lock 需要程序员在finally块中手工释放,如果不释放,可能会引起难以预料的后果(在多线程环境中)。


   synchronized 快速回顾:

   1.当代码块 加上 synchrozized之后,代码会发生什么改变?

    答案:有两条改变。一个是原子性(atomicity),一个是可见性(visibility)。原子性意味着一次只能有一个线程获得代码锁,进入synchronized 包围的代码块中执行。而可见性则是对不同范围内对变量的修改做出的一致性。强调变量的可见性与一致性,是因为在Java 内存中,内存缓存和编译器优化在多线程条件下会造成各种反常行为。一般来说,线程以某种不必让其他线程立即可以看到的方式(不管这些线程在寄存器(register)中、在处理器特定的缓存(CPU cache)中,还是通过指令重排或者其他编译器优化),不受缓存变量值的约束,但是加人synchronized关键字之后,那么运行库将确保某一线程对变量所做的更新先于对现有 synchronized 块所进行的更新,当进入由同一监控器(lock)保护的另一个 synchronized 块时,将立刻可以看到这些对变量所做的更新。类似的规则也存在于 volatile 变量

  2. 为什么需要java.util.concurrent.lock?

  主要是synchronized是比较古老的实现机制,设计较早,有一些功能上的限制:

—— 它无法中断一个正在等候获得锁的线程

——也无法通过投票得到锁,如果不想等下去,也就没法得到锁。

——同步还要求锁的释放只能在与获得锁所在的堆栈帧相同的堆栈帧中进行

  

3. 重入锁 ReentrantLock

java.util.concurrent.lock 中的 Lock 框架是锁定的一个抽象,它允许把锁定的实现作为 Java 类,而不是作为语言的特性来实现。

这就为 Lock 的多种实现留下了空间,各种实现可能有不同的调度算法、性能特性或者锁定语义。

ReentrantLock 类实现了Lock ,它拥有与 synchronized 相同的并发性和内存语义,但是添加了类似锁投票、定时锁等候和可中断锁等候的一些特性。

此外,它还提供了在激烈争用情况下更佳的性能。(换句话说,当许多线程都想访问共享资源时,JVM 可以花更少的时候来调度线程,把更多时间用在执行线程上。)

reentrant 锁意味着什么呢?简单来说,它有一个与锁相关的获取计数器,如果拥有锁的某个线程再次得到锁,那么获取计数器就加1,然后锁需要被释放两次才能获得真正释放。这模仿了 synchronized 的语义;如果线程进入由线程已经拥有的监控器保护的 synchronized 块,就允许线程继续进行,当线程退出第二个(或者后续) synchronized 块的时候,不释放锁,只有线程退出它进入的监控器保护的第一个 synchronized 块时,才释放锁。

  1. Lock lock = new ReentrantLock();
  2. lock.lock();
  3. try {
  4.   // update object state
  5. }
  6. finally {
  7.   lock.unlock();
  8. }
复制代码
上面的代码可以看出, Lock 和 synchronized 有一点明显的区别 —— lock 必须在 finally 块中释放。否则,如果受保护的代码将抛出异常,锁就有可能永远得不到释放!这一点区别看起来可能没什么,但是实际上,它极为重要。忘记在 finally 块中释放锁,可能会在程序中留下一个定时炸弹,当有一天炸弹爆炸时,您要花费很大力气才有找到源头在哪。而使用同步,JVM 将确保锁会获得自动释放。

  1. package com.mahoutchina.java.concurrent.lock;

  2. import java.util.concurrent.ExecutorService;
  3. import java.util.concurrent.Executors;
  4. import java.util.concurrent.TimeUnit;
  5. import java.util.concurrent.locks.Condition;
  6. import java.util.concurrent.locks.ReentrantLock;

  7. public class LockSample {

  8.         /**
  9.          * @param args
  10.          */
  11.         public static void main(String[] args) {
  12.                 final ExecutorService exec = Executors.newFixedThreadPool(4);
  13.                 final ReentrantLock lock = new ReentrantLock();
  14.                 final Condition con = lock.newCondition();
  15.                 final int time = 5;
  16.                 final Runnable add = new Runnable() {

  17.                         @Override
  18.                         public void run() {
  19.                                 System.out.println("Pre" + lock.toString());
  20.                                 lock.lock();

  21.                                 try {
  22.                                         con.await(time, TimeUnit.SECONDS);
  23.                                 } catch (InterruptedException e) {
  24.                                         e.printStackTrace();
  25.                                 } finally {
  26.                                         System.out.println("Post " + lock.toString());
  27.                                         lock.unlock();
  28.                                 }

  29.                         }
  30.                 };

  31.                 for (int i = 0; i < 4; i++) {
  32.                         exec.submit(add);
  33.                 }
  34.                 exec.shutdown();

  35.         }

  36. }
复制代码


回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|Archiver|手机版|小黑屋|firemail ( 粤ICP备15085507号-1 )

GMT+8, 2024-11-1 07:52 , Processed in 0.058150 second(s), 19 queries .

Powered by Discuz! X3

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表