博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Java多线程_ReentrantLock
阅读量:4957 次
发布时间:2019-06-12

本文共 5679 字,大约阅读时间需要 18 分钟。

ReentrantLock是重入锁,它与synchronized很像,它是synchronized的加强版,因为它具有一些synchronized没有的功能。

下面我们看看两者的区别:
synchronized具有一定的局限性:

  • 当线程尝试获取锁的时候,如果获取不到锁会一直阻塞;
  • 如果获取锁的线程进入休眠或者阻塞,除非当前线程异常,否则其他线程尝试获取锁必须一直等待;
  • 是非公平的。

而ReentrantLock实现了AQS,可以完成下列功能:

  • 可中断响应;
  • 锁申请等待限时;
  • 公平锁;
  • 与Condition一起使用,实现synchronized与wait/notify的功能。

引入几个概念:

提到ReentrantLock,我们不得不明白几个概念:

  • 可重入锁。可重入锁是指同一个线程可以多次获取同一把锁。ReentrantLock和synchronized都是可重入锁。
  • 可中断锁。可中断锁是指线程尝试获取锁的过程中,是否可以响应中断。synchronized是不可中断锁,ReentrantLock则提供了中断功能。
  • 公平锁与非公平锁。公平锁是指多个线程必须按顺序,不许插队。非公平锁允许插队。synchronized是非公平锁,而ReentrantLock的默认实现是非公平锁,但是也可以设置为公平锁。
  • CAS,在前面已经提到。

使用示例

具体用法通过简单代码通过代码演示:

import java.util.concurrent.locks.ReentrantLock;public class ReentrantLockDemo implements Runnable {    static ReentrantLock lock = new ReentrantLock();    static int count = 0;    @Override    public void run() {        for (int i = 0; i < 10000; i++) {            lock.lock();            try {                count++;            } finally {                lock.unlock();            }        }    }    public static void main(String[] args) {        ReentrantLockDemo r = new ReentrantLockDemo();        Thread t1 = new Thread(r);        Thread t2 = new Thread(r);        t1.start();        t2.start();        try {            t1.join();            t2.join();        } catch (InterruptedException e) {            e.printStackTrace();        }        System.out.println(count);        //结果输出:20000    }}

 

通过ReentrantLock解决死锁问题:

 

import java.util.concurrent.locks.ReentrantLock;public class KillDeadlockDemo implements Runnable {    static ReentrantLock lock1 = new ReentrantLock();    static ReentrantLock lock2 = new ReentrantLock();    int lock;    public KillDeadlockDemo(int lock) {        super();        this.lock = lock;    }    @Override    public void run() {        try {            if (lock == 1) {                lock1.lockInterruptibly();                Thread.sleep(500);                lock2.lockInterruptibly();            } else {                lock2.lockInterruptibly();                Thread.sleep(500);                lock1.lockInterruptibly();            }        } catch (InterruptedException e) {        } finally {            if (lock1.isHeldByCurrentThread()) {                lock1.unlock();            }            if (lock2.isHeldByCurrentThread()) {                lock2.unlock();            }            System.out.println(Thread.currentThread().getName() + "退出!");        }    }    public static void main(String[] args) {        KillDeadlockDemo kdd1 = new KillDeadlockDemo(1);        KillDeadlockDemo kdd2 = new KillDeadlockDemo(2);        Thread t1 = new Thread(kdd1);        Thread t2 = new Thread(kdd2);        t1.start();        t2.start();        try {            Thread.sleep(1000);        } catch (InterruptedException e) {            e.printStackTrace();        }        t2.interrupt();      /*       *结果输出:       *Thread-1退出!       *Thread-0退出!       */
}
}

 

使用 tryLock()或者tryLock(long timeout, TimeUtil unit) 方法进行一次限时的锁等待,也可以解决死锁问题:

import java.util.concurrent.TimeUnit;import java.util.concurrent.locks.ReentrantLock;public class TryLockDemo implements Runnable {    static ReentrantLock lock = new ReentrantLock();    @Override    public void run() {        try {            if (lock.tryLock(1, TimeUnit.SECONDS)) {                Thread.sleep(1100);            } else {                System.out.println(Thread.currentThread().getName() + "获取锁失败!释放");            }        } catch (InterruptedException e) {            e.printStackTrace();        } finally {            if (lock.isHeldByCurrentThread()) {                lock.unlock();            }        }    }    public static void main(String[] args) {        TryLockDemo td = new TryLockDemo();        Thread thread1 = new Thread(td);        Thread thread2 = new Thread(td);        thread1.start();        thread2.start();        try {            thread1.join();            thread2.join();        } catch (InterruptedException e) {            e.printStackTrace();        }              //结果输出:Thread-1获取锁失败!释放    }}

 

公平锁演示:

import java.util.concurrent.locks.ReentrantLock;public class FairLockDemo implements Runnable {    static ReentrantLock lock = new ReentrantLock(true);    @Override    public void run() {        while (true) {            try {                lock.lock();                System.out.println(Thread.currentThread().getName() + " get lock");                Thread.sleep(1000);            } catch (InterruptedException e) {            } finally {                lock.unlock();            }        }    }    public static void main(String[] args) {        FairLockDemo fld = new FairLockDemo();        Thread t1 = new Thread(fld);        Thread t2 = new Thread(fld);        Thread t3 = new Thread(fld);        t1.start();        t2.start();        t3.start();/**Thread-0 get lock*Thread-1 get lock*Thread-2 get lock*Thread-0 get lock*Thread-1 get lock*Thread-2 get lock*..........*/    }}

浅谈原理

 

ReentrantLock使用到了AQS,AQS的全称为AbstractQueuedSynchronizer,这个类也是在java.util.concurrent.locks下面。这个类似乎很不容易看懂,因为它仅仅是提供了一系列公共的方法,让子类来调用。

先以ReentrantLock排它锁为例开始展开讲解如何利用AQS的。

ReentrantLock的构造方法有两个,如下图所示:

 

对象中有一个属性叫sync,有两种不同的实现类,默认是“NonfairSync”非公平锁来实现,而另一个“FairSync”公平锁它们都是排它锁的内部类,不论用那一个都能实现排它锁,只是内部可能有点原理上的区别。先以“NonfairSync”类为例,它的lock()方法

 

lock()方法先通过CAS尝试将状态从0修改为1。若直接修改成功,前提条件自然是锁的状态为0,则直接将线程的OWNER修改为当前线程。若上一个动作未成功,则会间接调用了acquire(1)来继续操作,这个acquire(int)方法就是在AbstractQueuedSynchronizer当中了。

 

首先获取这个锁的状态,如果状态为0,则尝试设置状态为传入的参数(这里就是1),若设置成功就代表自己获取到了锁,返回true了。状态为0设置1的动作在外部就有做过一次,内部再一次做只是提升概率,而且这样的操作相对锁来讲不占开销。

○ 如果状态不是0,则判定当前线程是否为排它锁的Owner,如果是Owner则尝试将状态增加acquires(也就是增加1),如果这个状态值越界,则会抛出异常提示,若没有越界,将状态设置进去后返回true.如果状态不是0,且自身不是owner,则返回false。

 

转载于:https://www.cnblogs.com/ericz2j/p/10292664.html

你可能感兴趣的文章
利用sed把一行的文本文件改成每句一行
查看>>
使用Asyncio的Coroutine来实现一个有限状态机
查看>>
Android应用开发:核心技术解析与最佳实践pdf
查看>>
python——爬虫
查看>>
2.2 标识符
查看>>
孤荷凌寒自学python第五天初识python的列表
查看>>
孤荷凌寒自学python第五十八天成功使用python来连接上远端MongoDb数据库
查看>>
求一个字符串中最长回文子串的长度(承接上一个题目)
查看>>
简单权限管理系统原理浅析
查看>>
springIOC第一个课堂案例的实现
查看>>
求输入成绩的平均分
查看>>
ORACLE 数据库概述
查看>>
php PDO (转载)
查看>>
wordpress自动截取文章摘要代码
查看>>
[置顶] 一名优秀的程序设计师是如何管理知识的?
查看>>
scanf和gets
查看>>
highcharts 图表实例
查看>>
ubuntu下如何查看用户登录及系统授权相关信息
查看>>
秋季学期学习总结
查看>>
SpringBoot 优化内嵌的Tomcat
查看>>