博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Java多线程之synchronized增强版——ReentrantLock
阅读量:7074 次
发布时间:2019-06-28

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

接下来介绍比synchronized功能上更丰富的关键字:重入锁

  • 灵活性:

    public class ReentrantLockTest implements Runnable{    public static ReentrantLock lock = new ReentrantLock();    public static int flag = 0;    @Override    public void run() {        for (int i = 0; i < 10000000; i++) {            lock.lock();            try {                flag++;            } finally {                lock.unlock();            }        }    }    public static void main(String args[]) throws InterruptedException {        ReentrantLockTest test = new ReentrantLockTest();        Thread first = new Thread(test);        Thread second = new Thread(test);        first.start();        second.start();        first.join();        second.join();        System.out.println(flag);    }}复制代码

    lock.lock();这里,通过重入锁保护临界区安全,以免发生线程安全问题。

    lock.unlock();这里,必须手动指示释放锁的操作,否则其他线程将无法获得。

    在这段代码里,我们能见到重入锁灵活的特点。但为什么叫“重入”呢?

    看下段代码:

    @Override    public void run() {        for (int i = 0; i < 10000000; i++) {            lock.lock();            lock.lock();            try {                flag++;            } finally {                lock.unlock();                lock.unlock();            }        }    }复制代码

    因为该锁能反复进进出出。但要注意一下:

    在上段代码中,锁是可以重复获取的。如果不允许,则该线程在第二次获取锁时会和自己产生死锁问题。同时也要注意,线程获取多少次锁就要释放多少此锁。当获取锁的次数大于释放锁的次数、相当于该线程还持有锁。当获取锁的次数少于释放锁的次数、则会得到一个java.lang.IllegalMonitorStateException异常。

  • 中断响应:

    二话不说贴代码:

    public class ReentrantLockTest implements Runnable{    public static ReentrantLock producer = new ReentrantLock();    public static ReentrantLock consumer = new ReentrantLock();    public int flag = 0;    public ReentrantLockTest(int flag){        this.flag = flag;    }    @Override    public void run() {        try {            if (flag == 0) {                producer.lockInterruptibly();                try {                    Thread.sleep(500);                } catch (InterruptedException e) {                }                consumer.lockInterruptibly();                System.out.println(Thread.currentThread().getName() + "完成工作");            } else {                consumer.lockInterruptibly();                try {                    Thread.sleep(500);                } catch (InterruptedException e) {                }                producer.lockInterruptibly();                System.out.println(Thread.currentThread().getName() + "完成工作");            }        } catch (InterruptedException e) {            e.printStackTrace();        } finally {            if (producer.isHeldByCurrentThread()) {                producer.unlock();            }            if (consumer.isHeldByCurrentThread()) {                consumer.unlock();            }            System.out.println(Thread.currentThread().getName() + ": 线程退出");        }    }    public static void main(String args[]) throws InterruptedException {        ReentrantLockTest producerThread = new ReentrantLockTest(1);        ReentrantLockTest consumerThread = new ReentrantLockTest(0);        Thread first = new Thread(producerThread);        Thread second = new Thread(consumerThread);        first.setName("producer");        second.setName("consumer");        first.start();        second.start();        Thread.sleep(1000);        second.interrupt();    }}复制代码

    这是一段容易造成死锁的代码,具体原因大家应该懂。当执行到second.interrupt();时,second线程在等待锁时被中断,故second线程会放弃对锁的申请、并对已持有资源进行释放。first线程则能够正常获取所等待的锁并继续执行下去。

    结果如下:

    producer完成工作        java.lang.InterruptedException        consumer: 线程退出        producer: 线程退出    	at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireInterruptibly(AbstractQueuedSynchronizer.java:898)    	at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireInterruptibly(AbstractQueuedSynchronizer.java:1222)    	at java.util.concurrent.locks.ReentrantLock.lockInterruptibly(ReentrantLock.java:335)    	at blog.ReentrantLockTest.run(ReentrantLockTest.java:22)    	at java.lang.Thread.run(Thread.java:748)复制代码

    真正完成工作的只有producer线程。

  • 限时等待:

    除了用中断避免死锁问题外,还可以用限时等待锁来避免。限时等待锁有点像是系统自动完成线程中断的感觉。先展示下限时等待锁的使用:

    public class showWait implements Runnable {    public static ReentrantLock lock = new ReentrantLock();    @Override    public void run() {        try {            if (lock.tryLock(5, TimeUnit.SECONDS)) {                Thread.sleep(6000);            } else {                System.out.println(Thread.currentThread().getName() + " get lock failed");            }        } catch (InterruptedException e) {            e.printStackTrace();        }    }    public static void main(String args[]) {        showWait test = new showWait();        Thread t1 = new Thread(test);        Thread t2 = new Thread(test);        t1.setName("producer");        t2.setName("consumer");        t1.start();        t2.start();    }}复制代码

    上述代码展示了lock.tryLock(5,TimeUnit.SECONDS);的使用,在这里,该方法接收两个参数,分别是时长和计时单位。

    该方法也可以不带参数,当不带参数时,当前线程会尝试获取锁,如果锁未被其他线程占有则会申请成功并立即返回true。如果锁被其他线程占用则立即返回false。这种方法不会引起线程等待,所以不会产生死锁问题。

  • 公平锁:

    在多大数情况下,锁的申请都是非公平性的,有时会造成线程饥饿问题。当我们使用synchronized时产生的锁是非公平性的,但我们使用ReentrantLock时可以通过构造函数进行指定其公平性。 public ReentrantLock(boolean fair)

    当参数为true时为公平锁,默认为非公平锁。公平锁看起来挺优美的,但其必然要维护一个等待队列,其性能必然会降低

  • 整理:

    • lock()
    • lockInterruptibly()
    • tryLock()
    • tryLock(long time,TimeUnit unit)
    • unlock()

    大家回顾下这几个方法吧。

转载地址:http://rokml.baihongyu.com/

你可能感兴趣的文章
cookie的secure属性详解
查看>>
[Jquery] 实现鼠标移到某个对象,在旁边显示层。
查看>>
logrotate工具的使用
查看>>
我的友情链接
查看>>
华尔街为何弃苹果而力挺谷歌?因为谷歌无对手
查看>>
电脑蓝屏代码含义和解决方法
查看>>
Ubuntu server 14.04升级16.04
查看>>
Spring事务
查看>>
hive 更多资料urls
查看>>
bash中获取其他时间的日期
查看>>
java27:集合框架
查看>>
判断一个数组里是否有重复的值(重复返回true)
查看>>
&lt;备份&gt;10月11日系统安装与除错,sudo讲解
查看>>
网站优化简单探讨
查看>>
samba部署和优化
查看>>
libsuperuser简介
查看>>
Python自动化开发学习-爬虫2
查看>>
Scribe配置文件解析
查看>>
HISTFILESIZE与HISTSIZE的区别
查看>>
Parallels Desktop 8 虚拟机安装 windows 教程
查看>>