zoukankan      html  css  js  c++  java
  • synchronized和lock比较

    一、synchronized的实现方案

      1.synchronized能够把任何一个非null对象当成锁,实现由两种方式:

      a.当synchronized作用于非静态方法时,锁住的是当前对象的事例,当synchronized作用于静态方法时,锁住的是class实例,又因为Class的相关数据存储在永久带,因此静态方法锁相当于类的一个全局锁。

      b.当synchronized作用于一个对象实例时,锁住的是对应的代码块。

      2.synchronized锁又称为对象监视器(object)。

          3.当多个线程一起访问某个对象监视器的时候,对象监视器会将这些请求存储在不同的容器中。

      >Contention List:竞争队列,所有请求锁的线程首先被放在这个竞争队列中

      >Entry List:Contention List中那些有资格成为候选资源的线程被移动到Entry List中

      >Wait Set:哪些调用wait方法被阻塞的线程被放置在这里

      >OnDeck:任意时刻,最多只有一个线程正在竞争锁资源,该线程被成为OnDeck

      >Owner:当前已经获取到所资源的线程被称为Owner

      > !Owner:当前释放锁的线程

      下图展示了他们之前的关系

          

    二、lock的实现方案

      与synchronized不同的是lock是纯java手写的,与底层的JVM无关。在java.util.concurrent.locks包中有很多Lock的实现类,常用的有ReenTrantLock、ReadWriteLock(实现类有ReenTrantReadWriteLock)

    ,其实现都依赖java.util.concurrent.AbstractQueuedSynchronizer类(简称AQS),实现思路都大同小异,因此我们以ReentrantLock作为讲解切入点。

    分析之前我们先来花点时间看下AQS。AQS是我们后面将要提到的CountDownLatch/FutureTask/ReentrantLock/RenntrantReadWriteLock/Semaphore的基础,因此AQS也是Lock和Excutor实现的基础。它的基本思想就是一个同步器,支持获取锁和释放锁两个操作。

      

      要支持上面锁获取、释放锁就必须满足下面的条件:

      1、  状态位必须是原子操作的

      2、  阻塞和唤醒线程

      3、  一个有序的队列,用于支持锁的公平性

      场景:可定时的、可轮询的与可中断的锁获取操作,公平队列,或者非块结构的锁。

      使用用法介绍

      

    public class Test {
        private ArrayList<Integer> arrayList = new ArrayList<Integer>();
        private Lock lock = new ReentrantLock();    //注意这个地方
        public static void main(String[] args)  {
            final Test test = new Test();
             
            new Thread(){
                public void run() {
                    test.insert(Thread.currentThread());
                };
            }.start();
             
            new Thread(){
                public void run() {
                    test.insert(Thread.currentThread());
                };
            }.start();
        }  
         
        public void insert(Thread thread) {
            lock.lock();
            try {
                System.out.println(thread.getName()+"得到了锁");
                for(int i=0;i<5;i++) {
                    arrayList.add(i);
                }
            } catch (Exception e) {
                // TODO: handle exception
            }finally {
                System.out.println(thread.getName()+"释放了锁");
                lock.unlock();
            }
        }
    }

    tryLock()的使用方法

    public class Test {
        private ArrayList<Integer> arrayList = new ArrayList<Integer>();
        private Lock lock = new ReentrantLock();    //注意这个地方
        public static void main(String[] args)  {
            final Test test = new Test();
             
            new Thread(){
                public void run() {
                    test.insert(Thread.currentThread());
                };
            }.start();
             
            new Thread(){
                public void run() {
                    test.insert(Thread.currentThread());
                };
            }.start();
        }  
         
        public void insert(Thread thread) {
            if(lock.tryLock()) {
                try {
                    System.out.println(thread.getName()+"得到了锁");
                    for(int i=0;i<5;i++) {
                        arrayList.add(i);
                    }
                } catch (Exception e) {
                    // TODO: handle exception
                }finally {
                    System.out.println(thread.getName()+"释放了锁");
                    lock.unlock();
                }
            } else {
                System.out.println(thread.getName()+"获取锁失败");
            }
        }
    }

    lockInterruptibly()

    public class Test {
        private Lock lock = new ReentrantLock();   
        public static void main(String[] args)  {
            Test test = new Test();
            MyThread thread1 = new MyThread(test);
            MyThread thread2 = new MyThread(test);
            thread1.start();
            thread2.start();
             
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            thread2.interrupt();
        }  
         
        public void insert(Thread thread) throws InterruptedException{
            lock.lockInterruptibly();   //注意,如果需要正确中断等待锁的线程,必须将获取锁放在外面,然后将InterruptedException抛出
            try {  
                System.out.println(thread.getName()+"得到了锁");
                long startTime = System.currentTimeMillis();
                for(    ;     ;) {
                    if(System.currentTimeMillis() - startTime >= Integer.MAX_VALUE)
                        break;
                    //插入数据
                }
            }
            finally {
                System.out.println(Thread.currentThread().getName()+"执行finally");
                lock.unlock();
                System.out.println(thread.getName()+"释放了锁");
            }  
        }
    }
     
    class MyThread extends Thread {
        private Test test = null;
        public MyThread(Test test) {
            this.test = test;
        }
        @Override
        public void run() {
             
            try {
                test.insert(Thread.currentThread());
            } catch (InterruptedException e) {
                System.out.println(Thread.currentThread().getName()+"被中断");
            }
        }
    }
    

      

    ReentrantReadWriteLock

    public class Test {
        private ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
         
        public static void main(String[] args)  {
            final Test test = new Test();
             
            new Thread(){
                public void run() {
                    test.get(Thread.currentThread());
                };
            }.start();
             
            new Thread(){
                public void run() {
                    test.get(Thread.currentThread());
                };
            }.start();
             
        }  
         
        public void get(Thread thread) {
            rwl.readLock().lock();
            try {
                long start = System.currentTimeMillis();
                 
                while(System.currentTimeMillis() - start <= 1) {
                    System.out.println(thread.getName()+"正在进行读操作");
                }
                System.out.println(thread.getName()+"读操作完毕");
            } finally {
                rwl.readLock().unlock();
            }
        }
    }

     另外在ReentrantLock类中定义了很多方法,比如:

      isFair()        //判断锁是否是公平锁

      isLocked()    //判断锁是否被任何线程获取了

      isHeldByCurrentThread()   //判断锁是否被当前线程获取了

      hasQueuedThreads()   //判断是否有线程在等待该锁

      在ReentrantReadWriteLock中也有类似的方法,同样也可以设置为公平锁和非公平锁。不过要记住,ReentrantReadWriteLock并未实现Lock接口,它实现的是ReadWriteLock接口。

      主要从以下几个特点介绍:

      1.可重入锁

        如果锁具备可重入性,则称作为可重入锁。像synchronized和ReentrantLock都是可重入锁,可重入性在我看来实际上表明了锁的分配机制:基于线程的分配,而不是基于方法调用的分配。

      2.可中断锁

        可中断锁:顾名思义,就是可以相应中断的锁。

        在Java中,synchronized就不是可中断锁,而Lock是可中断锁。

        如果某一线程A正在执行锁中的代码,另一线程B正在等待获取该锁,可能由于等待时间过长,线程B不想等待了,想先处理其他事情,我们可以让它中断自己或者在别的线程中中断它,这种就是可中断锁。

      3.公平锁和非公平锁

         公平锁以请求锁的顺序来获取锁,非公平锁则是无法保证按照请求的顺序执行。synchronized就是非公平锁,它无法保证等待的线程获取锁的顺序。而对于ReentrantLock和ReentrantReadWriteLock,它默认情况下是非公平锁,但是可以设置为公平锁。

        参数为true时表示公平锁,不传或者false都是为非公平锁。

    ReentrantLock lock = new ReentrantLock(true);

      4.读写锁

      读写锁将对一个资源(比如文件)的访问分成了2个锁,一个读锁和一个写锁。

      正因为有了读写锁,才使得多个线程之间的读操作不会发生冲突。

      ReadWriteLock就是读写锁,它是一个接口,ReentrantReadWriteLock实现了这个接口。

      可以通过readLock()获取读锁,通过writeLock()获取写锁。

    三、总结

      1.synchronized

      优点:实现简单,语义清晰,便于JVM堆栈跟踪,加锁解锁过程由JVM自动控制,提供了多种优化方案,使用更广泛

      缺点:悲观的排他锁,不能进行高级功能

      2.lock

      优点:可定时的、可轮询的与可中断的锁获取操作,提供了读写锁、公平锁和非公平锁  

      缺点:需手动释放锁unlock,不适合JVM进行堆栈跟踪

      3.相同点 

      都是可重入锁

    ReenTrantLock独有的能力:

    1.      ReenTrantLock可以指定是公平锁还是非公平锁。而synchronized只能是非公平锁。所谓的公平锁就是先等待的线程先获得锁。

    2.      ReenTrantLock提供了一个Condition(条件)类,用来实现分组唤醒需要唤醒的线程们,而不是像synchronized要么随机唤醒一个线程要么唤醒全部线程。

    3.      ReenTrantLock提供了一种能够中断等待锁的线程的机制,通过lock.lockInterruptibly()来实现这个机制。

    什么情况下使用ReenTrantLock:

    答案是,如果你需要实现ReenTrantLock的三个独有功能时。

    参考:

    https://www.cnblogs.com/jiangds/p/6476293.html

    https://www.cnblogs.com/handsomeye/p/5999362.html

    https://www.cnblogs.com/baizhanshi/p/7211802.html

  • 相关阅读:
    24个JavaScript初学者最佳实践
    Algs4-2.1.7对于逆序数组,选择排序和插入排序谁更快?
    Algs4-2.1.4插入排序过程
    Algs4-2.1.2在选择排序中,一个元素最多可能会被交换多少次?
    Algs4-2.1.3构造一个含有N个元素的数组
    *Algs4-1.5.26Erdos-Renyi模型的均摊成本图像-(未读懂题)
    Algs4-2.1.1选择排序的每步动作
    *Algs4-1.5.25随机网格的倍率测试-(未读懂题)
    Algs4-1.5.24适用于Erdos-Renyi模型的快速算
    Algs4-1.5.22Erods-renyi模型的倍率实验
  • 原文地址:https://www.cnblogs.com/draem0507/p/9302332.html
Copyright © 2011-2022 走看看