Loading...
墨滴

Bayanbulake

2021/12/24  阅读:37  主题:自定义主题1

Java多线程通信方式[二]

使用Lock重构生产者消费者及线程通信

在上一篇在文章中,都是使用synchronized对方法进行加锁,然后通过wait()notify()notifyAll进行线程通信。除此之外,还可以使用Lock给方法加锁,然后使用Condition接口提供的await()singalAll()进行线程通信。二者的对应关系如下表:

Lock加锁目的 synchronzied Lock接口的Lock()、unlock()方法
使得当前线程处于等待状态 wait() Condition接口提供的await()
唤醒当前对象的线程 notify()、notifyAll() Condition接口提供的singal()、signalAll()方法

接下来使用LockCondition重构之前的第一个生产者和消费者代码。

package com.geovis.bin.custom.study.wangbin.lock.contion.lock;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @Author: Wangb
 * @EMail: 1149984363@qq.com
 * @Date: 24/12/2021 下午4:53
 * @Description
 */

public class CarStock1 {
    int cars;
    private Lock lock = new ReentrantLock();
    private Condition condition = lock.newCondition();

    //生产车
    public void productCar() {
        lock.lock();

        try {
            if (cars <20) {
                System.out.println("生产车...."+ cars);
                cars++;
                condition.signalAll();//唤醒
            } else {
                condition.await();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }

    }
    //消费车
    public void resumeCar() {
        lock.lock();

        try {
            if (cars >0) {
                System.out.println("销售车...."+ cars);
                cars--;
                condition.signalAll();//唤醒
            } else {
                condition.await();//等待
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }

    }
}

剩下的消费者、生产者、实体类、测试类都与上一篇文章一样。

循环打印123案例

为了加深对lock的理解,我们再来学习一个循环打印123的例子。

package com.geovis.bin.custom.study.wangbin.lock.contion.print;

/**
 * @Author: Wangb
 * @EMail: 1149984363@qq.com
 * @Date: 24/12/2021 下午6:05
 * @Description
 */


import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class LoopPrint123 {
    //初始值
    private int number = 1;
    //Lock对象
    private Lock lock = new ReentrantLock();
    //通知打印1的信号
    private Condition con1 = lock.newCondition();
    //通知打印2的信号
    private Condition con2 = lock.newCondition();
    //通知打印3的信号
    private Condition con3 = lock.newCondition();

    public void print1() {
        lock.lock();
        try {
            //本方法只能打印1,如果不是1就等待
            if (number != 1) {
                con1.await();//wait()
            }
            //如果是1,就打印“1”
            System.out.println(1);
            //打印完“1”之后,去唤醒打印“2”的线程
            number = 2;
            con2.signal();//notify;
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public void print2() {
        lock.lock();
        try {
            //本方法只能打印2,如果不是2就等待
            if (number != 2) {
                con2.await();
            }
            //如果是2,就打印“2”
            System.out.println(2);
            //打印完“2”之后,去唤醒打印“3”的线程
            number = 3;
            con3.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public void print3() {
        lock.lock();

        try {
            //本方法只能打印3,如果不是3就等待
            if (number != 3) {
                con3.await();
            }
            //如果是3,就打印“3”
            System.out.println(3);
            //打印完“3”之后,去唤醒打印“1”的线程
            number = 1;
            con1.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}

测试类

package com.geovis.bin.custom.study.wangbin.lock.contion.print;

/**
 * @Author: Wangb
 * @EMail: 1149984363@qq.com
 * @Date: 24/12/2021 下午6:06
 * @Description
 */

public class TestLoopPrint123 {
    public static void main(String[] args) {
        LoopPrint123 loopPrint = new LoopPrint123();
        //创建一个线程,用于不断的打印1(调用print1()方法)
        new Thread(() -> {
            while (true) {
                loopPrint.print1();
            }
        }).start();
        //创建一个线程,用于不断的打印2(调用print2()方法)
        new Thread(() -> {
            while (true) {
                loopPrint.print2();
            }
        }).start();

        //创建一个线程,用于不断的打印3(调用print3()方法)
        new Thread(() -> {
            while (true) {
                loopPrint.print3();
            }
        }).start();
    }
}

运行结果如下图:

现在我们来看下Locksynchroized两种加锁方式的区别:

synchronized Lock
形式 Java关键字 接口
常用的实现类是ReentrantLock
锁的获取(加锁) 如果要访问的资源被加锁,则会一直等待,直到该资源解锁。 有多种方式
可以使用synchronized类似一直等待;也可以先尝试获取锁,如果被占用就放弃获取,而不再一直等待。
锁的释放形式(解锁) 1. synchronized修饰的方法或者代码块执行完毕。
2.发生异常
必须使用unlock()方法释放
一般建议将unlock()写在finally代码块中
锁的状态 无法判断 可以判断
中断等待 不支持 支持

通过上表就可以得知,使用synchronized加锁,当发生死锁时,相互争夺资源的线程就会一直等待。而如果使用Lock加锁,则可以避免这种情况。

尝试加锁案例

当一个线程加锁后,另一个线程不会一直等待,而会持续尝试5ms,如果一直失败再放弃加锁。

package com.geovis.bin.custom.study.wangbin.lock.contion.lock.lock;

/**
 * @Author: Wangb
 * @EMail: 1149984363@qq.com
 * @Date: 24/12/2021 下午6:35
 * @Description
 */


import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;


public class TestTryLock extends Thread {
    static Lock lock = new ReentrantLock();

    @Override
    public void run() {
        boolean isLocked = false;
        String currentThreadName = Thread.currentThread().getName();
        try {
            //在5毫秒的时间内,始终尝试加锁
            isLocked = lock.tryLock(5, TimeUnit.MILLISECONDS);
            System.out.println(currentThreadName + "-尝试加锁: " + isLocked);
            if (isLocked) {
                //如果尝试成功,则加锁
                Thread.sleep(20);
                System.out.println(currentThreadName + "-加锁成功");
            } else {
                System.out.println(currentThreadName + "-加锁失败");

            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (isLocked) {
                System.out.println(currentThreadName + "-解锁");
                lock.unlock();
            }
        }
    }

    public static void main(String[] args) {
        TestTryLock t1 = new TestTryLock();
        TestTryLock t2 = new TestTryLock();
        t1.setName("t1");
        t2.setName("t2");
        t1.start();
        t2.start();
    }
}
  • lock():立刻获取锁,如果锁已经被占用则等待。
  • tryLock()尝试获取锁,如果锁已经被占用则返回false,且不再等待;如果锁处于空闲,则立刻获取锁,并返回true;
  • tryLock(long time, TimeUnit unit):与tryLock()的区别是,该锁会在time时间内不断尝试。

程序运行结果如下:

中断等待案例

如果线程A使用Lock()方式加了锁,并且长时间独占使用,那么线程B就会因为长时间都无法获取锁而一直处于等待状态,但是这种等待的状态可能被其他线程中断。

package com.geovis.bin.custom.study.wangbin.lock.contion.lock.lock;

/**
 * @Author: Wangb
 * @EMail: 1149984363@qq.com
 * @Date: 24/12/2021 下午6:44
 * @Description
 */

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class TestInterruptibly {
    private Lock myLock = new ReentrantLock();

    public static void main(String[] args) throws InterruptedException {
        TestInterruptibly myInter = new TestInterruptibly();
        MyThread aThread = new MyThread(myInter);
        aThread.setName("A");
        MyThread bThread = new MyThread(myInter);
        bThread.setName("B");
        aThread.start();
        bThread.start();
        Thread.sleep(1000);
        /**
         * main线程休眠1秒,A、B线程各休眠3秒; 即当main结束休眠时,A、B仍然在休眠; 因此,main线程会中断B线程的等待
         */

        bThread.interrupt();
    }

    public void myInterrupt(Thread thread) throws InterruptedException {
        myLock.lockInterruptibly();
        try {
            System.out.println(thread.getName() + "-加锁");
            Thread.sleep(3000);
        } finally {
            System.out.println(thread.getName() + "-解锁");
            myLock.unlock();
        }
    }
}

class MyThread extends Thread {
    private TestInterruptibly myInter = null;

    public MyThread(TestInterruptibly myInter) {
        this.myInter = myInter;
    }

    @Override
    public void run() {
        try {
            myInter.myInterrupt(Thread.currentThread());
        } catch (Exception e) {
            System.out.println(Thread.currentThread().getName() + "被中断");
        }
    }
}

程序运行结果如下:

以上就是关于Lock()方法的内容和三个案例,希望对你有所帮助~

Bayanbulake

2021/12/24  阅读:37  主题:自定义主题1

作者介绍

Bayanbulake