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)
原文地址:https://www.cnblogs.com/zhangmingda/p/14698504.html