zoukankan      html  css  js  c++  java
  • java 多线程 Thread 锁ReentrantLock;Condition等待与通知;公平锁

    1,介绍:

    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
     
    在JAVA的多线程编程中,我们可以使用synchronized关键字来实现线程之间的同步互斥,但是JDK1.5中新增了ReentrantLock类同样也能达到效果,并且功能上更加强大。比如有嗅探锁定功能,多路分支通知功能,并且使用上比synchronized更加灵活。

    2,基本使用:

    使用lock()方法上锁,使用unlock()方法解锁
    效果:
    • 加锁和解锁之间的代码块,多个线程运行变串行

    代码示例:

    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    /**
     * @ClassName LockReentrantLockExample
     * @projectName: object1
     * @author: Zhangmingda
     * @description: XXX
     * date: 2021/4/24.
     */
    public class LockReentrantLockExample {
        public static void main(String[] args) {
            Lock lock = new ReentrantLock();
            Runnable r = () -> {
                String tName = Thread.currentThread().getName();
                System.out.println("子线程运行");
                lock.lock();
                System.out.println(tName + "准备睡觉了");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(tName + "睡醒了");
                lock.unlock();
            };
    
            Thread thread = new Thread(r,"child1");
            Thread thread1 = new Thread(r,"child2");
            Thread thread2 = new Thread(r,"child3");
            thread.start();
            thread1.start();
            thread2.start();
        }
    }

    3、等待/通知 机制的实现:

    3.1 Conditiond对象的await方法实现等待,signal方法实现通知
    import java.util.concurrent.locks.Condition;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    /**
     * @ClassName LockReentrantLockCondition
     * @projectName: object1
     * @author: Zhangmingda
     * @description: XXX
     * date: 2021/4/25.
     */
    public class LockReentrantLockCondition {
        public static void main(String[] args) {
            //
            Lock lock = new ReentrantLock();
            //暂停唤醒工具
            Condition condition = lock.newCondition();
            //子线程本地变量
            ThreadLocal<String> tName = new ThreadLocal<>(){
                @Override
                protected String initialValue() {
                    return Thread.currentThread().getName();
                }
            };
            Runnable r = new Runnable() {
                @Override
                public void run() {
                    System.out.println(tName.get() + "开始运行!");
                    lock.lock();
                    try {
                        Thread.sleep(1000);
                        System.out.println(tName.get() + "暂停");
                        condition.await();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(tName.get() + "运行结束");
                    lock.unlock();
                }
            };
            Thread thread = new Thread(r,"A");
            Thread thread1 = new Thread(r,"B");
            Thread thread2 = new Thread(r,"C");
            Thread call  = new Thread(){
                @Override
                public void run() {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    lock.lock();
                    condition.signalAll();
                    lock.unlock();
                }
            };
            thread.start();
            thread1.start();
            thread2.start();
            call.start();
    
        }
    }

    效果:锁依次被ABC线程获取到,分别进入await()状态。最后call线程才获取到ABC抛出的锁,

     3.2等待通知案例:做馒头,吃馒头,相对优化版

    • 做馒头多线程几乎并发,做馒头耗时并发sleep;
    • 吃馒头几乎并发,sleep 并发执行 lock.unlock();在 sleep外
    import java.util.LinkedList;
    import java.util.Random;
    import java.util.concurrent.locks.Condition;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    /**
     * @ClassName ReentrantLockConditionMantou
     * @projectName: object1
     * @author: Zhangmingda
     * @description: XXX
     * date: 2021/4/25.
     */
    public class ReentrantLockConditionMantou {
    
        private static void reentrantLockCondition(){
            LinkedList<String> mantous = new LinkedList<>();
            Lock lock = new ReentrantLock();
            Condition fuwuyuan = lock.newCondition();
            Runnable produce = () -> {
                String threadName = Thread.currentThread().getName();
                while (true) {
                    lock.lock();
                    if (mantous.size() >= 10 ){
                        fuwuyuan.signalAll(); //有可能被另一个厨师实例获取到
                        try {
                            Thread.sleep(100); //两个厨师,避免厨师之间相互获取到锁,给点时间让食客获取到锁
                            System.out.println(threadName + ": 馒头做够了.........................你们来吃吧");
                            fuwuyuan.await();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        lock.unlock();
                    }
                    else {
                        mantous.add("馒头");
                        System.out.println(threadName + ",我生产了一个馒头,当前的馒头数量是" + mantous.size());
                        lock.unlock();
                        try {
                            Thread.sleep(1000); //做馒头花时间
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            };
            Runnable customer = () -> {
                String threadName = Thread.currentThread().getName();
                Random random = new Random();
                while (true) {
                    //吃馒头的数量
                    int buyMantou = Math.abs(random.nextInt()) % 5 + 1;
                    //获取锁
                    lock.lock();
                    if (mantous.size() < buyMantou) {
                        fuwuyuan.signalAll();
                        System.out.println("33[31;1m" + threadName + "想吃" + buyMantou + "只有" +mantous.size() + "个了,快做馒头吧33[0m");
                        try {
                            //稍微歇一歇
                            Thread.sleep(300); //给点机会上厨师获取到锁,避免其他食客获取到锁
                            fuwuyuan.await();
                            lock.unlock();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
    
                    } else {
                        //吃馒头
                        for (int i = 0; i < buyMantou; i++) {
                            if(mantous.poll() == null){
                                System.out.println(threadName + "吃了个空.....");
                            }
                        }
                        System.out.println(threadName + ",我吃了" + buyMantou + "个馒头,当前馒头的数量是:" + mantous.size());
                        lock.unlock();
                        try {
                            Thread.sleep(2000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            };
            Thread thread = new Thread(produce,"厨师1");
            Thread thread1 = new Thread(produce,"厨师2");
            Thread thread2 = new Thread(customer,"饭桶1");
            Thread thread3 = new Thread(customer,"饭桶2");
            Thread thread4 = new Thread(customer,"饭桶3");
            thread.start();
            thread1.start();
            thread2.start();
            thread3.start();
            thread4.start();
        }
        public static void main(String[] args) {
            reentrantLockCondition();
        }
    }
    "C:Program FilesJetBrainsIntelliJ IDEA Community Edition 2019.3jbrinjava.exe" "-javaagent:C:Program FilesJetBrainsIntelliJ IDEA Community Edition 2019.3libidea_rt.jar=54462:C:Program FilesJetBrainsIntelliJ IDEA Community Edition 2019.3in" -Dfile.encoding=UTF-8 -classpath D:JavaStudy2object1outproduction多线程 ReentrantLockConditionMantou
    厨师1,我生产了一个馒头,当前的馒头数量是1
    厨师2,我生产了一个馒头,当前的馒头数量是2
    饭桶3,我吃了2个馒头,当前馒头的数量是:0
    饭桶1想吃4只有0个了,快做馒头吧
    饭桶2想吃3只有0个了,快做馒头吧
    饭桶1想吃5只有0个了,快做馒头吧
    饭桶2想吃4只有0个了,快做馒头吧
    厨师1,我生产了一个馒头,当前的馒头数量是1
    厨师2,我生产了一个馒头,当前的馒头数量是2
    饭桶1想吃4只有2个了,快做馒头吧
    饭桶2想吃5只有2个了,快做馒头吧
    饭桶1,我吃了1个馒头,当前馒头的数量是:1
    饭桶3,我吃了1个馒头,当前馒头的数量是:0
    厨师2,我生产了一个馒头,当前的馒头数量是1
    厨师1,我生产了一个馒头,当前的馒头数量是2
    厨师1,我生产了一个馒头,当前的馒头数量是3
    厨师2,我生产了一个馒头,当前的馒头数量是4
    饭桶1,我吃了3个馒头,当前馒头的数量是:1
    饭桶3想吃3只有1个了,快做馒头吧
    饭桶2想吃3只有1个了,快做馒头吧
    厨师1,我生产了一个馒头,当前的馒头数量是2
    厨师2,我生产了一个馒头,当前的馒头数量是3
    饭桶3想吃5只有3个了,快做馒头吧
    饭桶2想吃4只有3个了,快做馒头吧
    饭桶3,我吃了1个馒头,当前馒头的数量是:2
    厨师1,我生产了一个馒头,当前的馒头数量是3
    厨师2,我生产了一个馒头,当前的馒头数量是4
    饭桶1,我吃了2个馒头,当前馒头的数量是:2
    厨师1,我生产了一个馒头,当前的馒头数量是3
    厨师2,我生产了一个馒头,当前的馒头数量是4
    饭桶3想吃5只有4个了,快做馒头吧
    饭桶2,我吃了4个馒头,当前馒头的数量是:0
    厨师2,我生产了一个馒头,当前的馒头数量是1
    厨师1,我生产了一个馒头,当前的馒头数量是2
    饭桶1想吃4只有2个了,快做馒头吧
    饭桶3想吃5只有2个了,快做馒头吧
    饭桶1,我吃了2个馒头,当前馒头的数量是:0
    厨师2,我生产了一个馒头,当前的馒头数量是1
    厨师1,我生产了一个馒头,当前的馒头数量是2
    饭桶2想吃5只有2个了,快做馒头吧
    饭桶3想吃4只有2个了,快做馒头吧
    厨师2,我生产了一个馒头,当前的馒头数量是3
    厨师1,我生产了一个馒头,当前的馒头数量是4
    饭桶2,我吃了1个馒头,当前馒头的数量是:3
    饭桶1,我吃了3个馒头,当前馒头的数量是:0
    厨师2,我生产了一个馒头,当前的馒头数量是1
    厨师1,我生产了一个馒头,当前的馒头数量是2
    饭桶2想吃5只有2个了,快做馒头吧
    饭桶3想吃3只有2个了,快做馒头吧
    厨师1,我生产了一个馒头,当前的馒头数量是3
    厨师2,我生产了一个馒头,当前的馒头数量是4
    饭桶2,我吃了1个馒头,当前馒头的数量是:3
    饭桶1,我吃了3个馒头,当前馒头的数量是:0
    厨师1,我生产了一个馒头,当前的馒头数量是1
    厨师2,我生产了一个馒头,当前的馒头数量是2
    厨师2,我生产了一个馒头,当前的馒头数量是3
    饭桶2,我吃了1个馒头,当前馒头的数量是:2
    厨师1,我生产了一个馒头,当前的馒头数量是3
    饭桶1想吃5只有3个了,快做馒头吧
    饭桶3,我吃了1个馒头,当前馒头的数量是:2
    厨师1,我生产了一个馒头,当前的馒头数量是3
    厨师2,我生产了一个馒头,当前的馒头数量是4
    饭桶2,我吃了3个馒头,当前馒头的数量是:1
    厨师2,我生产了一个馒头,当前的馒头数量是2
    厨师1,我生产了一个馒头,当前的馒头数量是3
    饭桶3,我吃了2个馒头,当前馒头的数量是:1
    厨师1,我生产了一个馒头,当前的馒头数量是2
    厨师2,我生产了一个馒头,当前的馒头数量是3
    饭桶2,我吃了2个馒头,当前馒头的数量是:1
    厨师1,我生产了一个馒头,当前的馒头数量是2
    厨师2,我生产了一个馒头,当前的馒头数量是3
    饭桶3想吃5只有3个了,快做馒头吧
    饭桶1想吃5只有3个了,快做馒头吧
    饭桶3,我吃了2个馒头,当前馒头的数量是:1
    厨师1,我生产了一个馒头,当前的馒头数量是2
    厨师2,我生产了一个馒头,当前的馒头数量是3
    饭桶2想吃4只有3个了,快做馒头吧
    饭桶1,我吃了3个馒头,当前馒头的数量是:0
    厨师2,我生产了一个馒头,当前的馒头数量是1
    厨师1,我生产了一个馒头,当前的馒头数量是2
    饭桶3想吃5只有2个了,快做馒头吧
    饭桶2,我吃了1个馒头,当前馒头的数量是:1
    厨师1,我生产了一个馒头,当前的馒头数量是2
    厨师2,我生产了一个馒头,当前的馒头数量是3
    厨师1,我生产了一个馒头,当前的馒头数量是4
    厨师2,我生产了一个馒头,当前的馒头数量是5
    饭桶1,我吃了5个馒头,当前馒头的数量是:0
    饭桶2想吃4只有0个了,快做馒头吧
    饭桶3想吃3只有0个了,快做馒头吧
    厨师2,我生产了一个馒头,当前的馒头数量是1
    厨师1,我生产了一个馒头,当前的馒头数量是2
    饭桶2,我吃了2个馒头,当前馒头的数量是:0
    饭桶1想吃1只有0个了,快做馒头吧
    饭桶3想吃2只有0个了,快做馒头吧
    饭桶1想吃1只有0个了,快做馒头吧
    厨师2,我生产了一个馒头,当前的馒头数量是1
    厨师1,我生产了一个馒头,当前的馒头数量是2
    饭桶3,我吃了1个馒头,当前馒头的数量是:1
    饭桶2,我吃了1个馒头,当前馒头的数量是:0
    厨师2,我生产了一个馒头,当前的馒头数量是1
    厨师1,我生产了一个馒头,当前的馒头数量是2
    饭桶3想吃4只有2个了,快做馒头吧
    饭桶1,我吃了2个馒头,当前馒头的数量是:0
    厨师2,我生产了一个馒头,当前的馒头数量是1
    厨师1,我生产了一个馒头,当前的馒头数量是2
    饭桶2想吃3只有2个了,快做馒头吧
    饭桶3想吃3只有2个了,快做馒头吧
    饭桶2,我吃了2个馒头,当前馒头的数量是:0
    厨师2,我生产了一个馒头,当前的馒头数量是1
    厨师1,我生产了一个馒头,当前的馒头数量是2
    饭桶1想吃5只有2个了,快做馒头吧
    饭桶3想吃5只有2个了,快做馒头吧
    厨师2,我生产了一个馒头,当前的馒头数量是3
    厨师1,我生产了一个馒头,当前的馒头数量是4
    饭桶1想吃5只有4个了,快做馒头吧
    饭桶3,我吃了3个馒头,当前馒头的数量是:1
    饭桶2想吃5只有1个了,快做馒头吧
    饭桶1想吃3只有1个了,快做馒头吧
    
    Process finished with exit code -1
    测试输出

    公平锁和非公平锁:

    锁Lock分为公平锁和非公平锁,公平锁表示线程获取锁的顺序是按照线程加锁的顺序来分配的,也就是先进先出的顺序。而非公平锁就是一种获取锁的抢占机制,是随机获取的,和公平锁不一样的就是先来的不一定先得到锁,这个方式可能造成某些线程一直拿不到锁。如果我们用synchronized的方式我们没有办法使用公平锁。
    创建公平锁的办法是在new ReentrantLock的时候加一个参数true.
    new ReentrantLock(true)
  • 相关阅读:
    mysql ibd 文件过大问题
    magento性能分析插件
    magento 自定义url路径 和 filter data 小结
    magento layout xml 小结
    magento 开启 3D secure credit card validation
    magento package
    docker安装与使用记录(debian9)
    Windows使用Charles对模拟器/真机进行抓包 问题记录
    windows10 windump使用记录
    使用systrace的问题记录
  • 原文地址:https://www.cnblogs.com/zhangmingda/p/14698504.html
Copyright © 2011-2022 走看看