Synchronize和ReentrantLock区别

目录介绍

  • 1.Synchronize和ReentrantLock区别php

    • 1.1 类似点
    • 1.2 区别
    • 1.3 什么是线程安全问题?如何理解
    • 1.4 线程安全须要保证几个基本特性
  • 2.Synchronize在编译时如何实现锁机制
  • 3.ReentrantLock使用方法
  • 4.ReentrantLock锁机制测试案例分析java

    • 4.1 代码案例分析
    • 4.2 何时选择用ReentrantLock
    • 4.3 公平锁和非公平锁有何区别
  • 5.问答测试题git

    • 5.1 ReentrantLock和synchronized使用分析

好消息

  • 博客笔记大汇总【16年3月到至今】,包括Java基础及深刻知识点,Android技术博客,Python学习笔记等等,还包括平时开发中遇到的bug汇总,固然也在工做之余收集了大量的面试题,长期更新维护而且修正,持续完善……开源的文件是markdown格式的!同时也开源了生活博客,从12年起,积累共计47篇[近20万字],转载请注明出处,谢谢!
  • 连接地址:https://github.com/yangchong2...
  • 若是以为好,能够star一下,谢谢!固然也欢迎提出建议,万事起于忽微,量变引发质变!

关于锁机制文章

  • 01.Synchronize深刻解析github

    • Synchronize深刻解析,sychonized method 和 synchonized代码块的效率问题
  • 02.Synchronize和ReentrantLock区别面试

    • Synchronize和ReentrantLock区别,Synchronize在编译时如何实现锁机制,ReentrantLock锁机制测试案例分析,公平锁和非公平锁有何区别等等
  • 03.死锁的发生,定位与修复segmentfault

    • 死锁的概念和产生死锁的根本缘由是什么?死锁的预防策略中资源有序分配策略是什么。死锁发生的场景,死锁的危害,出现死锁须要知足条件分析,如何预防死锁,如何定位死锁,以及死锁修复方案分析等等

1.Synchronize和ReentrantLock区别

1.1 类似点:

  • 这两种同步方式有不少类似之处,它们都是加锁方式同步,并且都是阻塞式的同步,也就是说当若是一个线程得到了对象锁,进入了同步块,其余访问该同步块的线程都必须阻塞在同步块外面等待,而进行线程阻塞和唤醒的代价是比较高的(操做系统须要在用户态与内核态之间来回切换,代价很高,不过能够经过对锁优化进行改善)。

1.2 区别:

1.2.1 API层面
  • 这两种方式最大区别就是对于Synchronized来讲,它是java语言的关键字,是原生语法层面的互斥,须要jvm实现。而ReentrantLock它是JDK 1.5以后提供的API层面的互斥锁,须要lock()和unlock()方法配合try/finally语句块来完成。
  • synchronized既能够修饰方法,也能够修饰代码块。安全

    //synchronized修饰一个方法时,这个方法叫同步方法。
    public synchronized void test() {
    //方法体``
    
    }
    
    synchronized(Object) {
    //括号中表示须要锁的对象.
    //线程执行的时候会对Object上锁
    }
  • ReentrantLock使用markdown

    private ReentrantLock lock = new ReentrantLock();
    public void run() {
        lock.lock();
        try{
            for(int i=0;i<5;i++){
                System.out.println(Thread.currentThread().getName()+":"+i);
            }
        }finally{
            lock.unlock();
        }
    }
1.2.2 等待可中断
  • 等待可中断是指当持有锁的线程长期不释放锁的时候,正在等待的线程能够选择放弃等待,改成处理其余事情。可等待特性对处理执行时间很是长的同步快颇有帮助。
  • 具体来讲,假如业务代码中有两个线程,Thread1 Thread2。假设 Thread1 获取了对象object的锁,Thread2将等待Thread1释放object的锁。多线程

    • 使用synchronized。若是Thread1不释放,Thread2将一直等待,不能被中断。synchronized也能够说是Java提供的原子性内置锁机制。内部锁扮演了互斥锁(mutual exclusion lock ,mutex)的角色,一个线程引用锁的时候,别的线程阻塞等待。
    • 使用ReentrantLock。若是Thread1不释放,Thread2等待了很长时间之后,能够中断等待,转而去作别的事情。
1.2.3 公平锁
  • 公平锁是指多个线程在等待同一个锁时,必须按照申请的时间顺序来依次得到锁;而非公平锁则不能保证这一点。非公平锁在锁被释放时,任何一个等待锁的线程都有机会得到锁。
  • synchronized的锁是非公平锁,ReentrantLock默认状况下也是非公平锁,但能够经过带布尔值的构造函数要求使用公平锁。jvm

    • ReentrantLock 构造器的一个参数是boolean值,它容许您选择想要一个公平(fair)锁,仍是一个不公平(unfair)锁。公平锁:使线程按照请求锁的顺序依次得到锁, 可是有成本;不公平锁:则容许讨价还价
    • 那么如何用代码设置公平锁呢?以下所示
    • image
1.2.4 锁绑定多个条件
  • ReentrantLock能够同时绑定多个Condition对象,只需屡次调用newCondition方法便可。
  • synchronized中,锁对象的wait()和notify()或notifyAll()方法能够实现一个隐含的条件。但若是要和多于一个的条件关联的时候,就不得不额外添加一个锁。

1.3 什么是线程安全问题?如何理解

  • 若是你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。若是每次运行结果和单线程运行的结果是同样的,并且其余的变量的值也和预期的是同样的,就是线程安全的,或者说:一个类或者程序所提供的接口对于线程来讲是原子操做或者多个线程之间的切换不会致使该接口的执行结果存在二义性,也就是说咱们不用考虑同步的问题 。

1.4 线程安全须要保证几个基本特性

  • 一、原子性,简单说就是相关操做不会中途被其余线程干扰,通常经过同步机制实现。
  • 二、可见性,是一个线程修改了某个共享变量,其状态可以当即被其余线程知晓,一般被解释为将线程本地状态反映到主内存上,volatile 就是负责保证可见性的。
  • 三、有序性,是保证线程内串行语义,避免指令重排等。

2.Synchronize在编译时如何实现锁机制

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

3.ReentrantLock使用方法

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

    • 1.等待可中断,持有锁的线程长期不释放的时候,正在等待的线程能够选择放弃等待,这至关于Synchronized来讲能够避免出现死锁的状况。
    • 2.公平锁,多个线程等待同一个锁时,必须按照申请锁的时间顺序得到锁,Synchronized锁非公平锁,ReentrantLock默认的构造函数是建立的非公平锁,能够经过参数true设为公平锁,但公平锁表现的性能不是很好。
    • 3.锁绑定多个条件,一个ReentrantLock对象能够同时绑定对个对象。
  • 使用方法代码以下

    private ReentrantLock lock = new ReentrantLock();
    public void run() {
        lock.lock();
        try{
            for(int i=0;i<5;i++){
                System.out.println(Thread.currentThread().getName()+":"+i);
            }
        }finally{
            lock.unlock();
        }
    }
  • 注意问题:为保证锁释放,每个 lock() 动做,建议都当即对应一都当即对应一个 try-catch-finally

4.ReentrantLock锁机制测试案例分析

4.1 代码案例分析

  • 代码以下所示

    private void test2() {
        Runnable t1 = new MyThread();
        new Thread(t1,"t1").start();
        new Thread(t1,"t2").start();
    }
    
    class MyThread implements Runnable {
        private ReentrantLock lock = new ReentrantLock();
        public void run() {
            lock.lock();
            try{
                for(int i=0;i<5;i++){
                    System.out.println(Thread.currentThread().getName()+":"+i);
                }
            }finally{
                lock.unlock();
            }
        }
    }
    
    //打印值以下所示
    10-17 17:06:59.222 6531-6846/com.yc.cn.ycbaseadapter I/System.out: t1:0
    10-17 17:06:59.222 6531-6846/com.yc.cn.ycbaseadapter I/System.out: t1:1
    10-17 17:06:59.222 6531-6846/com.yc.cn.ycbaseadapter I/System.out: t1:2
    10-17 17:06:59.222 6531-6846/com.yc.cn.ycbaseadapter I/System.out: t1:3
    10-17 17:06:59.222 6531-6846/com.yc.cn.ycbaseadapter I/System.out: t1:4
    10-17 17:06:59.224 6531-6847/com.yc.cn.ycbaseadapter I/System.out: t2:0
    10-17 17:06:59.225 6531-6847/com.yc.cn.ycbaseadapter I/System.out: t2:1
    10-17 17:06:59.225 6531-6847/com.yc.cn.ycbaseadapter I/System.out: t2:2
    10-17 17:06:59.225 6531-6847/com.yc.cn.ycbaseadapter I/System.out: t2:3
    10-17 17:06:59.225 6531-6847/com.yc.cn.ycbaseadapter I/System.out: t2:4

4.2 何时选择用ReentrantLock

  • 适用场景:时间锁等候、可中断锁等候、无块结构锁、多个条件变量或者锁投票
  • 在确实须要一些 synchronized所没有的特性的时候,好比时间锁等候、可中断锁等候、无块结构锁、多个条件变量或者锁投票。 ReentrantLock 还具备可伸缩性的好处,应当在高度争用的状况下使用它,可是请记住,大多数 synchronized 块几乎历来没有出现过争用,因此能够把高度争用放在一边。我建议用 synchronized 开发,直到确实证实 synchronized 不合适,而不要仅仅是假设若是使用 ReentrantLock “性能会更好”。请记住,这些是供高级用户使用的高级工具。(并且,真正的高级用户喜欢选择可以找到的最简单工具,直到他们认为简单的工具不适用为止。)。一如既往,首先要把事情作好,而后再考虑是否是有必要作得更快。
  • 使用场景代码展现【摘自ThreadPoolExecutor类,这个类中不少地方用到了这个锁。本身能够查看】:

    /**
     * Rolls back the worker thread creation.
     * - removes worker from workers, if present
     * - decrements worker count
     * - rechecks for termination, in case the existence of this
*/
private void addWorkerFailed(Worker w) {
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        if (w != null)
            workers.remove(w);
        decrementWorkerCount();
        tryTerminate();
    } finally {
        mainLock.unlock();
    }
}
```

4.3 公平锁和非公平锁有何区别

  • 公平性是指在竞争场景中,当公平性为真时,会倾向于将锁赋予等待时间最久的线程。公平性是减小线程“饥饿”(个别线程长期等待锁,但始终没法获取)状况发生的一个办法。

    • 一、公平锁能保证:老的线程排队使用锁,新线程仍然排队使用锁。
    • 二、非公平锁保证:老的线程排队使用锁;可是没法保证新线程抢占已经在排队的线程的锁。
    • 看下面代码案例所示:能够得出结论,公平锁指的是哪一个线程先运行,那就能够先获得锁。非公平锁是无论线程是不是先运行,新的线程都有可能抢占已经在排队的线程的锁。
    private void test3() {
        Service service = new Service();
        ThreadClass tcArray[] = new ThreadClass[10];
        for(int i=0;i<10;i++){
            tcArray[i] = new ThreadClass(service);
            tcArray[i].start();
        }
    }
    
    public class Service {
        ReentrantLock lock = new ReentrantLock(true);
        Service() {
        }
    
        void getThreadName() {
            System.out.println(Thread.currentThread().getName() + " 已经被锁定");
        }
    }
    public class ThreadClass extends Thread{
        private Service service;
        ThreadClass(Service service) {
            this.service = service;
        }
        public void run(){
            System.out.println(Thread.currentThread().getName() + " 抢到了锁");
            service.lock.lock();
            service.getThreadName();
            service.lock.unlock();
        }
    }
    //当ReentrantLock设置true,也就是公平锁时
    10-17 19:32:22.422 6459-6523/com.yc.cn.ycbaseadapter I/System.out: Thread-5 抢到了锁
    10-17 19:32:22.422 6459-6523/com.yc.cn.ycbaseadapter I/System.out: Thread-5 已经被锁定
    10-17 19:32:22.424 6459-6524/com.yc.cn.ycbaseadapter I/System.out: Thread-6 抢到了锁
    10-17 19:32:22.424 6459-6524/com.yc.cn.ycbaseadapter I/System.out: Thread-6 已经被锁定
    10-17 19:32:22.427 6459-6525/com.yc.cn.ycbaseadapter I/System.out: Thread-7 抢到了锁
    10-17 19:32:22.427 6459-6526/com.yc.cn.ycbaseadapter I/System.out: Thread-8 抢到了锁
    10-17 19:32:22.427 6459-6525/com.yc.cn.ycbaseadapter I/System.out: Thread-7 已经被锁定
    10-17 19:32:22.427 6459-6526/com.yc.cn.ycbaseadapter I/System.out: Thread-8 已经被锁定
    10-17 19:32:22.427 6459-6527/com.yc.cn.ycbaseadapter I/System.out: Thread-9 抢到了锁
    10-17 19:32:22.427 6459-6527/com.yc.cn.ycbaseadapter I/System.out: Thread-9 已经被锁定
    10-17 19:32:22.428 6459-6528/com.yc.cn.ycbaseadapter I/System.out: Thread-10 抢到了锁
    10-17 19:32:22.428 6459-6528/com.yc.cn.ycbaseadapter I/System.out: Thread-10 已经被锁定
    10-17 19:32:22.429 6459-6529/com.yc.cn.ycbaseadapter I/System.out: Thread-11 抢到了锁
    10-17 19:32:22.429 6459-6529/com.yc.cn.ycbaseadapter I/System.out: Thread-11 已经被锁定
    10-17 19:32:22.430 6459-6530/com.yc.cn.ycbaseadapter I/System.out: Thread-12 抢到了锁
    10-17 19:32:22.430 6459-6530/com.yc.cn.ycbaseadapter I/System.out: Thread-12 已经被锁定
    10-17 19:32:22.431 6459-6532/com.yc.cn.ycbaseadapter I/System.out: Thread-14 抢到了锁
    10-17 19:32:22.431 6459-6532/com.yc.cn.ycbaseadapter I/System.out: Thread-14 已经被锁定
    10-17 19:32:22.432 6459-6531/com.yc.cn.ycbaseadapter I/System.out: Thread-13 抢到了锁
    10-17 19:32:22.433 6459-6531/com.yc.cn.ycbaseadapter I/System.out: Thread-13 已经被锁定
    
    
    //当ReentrantLock设置false,也就是非公平锁时
    10-17 19:34:58.102 7089-7183/com.yc.cn.ycbaseadapter I/System.out: Thread-5 抢到了锁
    10-17 19:34:58.102 7089-7184/com.yc.cn.ycbaseadapter I/System.out: Thread-6 抢到了锁
    10-17 19:34:58.103 7089-7183/com.yc.cn.ycbaseadapter I/System.out: Thread-5 已经被锁定
    10-17 19:34:58.103 7089-7185/com.yc.cn.ycbaseadapter I/System.out: Thread-7 抢到了锁
    10-17 19:34:58.103 7089-7185/com.yc.cn.ycbaseadapter I/System.out: Thread-7 已经被锁定
    10-17 19:34:58.103 7089-7184/com.yc.cn.ycbaseadapter I/System.out: Thread-6 已经被锁定
    10-17 19:34:58.104 7089-7186/com.yc.cn.ycbaseadapter I/System.out: Thread-8 抢到了锁
    10-17 19:34:58.105 7089-7186/com.yc.cn.ycbaseadapter I/System.out: Thread-8 已经被锁定
    10-17 19:34:58.108 7089-7187/com.yc.cn.ycbaseadapter I/System.out: Thread-9 抢到了锁
    10-17 19:34:58.108 7089-7187/com.yc.cn.ycbaseadapter I/System.out: Thread-9 已经被锁定
    10-17 19:34:58.111 7089-7188/com.yc.cn.ycbaseadapter I/System.out: Thread-10 抢到了锁
    10-17 19:34:58.112 7089-7188/com.yc.cn.ycbaseadapter I/System.out: Thread-10 已经被锁定
    10-17 19:34:58.112 7089-7189/com.yc.cn.ycbaseadapter I/System.out: Thread-11 抢到了锁
    10-17 19:34:58.113 7089-7189/com.yc.cn.ycbaseadapter I/System.out: Thread-11 已经被锁定
    10-17 19:34:58.113 7089-7193/com.yc.cn.ycbaseadapter I/System.out: Thread-14 抢到了锁
    10-17 19:34:58.113 7089-7193/com.yc.cn.ycbaseadapter I/System.out: Thread-14 已经被锁定
    10-17 19:34:58.115 7089-7190/com.yc.cn.ycbaseadapter I/System.out: Thread-12 抢到了锁
    10-17 19:34:58.115 7089-7190/com.yc.cn.ycbaseadapter I/System.out: Thread-12 已经被锁定
    10-17 19:34:58.116 7089-7191/com.yc.cn.ycbaseadapter I/System.out: Thread-13 抢到了锁
    10-17 19:34:58.116 7089-7191/com.yc.cn.ycbaseadapter I/System.out: Thread-13 已经被锁定

5.问答测试题

5.1 ReentrantLock和synchronized使用分析

  • ReentrantLock是Lock的实现类,是一个互斥的同步器,在多线程高竞争条件下,ReentrantLock比synchronized有更加优异的性能表现。
  • 1 用法比较

    • Lock使用起来比较灵活,可是必须有释放锁的配合动做
    • Lock必须手动获取与释放锁,而synchronized不须要手动释放和开启锁
    • Lock只适用于代码块锁,而synchronized可用于修饰方法、代码块等
  • 2 特性比较

    • ReentrantLock的优点体如今:

      • 具有尝试非阻塞地获取锁的特性:当前线程尝试获取锁,若是这一时刻锁没有被其余线程获取到,则成功获取并持有锁
      • 能被中断地获取锁的特性:与synchronized不一样,获取到锁的线程可以响应中断,当获取到锁的线程被中断时,中断异常将会被抛出,同时锁会被释放
      • 超时获取锁的特性:在指定的时间范围内获取锁;若是截止时间到了仍然没法获取锁,则返回
  • 3 注意事项

    • 在使用ReentrantLock类的时,必定要注意三点:

      • 在finally中释放锁,目的是保证在获取锁以后,最终可以被释放
      • 不要将获取锁的过程写在try块内,由于若是在获取锁时发生了异常,异常抛出的同时,也会致使锁无端被释放。
      • ReentrantLock提供了一个newCondition的方法,以便用户在同一锁的状况下能够根据不一样的状况执行等待或唤醒的动做。

关于其余内容介绍

01.关于博客汇总连接

02.关于个人博客

相关文章
相关标签/搜索