• 18.并发编程--锁-介绍


    并发编程--锁-介绍

    • LOCK
    • ReentrantLock
    • Condition
    • ReentrantReadWriteLock

    1. LOCK(计时器) 

    介绍

    • 从Java 5之后,在java.util.concurrent.locks包下提供了另外一种方式来实现同步访问,那就是Lock。
    • 既然都可以通过synchronized来实现同步访问了,那么为什么还需要提供Lock?  

    理解:

    • synchronized的缺陷
    • 一如果一个代码块被synchronized修饰了,当一个线程获取了对应的锁,并执行该代码块时,其他线程便只能一直等待,等待获取锁的线程释放锁,而这里获取锁的线程释放锁只会有两种情况
    • 1)获取锁的线程执行完了该代码块,然后线程释放对锁的占有;
    • 2)线程执行发生异常,此时JVM会让线程自动释放锁;
    • 那么如果这个获取锁的线程由于要等待IO或者其他原因(比如调用sleep方法)被阻塞了,但是又没有释放锁,其他线程便只能等待,试想一下,这多么影响程序执行效率。
    • 因此就需要有一种机制可以不让等待的线程一直无期限地等待下去(比如只等待一定的时间或者能够响应中断),通过Lock就可以办到。

    LOCK 的特点

    支持重入锁,嗅探锁定,多路分支等功能,Lock接口中每个方法的使用, 1-4是用来获取锁的,5方法是用来释放锁的。

    1)lock() //lock()方法是平常使用得最多的一个方法,就是用来获取锁。如果锁已被其他线程获取,则进行等待.

    2)tryLock() //tryLock()方法是有返回值的,它表示用来尝试获取锁,如果获取成功,则返回true,如果获取失败(即锁已被其他线程获取),则返回false.

    3)tryLock(long time, TimeUnit unit) //和tryLock()方法是类似的,只不过区别在于这个方法在拿不到锁时会等待一定的时间, 在时间期限之内如果还拿不到锁,就返回false。如果如果一开始拿到锁或者在等待期间内拿到了锁,则返回true

    4)lockInterruptibly() 比较特殊,当通过这个方法去获取锁时,如果线程正在等待获取锁,则这个线程能够响应中断,即中断线程的等待状态。当两个线程同时通过lock.lockInterruptibly()想获取某个锁时,假若此时线程A获取到了锁,而线程B只有在等待,那么对线程B调用threadB.interrupt()方法能够中断线程B的等待过程。

    5)unLock() 。

    6)newCondition() LOCK 中的类似Object等待wait/通知notify的方法

    7) isLocked() 是否锁定 锁定嗅探。

    8) isFail() 是否是公平锁。

    9) getQueueLength() 返回正在等待获得此锁定的线程数。

    10) getWaitQueueLength() 返回等待与锁定相关的给定条件Condition的线程数.

    11) hasQueueThread(Thread thread) 查询指定的线程是否等待此锁。

    12) hasQueueThreads() 查询是否存在线程正在等待此锁。

    13) hasWaiters() 查询是否存在啊线程正在等待与此锁定有关的condition条件。

    2. ReentrantLock

    ReentrantLock,意思是“可重入锁”。ReentrantLock是唯一实现了Lock接口的类,并且ReentrantLock提供了更多的方法.
    > 注意 重入锁,在需要进行同步的代码部分加上锁,最后一定要释放锁,不然你就蛋疼了。

     1 public class MyReentrantLock {
     2   private Lock lock = new ReentrantLock();
     3 
     4   public void method1(){
     5     try {
     6       lock.lock();
     7       System.out.println("当前线程:" + Thread.currentThread().getName() + "进入method1..");
     8       Thread.sleep(1000);
     9       System.out.println("当前线程:" + Thread.currentThread().getName() + "退出method1..");
    10       Thread.sleep(1000);
    11     } catch (InterruptedException e) {
    12       e.printStackTrace();
    13     } finally {
    14 
    15       lock.unlock();//释放锁
    16     }
    17   }
    18 
    19   public void method2(){
    20     try {
    21       lock.lock();
    22       System.out.println("当前线程:" + Thread.currentThread().getName() + "进入method2..");
    23       Thread.sleep(2000);
    24       System.out.println("当前线程:" + Thread.currentThread().getName() + "退出method2..");
    25       Thread.sleep(1000);
    26     } catch (InterruptedException e) {
    27       e.printStackTrace();
    28     } finally {
    29 
    30       lock.unlock();//释放锁
    31     }
    32   }
    33 
    34   public static void main(String[] args) {
    35 
    36     final MyReentrantLock ur = new MyReentrantLock();
    37     Thread t1 = new Thread(new Runnable() {
    38       @Override
    39       public void run() {
    40         ur.method1();
    41         ur.method2();
    42       }
    43     }, "t1");
    44 
    45     t1.start();
    46     try {
    47       Thread.sleep(10);
    48     } catch (InterruptedException e) {
    49       e.printStackTrace();
    50     }
    51   }
    52 }

    执行结果:

    当前线程:t1进入method1..
    当前线程:t1退出method1..
    当前线程:t1进入method2..
    当前线程:t1退出method2..
    

    lock.newCondition()

    Condition 的作用:newCondition() LOCK 中的类似Object等待wait/通知notify的方法。

    示例:

     1 public class MyCondition {
     2   private Lock lock = new ReentrantLock();
     3   private Condition condition = lock.newCondition();
     4 
     5   public void method1(){
     6     try {
     7       lock.lock();
     8       System.out.println("当前线程:" + Thread.currentThread().getName() + "进入等待状态..");
     9       Thread.sleep(3000);
    10       System.out.println("当前线程:" + Thread.currentThread().getName() + "释放锁..");
    11       condition.await();    // Object wait
    12       System.out.println("当前线程:" + Thread.currentThread().getName() +"继续执行...");
    13     } catch (Exception e) {
    14       e.printStackTrace();
    15     } finally {
    16       lock.unlock();
    17     }
    18   }
    19 
    20   public void method2(){
    21     try {
    22       lock.lock();
    23       System.out.println("当前线程:" + Thread.currentThread().getName() + "进入..");
    24       Thread.sleep(3000);
    25       System.out.println("当前线程:" + Thread.currentThread().getName() + "发出唤醒..");
    26       condition.signal();        //Object notify
    27     } catch (Exception e) {
    28       e.printStackTrace();
    29     } finally {
    30       lock.unlock();
    31     }
    32   }
    33 
    34   public static void main(String[] args) {
    35     final MyCondition uc = new MyCondition();
    36     Thread t1 = new Thread(new Runnable() {
    37       @Override
    38       public void run() {
    39         uc.method1();
    40       }
    41     }, "t1");
    42     Thread t2 = new Thread(new Runnable() {
    43       @Override
    44       public void run() {
    45         uc.method2();
    46       }
    47     }, "t2");
    48 
    49     t1.start();
    50     t2.start();
    51   }
    52 }  

    输出结果:

    1 当前线程:t1进入..
    2 当前线程:t1释放锁..
    3 当前线程:t2进入..
    4 当前线程:t2发出唤醒..
    5 当前线程:t1继续执行...

    多个Condition

    可以通过一个LOCK锁产生多个Condition进行线程间的交互。非常灵活,是的部分需要换新的线程唤醒,其他线程则继续等待通知。

    示例:MyManyCondition.java

      1 public class MyManyCondition {
      2 
      3  private ReentrantLock lock = new ReentrantLock();
      4  private Condition c1 = lock.newCondition();
      5  private Condition c2 = lock.newCondition();
      6 
      7  public void m1(){
      8    try {
      9      lock.lock();
     10      System.out.println("当前线程:" +Thread.currentThread().getName() + "进入方法m1等待..");
     11      c1.await();
     12      System.out.println("当前线程:" +Thread.currentThread().getName() + "方法m1继续..");
     13    } catch (Exception e) {
     14      e.printStackTrace();
     15    } finally {
     16      lock.unlock();
     17    }
     18  }
     19 
     20  public void m2(){
     21    try {
     22      lock.lock();
     23      System.out.println("当前线程:" +Thread.currentThread().getName() + "进入方法m2等待..");
     24      c1.await();
     25      System.out.println("当前线程:" +Thread.currentThread().getName() + "方法m2继续..");
     26    } catch (Exception e) {
     27      e.printStackTrace();
     28    } finally {
     29      lock.unlock();
     30    }
     31  }
     32 
     33  public void m3(){
     34    try {
     35      lock.lock();
     36      System.out.println("当前线程:" +Thread.currentThread().getName() + "进入方法m3等待..");
     37      c2.await();
     38      System.out.println("当前线程:" +Thread.currentThread().getName() + "方法m3继续..");
     39    } catch (Exception e) {
     40      e.printStackTrace();
     41    } finally {
     42      lock.unlock();
     43    }
     44  }
     45 
     46  public void m4(){
     47    try {
     48      lock.lock();
     49      System.out.println("当前线程:" +Thread.currentThread().getName() + "唤醒..");
     50      c1.signalAll();
     51    } catch (Exception e) {
     52      e.printStackTrace();
     53    } finally {
     54      lock.unlock();
     55    }
     56  }
     57 
     58  public void m5(){
     59    try {
     60      lock.lock();
     61      System.out.println("当前线程:" +Thread.currentThread().getName() + "唤醒..");
     62      c2.signal();
     63    } catch (Exception e) {
     64      e.printStackTrace();
     65    } finally {
     66      lock.unlock();
     67    }
     68  }
     69 
     70  public static void main(String[] args) {
     71 
     72 
     73    final MyManyCondition umc = new MyManyCondition();
     74    Thread t1 = new Thread(new Runnable() {
     75      @Override
     76      public void run() {
     77        umc.m1();
     78      }
     79    },"t1");
     80    Thread t2 = new Thread(new Runnable() {
     81      @Override
     82      public void run() {
     83        umc.m2();
     84      }
     85    },"t2");
     86    Thread t3 = new Thread(new Runnable() {
     87      @Override
     88      public void run() {
     89        umc.m3();
     90      }
     91    },"t3");
     92    Thread t4 = new Thread(new Runnable() {
     93      @Override
     94      public void run() {
     95        umc.m4();
     96      }
     97    },"t4");
     98    Thread t5 = new Thread(new Runnable() {
     99      @Override
    100      public void run() {
    101        umc.m5();
    102      }
    103    },"t5");
    104 
    105    t1.start();    // c1
    106    t2.start();    // c1
    107    t3.start();    // c2
    108 
    109 
    110    try {
    111      Thread.sleep(2000);
    112    } catch (InterruptedException e) {
    113      e.printStackTrace();
    114    }
    115 
    116    t4.start();    // c1
    117    try {
    118      Thread.sleep(2000);
    119    } catch (InterruptedException e) {
    120      e.printStackTrace();
    121    }
    122    t5.start();    // c2
    123 
    124  }
    125 }  

    执行结果:

    当前线程:t1进入方法m1等待..
    当前线程:t3进入方法m3等待..
    当前线程:t2进入方法m2等待..
    当前线程:t4唤醒..
    当前线程:t1方法m1继续..
    当前线程:t2方法m2继续..
    当前线程:t5唤醒..
    当前线程:t3方法m3继续..
    

    3. ReentrantReadWriteLock 

    ReentrantReadWriteLock是Lock的另一种实现方式.
    我们已经知道了ReentrantLock是一个排他锁,同一时间只允许一个线程访问,而ReentrantReadWriteLock允许多个读线程同时访问,但不允许写线程和读线程、写线程和写线程同时访问。相对于排他锁,提高了并发性。
    在实际应用中,大部分情况下对共享数据(如缓存)的访问都是读操作远多于写操作,这时ReentrantReadWriteLock能够提供比排他锁更好的并发性和吞吐量。

    ReentrantReadWriteLock支持以下功能:

    支持公平和非公平的获取锁的方式;
    支持可重入。读线程在获取了读锁后还可以获取读锁;写线程在获取了写锁之后既可以再次获取写锁又可以获取读锁;
    还允许从写入锁降级为读取锁,其实现方式是:先获取写入锁,然后获取读取锁,最后释放写入锁。但是,从读取锁升级到写入锁是不允许的;
    读取锁和写入锁都支持锁获取期间的中断;
    Condition支持。仅写入锁提供了一个 Conditon 实现;读取锁不支持 Conditon ,readLock().newCondition() 会抛出 UnsupportedOperationException;

    示例:MyReentrantReadWriteLock.java

     1 public class MyReentrantReadWriteLock {
     2 
     3   private ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
     4   private ReadLock readLock = rwLock.readLock();
     5   private WriteLock writeLock = rwLock.writeLock();
     6 
     7   public void read(){
     8     try {
     9       readLock.lock();
    10       System.out.println("当前线程:" + Thread.currentThread().getName() + "进入...");
    11       Thread.sleep(3000);
    12       System.out.println("当前线程:" + Thread.currentThread().getName() + "退出...");
    13     } catch (Exception e) {
    14       e.printStackTrace();
    15     } finally {
    16       readLock.unlock();
    17     }
    18   }
    19 
    20   public void write(){
    21     try {
    22       writeLock.lock();
    23       System.out.println("当前线程:" + Thread.currentThread().getName() + "进入...");
    24       Thread.sleep(3000);
    25       System.out.println("当前线程:" + Thread.currentThread().getName() + "退出...");
    26     } catch (Exception e) {
    27       e.printStackTrace();
    28     } finally {
    29       writeLock.unlock();
    30     }
    31   }
    32 
    33   public static void main(String[] args) {
    34 
    35     final MyReentrantReadWriteLock urrw = new MyReentrantReadWriteLock();
    36 
    37     Thread t1 = new Thread(new Runnable() {
    38       @Override
    39       public void run() {
    40         urrw.read();
    41       }
    42     }, "t1");
    43     Thread t2 = new Thread(new Runnable() {
    44       @Override
    45       public void run() {
    46         urrw.read();
    47       }
    48     }, "t2");
    49     Thread t3 = new Thread(new Runnable() {
    50       @Override
    51       public void run() {
    52         urrw.write();
    53       }
    54     }, "t3");
    55     Thread t4 = new Thread(new Runnable() {
    56       @Override
    57       public void run() {
    58         urrw.write();
    59       }
    60     }, "t4");        
    61     //注解1
    62   //        t1.start(); // R
    63   //        t2.start(); // R
    64     //注解2
    65   //        t1.start(); // R
    66   //        t3.start(); // W
    67     //注解3
    68     t3.start(); // W
    69     t4.start(); // W
    70   }
    71 }

    运行结构(打开注解1):

    当前线程:t2进入...
    当前线程:t1进入...
    当前线程:t2退出...
    当前线程:t1退出...
    

    运行结构(打开注解2):

    当前线程:t1进入...
    当前线程:t1退出...
    当前线程:t3进入...
    当前线程:t3退出...
    

    运行结果(打开注解3):

    当前线程:t3进入...
    当前线程:t3退出...
    当前线程:t4进入...
    当前线程:t4退出...
    

      总结:读读共享,读写互斥,写写互斥

  • 相关阅读:
    java 获取某个URL的文件扩展名的方法(非精确,精确的扩展名应该使用服务器返回的MIME-TYPE)
    导出Redis某个列表所有数据的语句
    CDH的几个包的下载地址
    使用visualVM 1.3.8(visualvm_138-ml.zip) 监控远程Tomcat运行情况
    ubuntu 16.04下安装oracle jdk 1.7
    迅雷影音下载的在线字幕在哪里?
    【Android开发】之Android环境搭建及HelloWorld
    Android开发环境搭建篇详尽的教程实例汇
    【Android开发】Api_Android_Distribute
    Eclipse_Configure
  • 原文地址:https://www.cnblogs.com/Mao-admin/p/9989510.html
Copyright © 2020-2023  润新知