同步控制和锁,ReenterLock和Condition的详细使用

本文深入探讨了Java并发编程中的重入锁ReentrantLock及其配套的Condition接口,阐述了它们相对于synchronized的优势和额外功能,如中断响应、限时等待和公平锁。同时介绍了信号量Semaphore、读写锁ReadWriteLock、倒计时器CountDownLatch以及线程阻塞工具LockSupport,详细讲解了这些工具在多线程控制中的应用和优势。
摘要由CSDN通过智能技术生成

多线程团队合作:同步控制和锁

同步控制是并发程序必不可少的重要手段。synchronized关键字就是一种最简单的控制方法。同时,wait()和notify()方法起到了线程等待和通知的作用。这些工具对于实现复杂的多线程协作起到了重要的作用。接下来将介绍synchronized,wait,notify方法的代替品(或者说是增强版)——重入锁,这个专题需要大家对多线程基本的内部锁synchronized,wait, notify方法先有基本的认识。

1 synchronized的功能扩展: 重入锁

重入锁完全可以代替synchronized关键字。在早期JDK版本,重入锁的性能远远优于synchronized关键字,在JDK后期版本,对synchronized关键字做了大量的优化,使得两者的性能差不多。

下面展示一段简单的重入锁ReentrantLock使用案例:

public class ReenterLock implements Runnable {
   
    public static ReentrantLock lock = new ReentrantLock();
    public static int i=0;

    public void run() {
   
        for(int j=0;j<10000000;j++){
   
            lock.lock();
//            lock.lock();
            try {
   
                i++;
            }finally {
   
                lock.unlock();
//                lock.unlock();
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
   
        ReenterLock r1 = new ReenterLock();
        Thread t1 = new Thread(r1);
        Thread t2 = new Thread(r1);
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println(i);
    }
}

​ 上述代码创建了一个全局的ReentrantLock对象,这个对象就是重入锁对象,该对象的lock()和unlock()方法之间7~12行的代码区域就是重入锁的保护临界区,确保了多线程对i变量的操作安全性。

​ 从这段代码可以看到,与synchronized相比,重入锁有着显示操作的过程。开发人员必须手动指定何时加锁 ,何时释放锁。也正是因为这样,重入锁逻辑控制远远要好于synchronized。但值得注意的是,在退出临界区时,必须记得要释放锁,否者永远没有机会再访问临界区了,会造成其线程的饥饿甚至是死锁。

​ 重入锁之所以被称作重入锁是因为重入锁是可以反复进入的。当然,这里的反复进入仅仅局限于一个线程。上诉代码还可以这样写:

    public void run() {
   
        for(int j=0;j<10000000;j++){
   
            lock.lock();
            lock.lock();
            try {
   
                i++;
            }finally {
   
                lock.unlock();
                lock.unlock();
            }
        }
    }

​ 在这种情况下,一个线程连续两次获得同一把锁。这是允许的!但要注意的是,如果一个线程多次获得锁,那么在释放锁的时候,也必须释放相同次数。如果释放的次数多了,那么会得到一个java.lang.IllegalMonitorStateException异常,反之,如果释放所得次数少了,MAME相当于县城还持有这个锁,因此,其他线程也无法进入临界区

​ 处使用上的灵活性以外,重入所还提供了一些高级功能。比如重入锁提供的中断处理的能力

1.1 中断响应

​ 重入锁除了提供上述的基本功能外,还提供了一些高级功能。比如,重入锁可以提供中断处理的能力。这是一个非常重要的功能,synchronized是没有中断功能的。在等待锁的过程中,程序可以根据需要取消对锁的请求。这是synchronized办不到的。也就是说,重入锁具有解除死锁的功能。

​ 比如你和朋友越好一起去打球,如果你等了半个小时朋友没有到,你突然接到一个电话,说由于突发情况,朋友不能如期钱来了,那么你一定扫兴的达到回府了。中断正是提供了一套类似的机制。如果一个县城正在等待锁,那么他依然可以收到一个通知,被告知无需等待,可以停止工作了,这种情况对于处理死锁是有一定帮助的。

​ 下面的代码产生了一个死锁,得益于锁的中断,我们可以轻易的解决这个死锁:

public class IntLock implements Runnable {
   
    public static ReentrantLock lock1 = new ReentrantLock();
    public static ReentrantLock lock2 = new ReentrantLock();
    int lock;

    public IntLock(int lock) {
   
        this.lock = lock;
    }

    public void run() {
   
        try {
   
            if (this.lock == 1) {
   
                lock1.lockInterruptibly();
                Thread.sleep(500);
                lock2.lockInterruptibly();
            } else {
   
                lock2.lockInterruptibly();
                Thread.sleep(500);
                lock1.lockInterruptibly();
            }
        } catch (InterruptedException e) {
   
            e.printStackTrace();
        } finally {
   
            if (lock1.isHeldByCurrentThread())
                lock1.unlock();
            if (lock2.isHeldByCurrentThread())
                lock2.unlock();
            System.out.println(this.lock + "线程退出");
        }
    }

    public static void main(String[] args) throws InterruptedException {
   
        IntLock r1 = new IntLock(1);
        IntLock r2 = new IntLock(2);
        Thread t1 = new Thread(r1);
        Thread t2 = new Thread(r2);
        t1.start();
        t2.start();
        Thread.sleep(1000);
        t2.interrupt();
    }
}

​ 线程t1和线程t2启动后,t1先占用lock1,再占用lock2;t2先占用lock2,再请求lock1。这样很容易形成t1和t2之间的互相等待,造成死锁。在这里,对锁的请求,统一使用lockInterruptibly()方法。这是一个可以对中断进行响应的锁申请动作,即在等待锁的过程中可以响应中断。

​ 在t1和t2线程start后,主线程39行main进入休眠,此时t1和t2线程处于死锁状态,然后主线程第40行main中断t2线程,故t2会放弃对lock1的请求,同时释放lock2。这个操作使得t1可以获得lock2从而继续执行下去。

​ 执行上诉代码,将输出:

2线程退出
1线程退出
java.lang.InterruptedException
	at com.lxs.demo.IntLock.run(IntLock.java:24)
	at java.lang.Thread.run(Thread.java:745)

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值