zoukankan      html  css  js  c++  java
  • Java并发基础--Lock的学习

    一、Lock的出现

    Lock的主要作用实现线程之间的同步互斥,与synchronized关键字的效果是一样的,synchronized是Java语言内置的特性,那么为什么又出现了Lock呢?原因是synchronized不是完美的,那么synchronized的缺陷在哪里呢?

    ①、通过synchronized实现同步,如果多个线程都只是进行读操作,所以当一个线程在进行读操作时,其他线程只能等待无法进行读操作。

    ②、通过synchronized实现同步的时候,无法知道线程是否获取到了锁。

    为了弥补synchronized的缺陷,Java 5中引入了新的锁机制——java.util.concurrent.locks中的显式的互斥锁。Lock接口,提供了比synchronized更加广泛的锁操作。它的主要实现类:ReentrantLock、ReetrantReadWriteLock.ReadLock和ReetrantRead.WriteLock.即重入锁、读锁和写锁。Lock必须被显式的创建和释放,为了保证最终锁一定被释放,经常将虎互斥区置放在try语句中,并在finally中释放锁,特别是当有return语句的时候,return语句必须放在try语句中,以确保unlock不会过早的发生,从而将数据暴露给下面的任务。使用比较多的是ReentrantLock。示例如下:

     1 public class LockDemo {
     2     private Lock lock = new ReentrantLock();
     3     
     4     public void testMethod(){
     5         try {
     6             lock.lock();
     7             for(int i=0;i<3;i++){
     8                 System.out.println(Thread.currentThread().getName()+" print....");
     9             }
    10         } catch (Exception e) {
    11             e.printStackTrace();
    12         }finally{
    13             lock.unlock();
    14         }
    15     }
    16     
    17     public static void main(String[] args) {
    18         LockDemo lockDemo = new LockDemo();
    19         
    20         new Thread(new MyThread1(lockDemo)).start();
    21         new Thread(new MyThread1(lockDemo)).start();
    22         
    23     }
    24 
    25 }
    26 class MyThread1 implements Runnable{
    27     private LockDemo lockDemo;
    28     public MyThread1(LockDemo lockDemo){
    29         this.lockDemo = lockDemo;
    30     }
    31     @Override
    32     public void run() {
    33         lockDemo.testMethod();
    34     }
    35     
    36 }

    结果输出:

    Thread-0 print....
    Thread-0 print....
    Thread-0 print....
    Thread-1 print....
    Thread-1 print....
    Thread-1 print....

    总结:

    • Lock是一个接口,它有不同的实现类,通过它的实现类可以实现多线程间的同步互斥。
    • Lock与synchronized最大的不同在于,synchronized不需要用户手动释放锁,在线程完成了同步块或者同步方法后自动释放锁(或者出现异常也自动释放锁)。而Lock锁需要用户手动释放,如果忘记释锁,可能造成死锁等后果。

    二、Lock的详细学习

    通过源码可以知道,Lock是一个接口,lock()、tryLock()、tryLock(long time, TimeUnit unit)和lockInterruptibly()是用来获取锁的。unLock()方法是用来释放锁的。newCondition()主要是用来创建Condition对象的,线程对象可以注册在指定的Condition上,Condition起到一个对象监视器的作用,通过Condition实例从而进行线程通知,在调用线程上更加灵活。

    1.lock()方法

    lock方法是使用最多的一个方法,即用来获取锁的,如果锁已经被其他线程占用,则进行等待。经常需要结合try语句一起使用。目的是为了将锁的释放工作放在finally中进行,保证锁的最终释放。如下:

    Lock lock = new ReentrantLock();
    
    lock.lock();
    try{
        //处理任务
    }catch(Exception ex){
         
    }finally{
        lock.unlock();   //释放锁
    }

    2.tryLock()方法

    tryLock()是有返回值的,它表示用来尝试获取锁,如果获取成功,则返回true,如果获取失败(即锁已被其他线程获取),则返回false,也就说这个方法无论如何都会立即返回。在拿不到锁时不会一直在那等待。

    3.tryLock(long time, TimeUnit unit)方法

    tryLock(long time, TimeUnit unit)方法类似tryLock(),区别在于这个方法在拿不锁的时候,不会立即返回,而是等待指定的时间,如果超过指定时间还未获取到锁,则立即返回false,获取锁失败。

    Lock lock = new ReentrantLock();
    
    if(lock.tryLock(3, TimeUnit.SECONDS)) {
         try{
             //处理任务
         }catch(Exception ex){
             
         }finally{
             lock.unlock();   //释放锁
         } 
    }else {
        //如果不能获取锁,则直接做其他事情
    }

    4.lockInterruptibly()方法

    lockInterruptibly()方法,比较特使,可以说是Lock锁的特点之一,即线程在获取锁的过程中,如果一直获取不到锁,它不会立即放弃,直到这个线程被中断。即它能够响应中断, void lockInterruptibly() throws InterruptedException;接口的方法定义是声明了异常的,lock.lockInterruptibly()必须放在try块中或者在调用lockInterruptibly()的方法外声明抛出InterruptedException。它的使用形式如下:

    public void method() throws InterruptedException {
    
        lock.lockInterruptibly();
        try {  
         //.....
        }
        finally {
            lock.unlock();
        }  
    }

    5.newCondition()方法

    在使用notify()/notifyAll()方法的时候,被唤醒的线程是JVM随机选择的,最终能够获取锁的线程也许不是我们想要的。在Lock中结合Condition类可以实现“选择性通知”。即可以通过Condition对象唤醒指定的线程。示例如下:

     1 public class LockConditionDemo {
     2     
     3     private Lock lock = new ReentrantLock();
     4     private Condition condition = lock.newCondition();
     5     
     6     public void testMethod(){
     7         try {
     8             lock.lock();
     9             System.out.println(Thread.currentThread().getName()+" test begin");
    10             condition.await();//当前线程进入等待状态,释放lock锁
    11             System.out.println(Thread.currentThread().getName()+" test end");
    12         } catch (InterruptedException e) {
    13             e.printStackTrace();
    14         }finally{
    15             lock.unlock();
    16             System.out.println(Thread.currentThread().getName()+" 释放锁");
    17         }
    18     }
    19     
    20     public void testMethod2(){
    21         try {
    22             lock.lock();
    23             System.out.println(Thread.currentThread().getName()+" test2 begin");
    24             condition.signal();//通知处于等待lock锁状态的线程
    25             System.out.println(Thread.currentThread().getName()+" test2 end");
    26         }finally{
    27             lock.unlock();
    28             System.out.println(Thread.currentThread().getName()+" 释放锁");
    29         }
    30     }
    31     
    32     public static void main(String[] args) {
    33         LockConditionDemo lockConditionDemo = new LockConditionDemo();
    34         new Thread(new Mythread3(lockConditionDemo)).start();
    35         new Thread(new Mythread4(lockConditionDemo)).start();
    36     }
    37 
    38 }
    39 
    40 class Mythread3 implements Runnable{
    41     private LockConditionDemo lockConditionDemo;
    42     
    43     Mythread3(LockConditionDemo lockConditionDemo){
    44         this.lockConditionDemo = lockConditionDemo;
    45     }
    46     
    47     @Override
    48     public void run() {
    49         lockConditionDemo.testMethod();
    50     }
    51 }
    52 
    53 class Mythread4 implements Runnable{
    54     private LockConditionDemo lockConditionDemo;
    55 
    56     Mythread4(LockConditionDemo lockConditionDemo){
    57         this.lockConditionDemo = lockConditionDemo;
    58     }
    59     
    60     @Override
    61     public void run() {
    62         lockConditionDemo.testMethod2();
    63     }
    64 }

    结果输出:

    Thread-0 test begin
    Thread-1 test2 begin
    Thread-1 test2 end
    Thread-1 释放锁
    Thread-0 test end
    Thread-0 释放锁

    三、Lock和synchronized的选择

    1.lock和synchronized的不同

    • Lock是一个接口,而synchronized是Java中的关键字,synchronized是内置的语言实现。

    • synchronized在发生异常时,会自动释放线程占有的锁,因此不会导致死锁现象发生;而Lock在发生异常时,如果没有主动通过unLock()去释放锁,则很可能造成死锁现象,因此使用Lock时需要在finally块中释放锁。

    • Lock可以让等待锁的线程响应中断,而synchronized却不行,使用synchronized时,等待的线程会一直等待下去,不能够响应中断

    • 通过Lock可以知道有没有成功获取锁,而synchronized却无法办到

    • Lock可以提高多个线程进行读操作的效率

  • 相关阅读:
    我的物联网项目(七)前期线上事故
    我的物联网项目(六)推广策略
    我的物联网项目(五)下单渠道
    我的物联网项目(四)订单系统
    我的物联网项目(三)平台架构
    我的物联网项目(二)初建团队
    我的物联网项目(一)开端
    從需求分析開始
    提升GDI画图的效率
    C#写COM组件,JS调用控件
  • 原文地址:https://www.cnblogs.com/liupiao/p/9333276.html
Copyright © 2011-2022 走看看