【JUC】线程消息 sync JUC工具类

本文主要是写了生产者消费者模型,sync的应用层面如何实现类锁、对象锁;
基本的JUC工具类(计数器、循环任务、信号量);读写锁;阻塞队列(线程池里面就是这个)
技术:深,透,明,细

线程

生产者消费者模型——线程操作资源类:

1、高内聚低耦合,线程操作资源类
2、判断干活通知
3、判断使用while,出来以后还是重新判断。(防止多线程的虚假唤醒)

这个生产者消费者demo挺好玩的

class ProDuc{
    int num = 0;
    public synchronized void increment() throws InterruptedException {
        // 1. num现在为 1:那么进入{},当前线程 wait等待唤醒
        // 2. num现在为 0:那么就一直等待,直到盘子里面不为空
        //                跳过判断,开始干活num++
        while (num != 0) {
            this.wait();
        }
        num++;
        System.out.println(Thread.currentThread().getName()+"现在数量"+num);
        this.notifyAll();
    }
    public synchronized void decrement() throws InterruptedException {
        // 1. num现在为 1:那么就跳过判断,开始干活num--
        // 2. num现在为 0:那么进入{},当前线程 wait等待唤醒(另一个线程干活完毕会叫醒服务)
        while (num == 0) {
            this.wait();
        }
        num--;
        System.out.println(Thread.currentThread().getName()+"现在数量"+num);
        this.notifyAll();
    }
}


public class Product01 {
    public static void main(String[] args) {
        ProDuc proDuc = new ProDuc();

        new Thread(()->{
            for (int i = 0; i < 40; i++) {
                try {
                    proDuc.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"生产1").start();
        new Thread(()->{
            for (int i = 0; i < 40; i++) {
                proDuc.increment();
            }
        },"生产2").start();
        new Thread(()->{
            for (int i = 0; i < 80; i++) {
                proDuc.decrement();
            }
        },"消费者1").start();
    }
}

lock——判断 干活 通知

synchronized升级lock

在这里插入图片描述

资源类更新

class ProDuc {
    int num = 0;
    Lock reentrantLock = new ReentrantLock();
    Condition condition = reentrantLock.newCondition();

    public void increment() throws InterruptedException {
        reentrantLock.lock();
        try {
            while (num != 0) {
                condition.await();
            }
            num++;
            System.out.println(Thread.currentThread().getName() + "现在数量" + num);
            condition.signalAll();
        } finally {
            reentrantLock.unlock();
        }
    }

精确通知,顺序访问

    private int number = 1;
    //1:A 2:B 3:C
    private Lock lock = new ReentrantLock();
    private Condition c1 = lock.newCondition();
    private Condition c2 = lock.newCondition();
    private Condition c3 = lock.newCondition();

    public void print5(int totalLoopNumber) {
        lock.lock();
        try {
            //1 判断
            while (number != 1) {
                //A 就要停止
                c1.await();
            }
            //2 干活
            for (int i = 1; i <= 5; i++) {
                System.out.println(Thread.currentThread().getName() + "\t" + i + "\t totalLoopNumber: " + totalLoopNumber);
            }
            //3 通知
            number = 2;
            c2.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
	…… ……
}

8锁模型——synchronized锁详细解析:

static是类的意思,加上了就是类锁,是粒度更大的锁

在这里插入图片描述

在这里插入图片描述

NotSafe的集合API

ConcurrentModificationException:

在这里插入图片描述

故障:
ConcurrentModificationException

public static void main(String[] args) {
    List<String> list = new CopyOnWriteArrayList<>();

    for (int i = 0; i < 20; i++) {
        final String tmp = String.valueOf(i);
        new Thread(() -> {
            list.add(tmp);
            System.out.println(Thread.currentThread().getName() + "添加成功");
        }, tmp).start();
    }
}

原因:

解决方案:
在这里插入图片描述

优化:

在这里插入图片描述


ArrayList<>()  =>  Collections.synchronizedList(new ArrayList<>());  =>  CopyOnWriteList

HashSet<>();  =>  Collections.synchronizedSet();  =>  CopyOnWriteArraySet
value是object: PRESENT永久固定写死!!

Callable接口

面试题:callable接口与runnable接口的区别?
答:
(1)是否有返回值
(2)是否抛异常
(3)落地方法不一样,一个是run,一个是call

如果线程尚未完成,还没有生成return,则阻塞 get 方法。
在这里插入图片描述

在这里插入图片描述

public static void main(String[] args) throws ExecutionException, InterruptedException {
    //1、资源类,Runnable、Callable
    Source source = new Source();

    //2、直接写在futureTask里面
    FutureTask<String> futureTask2 = new FutureTask<>(()->{
        System.out.println(Thread.currentThread().getName());
        return "futureTask2可执行对象 的返回值";
    });
    FutureTask<String> futureTask = new FutureTask<>(()->{
        System.out.println(Thread.currentThread().getName()+"callable");
        return "futureTask GOGOGOGOGO";
    });

    new Thread(futureTask2,"小明").start();
    new Thread(futureTask,"小智").start();

    System.out.println(futureTask2.get());
    System.out.println(futureTask.get());
}

道格李的JUC工具类

1. CountDownLatch

例子:教室关门
一定要那么多人都走了(判断以上执行完毕),我才能继续往下面走

public static void main(String[] args) throws InterruptedException {
    CountDownLatch countDownLatch = new CountDownLatch(8);

    // 1、主线程分配好了很多线程去执行任务
    for (int i = 0; i < 100; i++){
        new Thread(() -> {
            countDownLatch.countDown();
            System.out.println("我确实减下来了1");
        }, Thread.currentThread().getName()+"_").start();
    }

    // 2、到这里阻塞一下,只需要确定现在的计数确实把栅栏里面的数字全部countDown了
    countDownLatch.await();
    
    // 3、判断确实把栅栏里面的东西运行完毕了,就会到下面继续执行
    System.out.println("结束");
}

2. CyclicBarrier

七龙珠例子:

public static void main(String[] args) throws InterruptedException {
    // 1. 没达到7之前,都会阻塞,没有机会出去,只能等待其他线程帮忙积攒
    // 2. 达到7,就是给开了一个窗口,有机会跳出去
    CyclicBarrier cyclicBarrier = new CyclicBarrier(7, () -> {
        System.out.println("达成目标,开始执行");
    });

    for (int i = 0; i < 35; i++) {
        final int tmp = i;
        new Thread(() -> {
            System.out.println(Thread.currentThread().getName()+"收集到第"+tmp+"个");
            try {
                cyclicBarrier.await();
            } catch (InterruptedException | BrokenBarrierException e) {
                e.printStackTrace();
            }
        }, String.valueOf(i)).start();
    }

    System.out.println("aaaa");
}

3. semaphore

争抢车位:

在信号量上我们定义两种操作:

  • acquire(获取) 当一个线程调用acquire操作时,它要么通过成功获取信号量(信号量减1),
  • 要么一直等下去,直到有线程释放信号量,或超时。
  • release(释放)实际上会将信号量的值加1,然后唤醒等待的线程。
  • 信号量主要用于两个目的,一个是用于多个共享资源的互斥使用,另一个用于并发线程数的控制。

信号灯 + 栅栏 的Demo:

public static void main(String[] args) throws InterruptedException {
    CountDownLatch countDownLatch = new CountDownLatch(10);
    Semaphore semaphore = new Semaphore(3);

    for (int i = 0; i < 10; i++) {
        new Thread(()->{
            try {
                semaphore.acquire();
                System.out.println("线程"+Thread.currentThread().getName()+"抢到了车位");
                try { TimeUnit.SECONDS.sleep(2); } catch(InterruptedException e) { e.printStackTrace(); }
                System.out.println("线程"+Thread.currentThread().getName()+"离开车位");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }finally {
                countDownLatch.countDown();
                semaphore.release();
            }
        },String.valueOf(i+1)).start();
    }

    countDownLatch.await();
    System.out.println("抢车位游戏结束……");
}

在这里插入图片描述

ReentrantReadWriteLock

可重入读写锁:
可读读,不可写写,不可写读

BlockingQueue

阻塞队列:就是一个临时的等待区

具体实现

在这里插入图片描述
在这里插入图片描述

ArrayBlockingQueue:

public class ArrayBlockingQueue<E> extends AbstractQueue<E>
        implements BlockingQueue<E>, java.io.Serializable {
    final ReentrantLock lock;
    final Object[] items;
    public ArrayBlockingQueue(int capacity, boolean fair,
                              Collection<? extends E> c) {   
初步了解:由数组结构组成的有界阻塞队列。必须给定数组的大小

LinkedBlockingQueue:

public class LinkedBlockingDeque<E> extends AbstractQueue<E> implements BlockingDeque<E>, java.io.Serializable {
    final ReentrantLock lock = new ReentrantLock();
    static final class Node<E> {
    	E item;
    	Node<E> prev;
    	Node<E> next;
		Node(E x) {item = x;}
	}
	// 不传参就给你搞一个无限大小的等待区,搞死你,CPU直接爆OOM,程序直接崩溃
    public LinkedBlockingDeque() {
        this(Integer.MAX_VALUE);
    }

Tips:

面试的时候会经常问到的点:
1、手写冒泡排序
2、消费者生产者模型
3、设计模式的单例模式

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

willorn

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值