千家信息网

Java并发编程中Lock机制下API怎么用

发表于:2025-01-19 作者:千家信息网编辑
千家信息网最后更新 2025年01月19日,这篇文章主要介绍Java并发编程中Lock机制下API怎么用,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!一、Lock体系结构1、基础接口简介Lock加锁相关结构中涉及两个使用
千家信息网最后更新 2025年01月19日Java并发编程中Lock机制下API怎么用

这篇文章主要介绍Java并发编程中Lock机制下API怎么用,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!

一、Lock体系结构

1、基础接口简介

Lock加锁相关结构中涉及两个使用广泛的基础API:ReentrantLock类和Condition接口,基本关系如下:

Lock接口

Java并发编程中资源加锁的根接口之一,规定了资源锁使用的几个基础方法。

ReentrantLock类

实现Lock接口的可重入锁,即线程如果获得当前实例的锁,并进入任务方法,在线程没有释放锁的状态下,可以再次进入任务方法,特点:互斥排它性,即同一个时刻只有一个线程进入任务。

Condition接口

Condition接口描述可能会与锁有关联的条件变量,提供了更强大的功能,例如在线程的等待/通知机制上,Conditon可以实现多路通知和选择性通知。

2、使用案例

生产消费模式

写线程向容器中添加数据,读线程从容器获取数据,如果容器为空时,读线程等待。

public class LockAPI01 {    private static Lock lock = new ReentrantLock() ;    private static Condition condition1 = lock.newCondition() ;    private static Condition condition2 = lock.newCondition() ;    public static void main(String[] args) throws Exception {        List dataList = new ArrayList<>() ;        ReadList readList = new ReadList(dataList);        WriteList writeList = new WriteList(dataList);        new Thread(readList).start();        TimeUnit.SECONDS.sleep(2);        new Thread(writeList).start();    }    // 读数据线程    static class ReadList implements Runnable {        private List dataList ;        public ReadList (List dataList){            this.dataList = dataList ;        }        @Override        public void run() {            lock.lock();            try {                if (dataList.size() != 2){                    System.out.println("Read wait...");                    condition1.await();                }                System.out.println("ReadList WakeUp...");                for (String element:dataList){                    System.out.println("ReadList:"+element);                }                condition2.signalAll();            } catch (InterruptedException e){                e.fillInStackTrace() ;            } finally {                lock.unlock();            }        }    }    // 写数据线程    static class WriteList implements Runnable {        private List dataList ;        public WriteList (List dataList){            this.dataList = dataList ;        }        @Override        public void run() {            lock.lock();            try {                dataList.add("Java") ;                dataList.add("C++") ;                condition1.signalAll();                System.out.println("Write over...");                condition2.await();                System.out.println("Write WakeUp...");            } catch (InterruptedException e){                e.fillInStackTrace() ;            } finally {                lock.unlock();            }        }    }}

这个生产消费模式和生活中的点餐场景极为类似,用户下单,通知后厨烹饪,烹饪完成之后通知送餐。

顺序执行模式

既然线程执行可以互相通知,那也可以基于该机制实现线程的顺序执行,基本思路:在一个线程执行完毕后,基于条件唤醒下个线程。

public class LockAPI02 {    public static void main(String[] args) {        PrintInfo printInfo = new PrintInfo() ;        ExecutorService service =  Executors.newFixedThreadPool(3);        service.execute(new PrintA(printInfo));        service.execute(new PrintB(printInfo));        service.execute(new PrintC(printInfo));    }}class PrintA implements Runnable {    private PrintInfo printInfo ;    public PrintA (PrintInfo printInfo){        this.printInfo = printInfo ;    }    @Override    public void run() {        printInfo.printA ();    }}class PrintB implements Runnable {    private PrintInfo printInfo ;    public PrintB (PrintInfo printInfo){        this.printInfo = printInfo ;    }    @Override    public void run() {        printInfo.printB ();    }}class PrintC implements Runnable {    private PrintInfo printInfo ;    public PrintC (PrintInfo printInfo){        this.printInfo = printInfo ;    }    @Override    public void run() {        printInfo.printC ();    }}class PrintInfo {    // 控制下个执行的线程    private String info = "A";    private ReentrantLock lock = new ReentrantLock();    // 三个线程,三个控制条件    Condition conditionA = lock.newCondition();    Condition conditionB = lock.newCondition();    Condition conditionC = lock.newCondition();    public void printA (){        try {            lock.lock();            while (!info.equals("A")) {                conditionA.await();            }            System.out.print("A");            info = "B";            conditionB.signalAll();        } catch (InterruptedException e) {            e.printStackTrace();        } finally {            lock.unlock();        }    }    public void printB (){        try {            lock.lock();            while (!info.equals("B")) {                conditionB.await();            }            System.out.print("B");            info = "C";            conditionC.signalAll();        } catch (InterruptedException e) {            e.printStackTrace();        } finally {            lock.unlock();        }    }    public void printC (){        try {            lock.lock();            while (!info.equals("C")) {                conditionC.await();            }            System.out.print("C");            info = "A";            conditionA.signalAll();        } catch (InterruptedException e) {            e.printStackTrace();        } finally {            lock.unlock();        }    }}

该案例经常出现在多线程的面试题中,如何实现ABC的顺序打印问题,基本思路就是基于线程的等待通知机制,但是实现方式很多,上述只是其中一种方式。

二、读写锁机制

1、基础API简介

重入锁的排它特性决定了性能会产生瓶颈,为了提升性能问题,JDK中还有另一套读写锁机制。读写锁中维护一个共享读锁和一个排它写锁,在实际开发中,读的场景还是偏多的,所以读写锁可以很好的提高并发性。

读写锁相关结构中两个基础API:ReadWriteLock接口和ReentrantReadWriteLock实现类,基本关系如下:

ReadWriteLock

提供两个基础方法,readLock获取读机制锁,writeLock获取写机制锁。

ReentrantReadWriteLock

接口ReadWriteLock的具体实现,特点:基于读锁时,其他线程可以进行读操作,基于写锁时,其他线程读、写操作都禁止。

2、使用案例

读写分离模式

通过读写锁机制,分别向数据容器Map中写入数据和读取数据,以此验证读写锁机制。

public class LockAPI03 {    public static void main(String[] args) throws Exception {        DataMap dataMap = new DataMap() ;        Thread read = new Thread(new GetRun(dataMap)) ;        Thread write = new Thread(new PutRun(dataMap)) ;        write.start();        Thread.sleep(2000);        read.start();    }}class GetRun implements Runnable {    private DataMap dataMap ;    public GetRun (DataMap dataMap){        this.dataMap = dataMap ;    }    @Override    public void run() {        System.out.println("GetRun:"+dataMap.get("myKey"));    }}class PutRun implements Runnable {    private DataMap dataMap ;    public PutRun (DataMap dataMap){        this.dataMap = dataMap ;    }    @Override    public void run() {        dataMap.put("myKey","myValue");    }}class DataMap {    Map dataMap = new HashMap<>() ;    ReadWriteLock rwLock = new ReentrantReadWriteLock() ;    Lock readLock = rwLock.readLock() ;    Lock writeLock = rwLock.writeLock() ;    // 读取数据    public String get (String key){        readLock.lock();        try{            return dataMap.get(key) ;        } finally {            readLock.unlock();        }    }    // 写入数据    public void put (String key,String value){        writeLock.lock();        try{            dataMap.put(key,value) ;            System.out.println("执行写入结束...");            Thread.sleep(10000);        } catch (Exception e) {            System.out.println("Exception...");        } finally {            writeLock.unlock();        }    }}

说明:当put方法一直在睡眠状态时,因为写锁的排它性质,所以读方法是无法执行的。

三、基础工具类

LockSupport简介

LockSupprot定义一组公共静态方法,这些方法提供最基本的线程阻塞和唤醒功
能。

基础方法

park():当前线程阻塞,当前线程被中断或调用unpark方法,park()方法中返回;

park(Object blocker):功能同park(),传入Object对象,记录导致线程阻塞的阻塞对象,方便问题排查;

parkNanos(long nanos):指定时间nanos内阻塞当前线程,超时返回;

unpark(Thread thread):唤醒指定处于阻塞状态的线程;

代码案例

该流程在购物APP上非常常见,当你准备支付时放弃,会有一个支付失效,在支付失效期内可以随时回来支付,过期后需要重新选取支付商品。

public class LockAPI04 {    public static void main(String[] args) throws Exception {        OrderPay orderPay = new OrderPay("UnPaid") ;        Thread orderThread = new Thread(orderPay) ;        orderThread.start();        Thread.sleep(3000);        orderPay.changeState("Pay");        LockSupport.unpark(orderThread);    }}class OrderPay implements Runnable {    // 支付状态    private String orderState ;    public OrderPay (String orderState){        this.orderState = orderState ;    }    public synchronized void changeState (String orderState){        this.orderState = orderState ;    }    @Override    public void run() {        if (orderState.equals("UnPaid")){            System.out.println("订单待支付..."+orderState);            LockSupport.park(orderState);        }        System.out.println("orderState="+orderState);        System.out.println("订单准备发货...");    }}

这里基于LockSupport中park和unpark控制线程状态,实现的等待通知机制。

四、源代码地址

GitHub·地址https://github.com/cicadasmile/java-base-parentGitEE·地址https://gitee.com/cicadasmile/java-base-parent

以上是"Java并发编程中Lock机制下API怎么用"这篇文章的所有内容,感谢各位的阅读!希望分享的内容对大家有帮助,更多相关知识,欢迎关注行业资讯频道!

0