JUC并发编程与高性能内存队列disruptor实战

JUC并发实战

Synchonized与Lock

区别

  • Synchronized是Java的关键字,由JVM层面实现的,Lock是一个接口,有实现类,由JDK实现。
  • Synchronized无法获取锁的状态,Lock可以判断是否获取到了锁。
  • Synchronized自动释放锁,lock一般在finally中手动释放,如果不释放锁,会死锁。
  • Synchronized 线程1(获得锁,阻塞),线程2(等待,傻傻的等); lock锁不一定会等待下去(lock.tryLock())
  • Synchronized是可重入的,不可中断的,非公平锁。Lock, 可重入锁,可以判断锁,非公平锁。
  • Synchronized 适合锁少量的代码同步问题,Lock适合锁大量的同步代码。

代码示例

高铁票类synchronized实现TicketS.java,对于线程来说也属于资源类

package cn.itxs.synchronize;public class TicketS {    private int quantify = 20;    public synchronized void sale(){        if (quantify > 0) {            System.out.println("当前线程"+Thread.currentThread().getName() + "卖出了第" + quantify-- + "张高铁票,剩余票数量为" + quantify);        }    }}

高铁票类Lock实现TicketL.java

package cn.itxs.synchronize;import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock;​public class TicketL {    private int quantify = 20;    private Lock lock = new ReentrantLock();    public void sale(){        lock.lock();        try {            if (quantify > 0) {                System.out.println("当前线程"+Thread.currentThread().getName() + "卖出了第" + quantify-- + "张高铁票,剩余票数量为" + quantify);            }        }catch (Exception e) {            e.printStackTrace();        }finally {            lock.unlock();        }    }}

测试类,下面线程的使用采用lambda表达式写法,属于JDK8的特性之一

package cn.itxs.synchronize;​public class ThreadMain {    public static void main(String[] args) {        TicketS ticketS = new TicketS();        System.out.println("ticketS-----------");        new Thread(() -> {            for (int i = 1; i < 30; i++) {                ticketS.sale();            }        },"第一个线程").start();​        new Thread(() -> {            for (int i = 1; i < 30; i++) {                ticketS.sale();            }        },"第二个线程").start();​        new Thread(() -> {            for (int i = 1; i < 30; i++) {                ticketS.sale();            }        },"第三个线程").start();​        System.out.println("ticketL-----------");​        TicketL ticketL = new TicketL();        new Thread(() -> {            for (int i = 1; i < 30; i++) {                ticketL.sale();            }        },"第一个线程").start();​        new Thread(() -> {            for (int i = 1; i < 30; i++) {                ticketL.sale();            }        },"第二个线程").start();​        new Thread(() -> {            for (int i = 1; i < 30; i++) {                ticketL.sale();            }        },"第三个线程").start();    }}

虚假唤醒

概述

  • 虚假唤醒是指当一定的条件触发时会唤醒很多在阻塞态的线程,但只有部分的线程唤醒是有用的,其余线程的唤醒是多余的;比如说卖货,如果本来没有货物,突然进了一件货物,这时所有的顾客都被通知了,但是只能一个人买,所以对其他人都是做了无用的通知。

代码示例

计算类,提供加一减一的0和1结果,Counter.java

package cn.itxs.counter;​public class Counter {    private int count = 0;​    public synchronized void addCount() throws InterruptedException {        if (count > 0){            //线程开始等待            this.wait();        }        count++;        System.out.println("当前线程为" + Thread.currentThread().getName() + ",count=" + count);        //通知其他线程        this.notifyAll();    }​    public synchronized void subtractCount() throws InterruptedException {        if (count == 0){            //线程开始等待            this.wait();        }        count--;        System.out.println("当前线程为" + Thread.currentThread().getName() + ",count=" + count);        //通知其他线程        this.notifyAll();    }}

测试类

package cn.itxs.counter;​public class CounterMain {    public static void main(String[] args) {        Counter counter = new Counter();        new Thread(() -> {            for (int i = 1; i < 10; i++) {                try {                    counter.addCount();                } catch (InterruptedException e) {                    e.printStackTrace();                }            }        },"第一个线程").start();​        new Thread(() -> {            for (int i = 1; i < 10; i++) {                try {                    counter.subtractCount();                } catch (InterruptedException e) {                    e.printStackTrace();                }            }        },"第二个线程").start();​        new Thread(() -> {            for (int i = 1; i < 10; i++) {                try {                    counter.addCount();                } catch (InterruptedException e) {                    e.printStackTrace();                }            }        },"第三个线程").start();​        new Thread(() -> {            for (int i = 1; i < 10; i++) {                try {                    counter.subtractCount();                } catch (InterruptedException e) {                    e.printStackTrace();                }            }        },"第四个线程").start();    }}

image.png

从上面分析可以知道导致虚假唤醒的原因主要就是一个线程直接在if代码块中被唤醒了,这时它已经跳过了if判断。我们只需要将if判断改为while,这样线程就会被重复判断而不再会跳出判断代码块,从而不会产生虚假唤醒这种情况了。

image.png

Callable

Callable任务可拿到一个Future对象,表示异步计算的结果,它提供了检查是否计算完成的方法,以等待计算的完成,并检索计算的结果,通过Future对象可以了解任务执行情况,可以取消任务的执行,还可以获取执行结果。

  • Runnable和Callable的区别Callable规定的方法是call(),Runnable规定的接口是run();Callable的任务执行后可返回值,而Runnable的任务是不能有返回值的;call方法可以抛出异常,run方法不可以

实现Callable接口资源类MessageThread.java

package cn.itxs.collection;​import java.util.concurrent.Callable;import java.util.concurrent.TimeUnit;​public class MessageThread implements Callable<Integer> {    @Override    public Integer call() throws Exception {        System.out.println("hello callable!");        TimeUnit.SECONDS.sleep(3);        return 100;    }}

测试类

package cn.itxs.collection;​import java.util.concurrent.ExecutionException;import java.util.concurrent.FutureTask;​public class CallableMain {    public static void main(String[] args) throws ExecutionException, InterruptedException {        MessageThread messageThread = new MessageThread();        FutureTask futureTask = new FutureTask(messageThread);        new Thread(futureTask,"FutureTaskTest").start();        Integer res = (Integer)futureTask.get();        System.out.println(res);    }}

异步回调

package cn.itxs.asyncall;​import java.util.concurrent.CompletableFuture;import java.util.concurrent.ExecutionException;import java.util.concurrent.TimeUnit;​public class AsynCallMain {    public static void main(String[] args) throws ExecutionException, InterruptedException {        CompletableFuture<Void> completableFuture = CompletableFuture.runAsync(() -> {            try {                TimeUnit.SECONDS.sleep(3);            } catch (InterruptedException e) {                e.printStackTrace();            }            System.out.println(Thread.currentThread().getName() + ",async call void");        });        System.out.println("等待线程异步执行");        completableFuture.get();​        CompletableFuture<String> completableFutureR = CompletableFuture.supplyAsync(() -> {            try {                TimeUnit.SECONDS.sleep(3);            } catch (InterruptedException e) {                e.printStackTrace();            }            //int i = 1/0; //取消注释后下面e输出详细信息,返回值为bad            System.out.println(Thread.currentThread().getName() + ",async call return");            return "good";        });        System.out.println("等待线程异步执行");        System.out.println(completableFutureR.whenComplete((s, e) -> {            System.out.println("s=" + s + ",e=" + e);        }).exceptionally((e) -> {            System.out.println(e.getMessage());            return "bad";        }).get());    }}

image.png

Lock+Condition

代码示例

先将上一小节改造为Lock+Condition版本下CounterL.java

package cn.itxs.counter;import java.util.concurrent.locks.Condition;import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock;public class CounterL {    private int count = 0;    private Lock lock = new ReentrantLock();    private Condition condition = lock.newCondition();    public void addCount() throws InterruptedException {        lock.lock();        try {            while (count > 0){                //线程开始等待                condition.await();            }            count++;            System.out.println("当前线程为" + Thread.currentThread().getName() + ",count=" + count);            //通知其他线程            condition.signalAll();        }catch (Exception e){            e.printStackTrace();        }finally {            lock.unlock();        }    }    public void subtractCount() throws InterruptedException {        lock.lock();        try {            while (count == 0){                //线程开始等待                condition.await();            }            count--;            System.out.println("当前线程为" + Thread.currentThread().getName() + ",count=" + count);            //通知其他线程            condition.signalAll();        }catch (Exception e){            e.printStackTrace();        }finally {            lock.unlock();        }    }}

和上面的执行结果是一样,但Lock+Condition可以实现精准的唤醒

CounterA.java

package cn.itxs.counter;import java.util.concurrent.locks.Condition;import java.util.concurr
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值