JUC快速学习笔记

JUC快速学习笔记

狂神说JUC 个人学习笔记

介绍

JUC是指javaUtil包中的三个操作线程的包!

并发操作

不加锁

  • 方法
    //属性,方法
    private int number = 50;
    //买票的方式
    public void norSale() {
        if (number>0) {
            System.out.println(Thread.currentThread().getName()+"卖出了"+(number--)+"票,剩余:"+number);
        }
    }
  • 测试
    @Test
    public void RunnableTest(){
        Ticket ticket = new Ticket();
        //Runnable接口为函数式接口
        new Thread(()->{
            for (int i = 0; i < 40; i++) {
                ticket.norSale();
            }
        },"a").start();
        new Thread(()->{
            for (int i = 0; i < 40; i++) {
                ticket.norSale();
            }
        },"b").start();
        new Thread(()->{
            for (int i = 0; i < 40; i++) {
                ticket.norSale();
            }
        },"c").start();
    }
  • 输出
# 输出混乱
b卖出了36票,剩余:35
b卖出了34票,剩余:33
b卖出了33票,剩余:32
b卖出了32票,剩余:31
b卖出了31票,剩余:30

###加synchronized

  • 方法
    //买票的方式
    public synchronized void safeSale() {
        if (number>0) {
            System.out.println(Thread.currentThread().getName()+"卖出了"+(number--)+"票,剩余:"+number);
        }
    }
  • 测试
    @Test
    public void RunnableTest(){
        Ticket ticket = new Ticket();
        //Runnable接口为函数式接口
        new Thread(()->{
            for (int i = 0; i < 40; i++) {
                ticket.safeSale();
            }
        },"a").start();
        new Thread(()->{
            for (int i = 0; i < 40; i++) {
                ticket.safeSale();
            }
        },"b").start();
        new Thread(()->{
            for (int i = 0; i < 40; i++) {
                ticket.safeSale();
            }
        },"c").start();
    }
  • 输出
# 输出有序
a卖出了50票,剩余:49
a卖出了49票,剩余:48
a卖出了48票,剩余:47
a卖出了47票,剩余:46
a卖出了46票,剩余:45

###Lock锁(接口)

默认非公平锁:十分不公平可以插队;
公平锁:先来后到。

  • 上锁
    Lock lock = new ReentrantLock();
    // Lock买票
    public synchronized void safeLockSale() {
    
    
        lock.lock();//加锁
        try {
    
    
            //业务代码
            if (number>0) {
    
    
                System.out.println(Thread.currentThread().getName()+"卖出了"+(number--)+"票,剩余:"+number);
            }
        } catch (Exception e) {
    
    
            e.printStackTrace();
        } finally {
    
    
            lock.unlock();//解锁
        }
    }
  • 测试和输出与加Synchronize基本一致

  • Sychronized和lock的区别

1.Sychronized 内置的java关键字,Lock锁是一个java类

2.Sychronized 无法判断获取锁的状态,Lock锁可以判断是否获取到了锁.

3.Sychronized 会自动释放锁lock必须手动释放锁,如果不释放锁,死锁

4.Sychronized 线程一(获得锁,阻塞),线程二(等待,傻傻的等),Lock锁就不一定会等待下去.

5.Sychronized 可重入锁,不可以中断,非公平;Lock,可重入锁,可以中断锁,非公平(可以自己设置)

6.Sychronized 适合锁少量的代码的同步问题,Lock适合锁大量的代码同步问题.

线程通信,生产者消费者问题

Sychronized版

Sychronized版主要使用wait实现线程阻塞/等待,通过使用notifyAll来唤醒其他线程

  • 实体类
@lombok.Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Data {
    
    
    private int num = 0;
    public synchronized void increment() throws InterruptedException {
    
    
        while (num != 0){
    
    
            //等待
            this.wait();
        }
        num++;
        System.out.println(Thread.currentThread().getName()+">="+num);
        //通知其他线程,我加一完毕了
        this.notifyAll();
    }

    public synchronized void decrement() throws InterruptedException {
    
    
        while (num == 0){
    
    
            this.wait();
        }
        num--;
        System.out.println(Thread.currentThread().getName()+">="+num);
        //通知其他线程,我减一完毕
        this.notifyAll();
    }
}

  • 测试
    @Test
    public void ProduceAndConsumerTest(){
    
    
        Data data = new Data();
        new Thread(()->{
    
    
            for (int i = 0; i < 10; i++) {
    
    
                try {
    
    
                    data.increment();
                } catch (InterruptedException e) {
    
    
                    e.printStackTrace();
                }
            }
        },"A").start();
        new Thread(()->{
    
    
            for (int i = 0; i < 10; i++) {
    
    
                try {
    
    
                    data.increment();
                } catch (InterruptedException e) {
    
    
                    e.printStackTrace();
                }
            }
        },"C").start();
        new Thread(()->{
    
    
            for (int i = 0; i < 10; i++) {
    
    
                try {
    
    
                    data.decrement();
                } catch (InterruptedException e) {
    
    
                    e.printStackTrace();
                }
            }
        },"B").start();

    }

JUC版

在juc版本中使用condition.await()进行等待,使用condition.signalAll()进行唤醒

  • 基本操作
@lombok.Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Data {
    
    
    private int num = 0;
    Lock lock = new ReentrantLock();
    Condition condition = lock.newCondition();

    public void increment() throws InterruptedException {
    
    
        lock.lock();
        try{
    
    
            while (num != 0){
    
    
                //等待
                condition.await();
            }
            num++;
            System.out.println(Thread.currentThread().getName()+">="+num);
            //通知其他线程,我加一完毕了
            condition.signalAll();
        }finally {
    
    
            lock.unlock();
        }

    }

    public synchronized void decrement() throws InterruptedException {
    
    
        lock.lock();
        try{
    
    
            while (num == 0){
    
    
                //等待
                condition.await();
            }
            num--;
            System.out.println(Thread.currentThread().getName()+">="+num);
            //通知其他线程,我减一完毕了
            condition.signalAll();
        }finally {
    
    
            lock.unlock();
        }
    }
    
    


}

  • condition实现精准通知唤醒
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * A->B->C
 */
public class C {
    
    
    public static void main(String[] args) {
    
    
        Data3 data3 = new Data3();
        new Thread(()->{
    
    
            for (int i = 0; i < 10; i++) {
    
    
                data3.printA();
            }
        },"A").start();
        new Thread(()->{
    
    
            for (int i = 0; i < 10; i++) {
    
    
                data3.printB();
            }
        },"B").start();
        new Thread(()->{
    
    
            for (int i = 0; i < 10; i++) {
    
    
                data3.printC();
            }
        },"C").start();
    }
}
class Data3{
    
    
    private Lock lock = new ReentrantLock();
    private Condition condition1 = lock.newCondition();
    private Condition condition2 = lock.newCondition();
    private Condition condition3 = lock.newCondition();
    private int num =1; //1A,2B,3C
    public void printA() {
    
    
        lock.lock();
        try {
    
    
            //业务,判断,执行,通知
            while (num != 1){
    
    
                condition1.await();
            }
            System.out.println("aaaaaaaaaaaa");
            //唤醒指定的人,B
            num =2;
            condition2.signal();
        } catch (Exception e) {
    
    
            e.printStackTrace();
        } finally {
    
    
            lock.unlock();
        }
    }
    public void printB() {
    
    
        lock.lock();
        try {
    
    
            while (num != 2){
    
    
                condition2.await();
            }
            System.out.println("bbbbbbbbbbbb");
            //唤醒指定的人,B
            num =3;
            condition3.signal();
        } catch (Exception e) {
    
    
            e.printStackTrace();
        } finally {
    
    
            lock.unlock();
        }
    }
    public void printC() {
    
    
        lock.lock();
        try {
    
    
            while (num != 3){
    
    
                condition3.await();
            }
            System.out.println("ccccccccccc");
            //唤醒指定的人,B
            num =1;
            condition1.signal();
        } catch (Exception e) {
    
    
            e.printStackTrace();
        } finally {
    
    
            lock.unlock();
        }
    }
}

常用辅助类

CountDownLatch

    public void CountDownLatchTest() throws Exception{
    
    
        //总数是6
        CountDownLatch countDownLatch = new CountDownLatch(6);
        for (int i = 0; i < 6; i++) {
    
    
            new Thread(()->{
    
    
                System.out.println(Thread.currentThread().getName()+"go out");
                countDownLatch.countDown();
            },String.valueOf(i)).start();
        }
        //等待计数器归零才会向下执行
        countDownLatch.await();
        System.out.println("end");
        countDownLatch.countDown();//-1
    }

CyclicBarrier

和CountDownLatch相反

  • 代码
    @Test
    public void CyclicBarrierTest() throws Exception{
    
    
        CyclicBarrier cyclicBarrier = new CyclicBarrier(7,()->{
    
    
            System.out.println("finish");
        });
        for (int i = 0; i <= 7; i++) {
    
    
            final int temp = i;
            //lambda不能直接拿到for循环中的i
            new Thread(()->{
    
    
                System.out.println(Thread.currentThread().getName()+"collect"+temp);
                try {
    
    
                    cyclicBarrier.await();
                } catch (InterruptedException e) {
    
    
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
    
    
                    e.printStackTrace();
                }
            }).start();
        }
    }
  • 输出
Thread-0collect0
Thread-1collect1
Thread-2collect2
Thread-3collect3
Thread-5collect5
Thread-4collect4
Thread-6collect6
finish
Thread-7collect7

Semaphore

可以理解为CountDownLatch和CyclicBarrier的结合体,规定一个容量,允许增加或者减少

  • 代码
        Semaphore semaphore = new Semaphore(3);
        for (int i = 1; i <= 6; i++) {
    
    
            new Thread(()->{
    
    
                //acquire()
                try {
    
    
                    semaphore.acquire();//获得,如果满了,会等待被释放为止
                    System.out.println(Thread.currentThread().getName()+"抢到");
                    TimeUnit.SECONDS.sleep(2);
                    System.out.println(Thread.currentThread().getName()+"离开");
                } catch (InterruptedException e) {
    
    
                    e.printStackTrace();
                } finally {
    
    
                    semaphore.release();//释放
                }
            },String.valueOf(i)).start();
        }
  • 输出
1抢到
4抢到
3抢到
1离开
4离开
3离开
2抢到
6抢到
5抢到
2离开
5离开
6离开

ReadWriteLock

读锁和写锁相互之间排斥:

独占锁(写锁) 一次只能被一个线程占有

共享锁(读锁) 可以同时被多个线程占有

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

//ReadWriteLock
public class ReadWriteLockDemo {
    
    
    //    自定义缓存
    public static void main(String[] args) {
    
    
        MyCatchLock myCatch = new MyCatchLock();
//写入
        for (int i = 0; i <= 5; i++) {
    
    
            final int temp = i;
            new Thread(()->{
    
    
                myCatch.put(temp+"",temp+"");
            },String.valueOf(i)).start();
        }
        //读取
        for (int i = 0; i <=5; i++) {
    
    
            final int temp = i;
            new Thread(()->{
    
    
                myCatch.get(temp+"");
            },String.valueOf(i)).start();
        }
    }
}
class MyCatch {
    
    
    private volatile Map<String,Object> map = new HashMap<>(0);
    //存
    public void put(String key,Object value) {
    
    
        System.out.println(Thread.currentThread().getName()+"写入"+value);
        map.put(key, value);
        System.out.println(Thread.currentThread().getName()+"写入成功");
    }
    //取
    public void get(String key) {
    
    
        System.out.println(Thread.currentThread().getName()+"读取"+key);
        Object o = map.get(key);
        System.out.println(Thread.currentThread().getName()+"读取成功");
    }
}
//加锁的
class MyCatchLock {
    
    
    private volatile Map<String,Object> map = new HashMap<>(0);
    //读写锁更加细粒度的控制
    private ReadWriteLock lock = new ReentrantReadWriteLock();
    private Lock lock1 = new ReentrantLock();

    //存,写的时候,只希望同时有一个线程写
    public void put(String key,Object value) {
    
    
        lock.writeLock().lock();
        try {
    
    
            System.out.println(Thread.currentThread().getName()+"写入"+value);
            map.put(key, value);
            System.out.println(Thread.currentThread().getName()+"写入成功");
        } catch (Exception e) {
    
    
            e.printStackTrace();
        } finally {
    
    
            lock.writeLock().unlock();
        }
    }
    //取,读,所有的人都可以读
    public void get(String key) {
    
    
        lock.readLock().lock();
        try {
    
    
            System.out.println(Thread.currentThread().getName()+"读取"+key);
            Object o = map.get(key);
            System.out.println(Thread.currentThread().getName()+"读取成功");
        } catch (Exception e) {
    
    
            e.printStackTrace();
        } finally {
    
    
            lock.readLock().unlock();
        }
    }
}


  • 输出
1写入1
1写入成功
0写入0
0写入成功
3写入3
3写入成功
4写入4
4写入成功
2写入2
2写入成功
5写入5
5写入成功
0读取0
0读取成功
5读取5
2读取2
2读取成功
4读取4
4读取成功
1读取1
1读取成功
3读取3
3读取成功
5读取成功


BlockingQueue

队列的基本操作

    @Test
    public void ArrayBlockingQueueTest () {
    
    
        //队列的大小
        ArrayBlockingQueue arrayBlockingQueue = new ArrayBlockingQueue<>(3);
        System.out.println(arrayBlockingQueue.add("a"));
        System.out.println(arrayBlockingQueue.add("b"));
        System.out.println(arrayBlockingQueue.add("c"));
        System.out.println(arrayBlockingQueue.element());//查看队首元素
        //ava.lang.IllegalStateException
//        System.out.println(arrayBlockingQueue.add("d"));
//        System.out.println("=============");
        //队列移除顺序
        System.out.println(arrayBlockingQueue.remove());
        System.out.println(arrayBlockingQueue.remove());
        System.out.println(arrayBlockingQueue.remove());
        //java.util.NoSuchElementException
        System.out.println(arrayBlockingQueue.remove());
    }

阻塞等待

    @Test
    public void test3 () throws InterruptedException {
    
    
        //队列的大小
        ArrayBlockingQueue arrayBlockingQueue = new ArrayBlockingQueue<>(3);
        arrayBlockingQueue.put("a");
        arrayBlockingQueue.put("b");
        arrayBlockingQueue.put("c");
//        arrayBlockingQueue.put("d");队列没有位置,一直阻塞
        System.out.println("=============");
        //队列移除顺序
        System.out.println(arrayBlockingQueue.take());
        System.out.println(arrayBlockingQueue.take());
        System.out.println(arrayBlockingQueue.take());
        System.out.println(arrayBlockingQueue.take());//没有这个元素,一直阻塞
    }

超时等待

    @Test
    public  void test4 () throws InterruptedException {
    
    
        //队列的大小
        ArrayBlockingQueue arrayBlockingQueue = new ArrayBlockingQueue<>(3);
        System.out.println(arrayBlockingQueue.offer("a"));
        System.out.println(arrayBlockingQueue.offer("b"));
        System.out.println(arrayBlockingQueue.offer("c"));
        //等待超过两秒退出
        arrayBlockingQueue.offer("d", 2,TimeUnit.SECONDS);
        System.out.println(arrayBlockingQueue.poll());
        System.out.println(arrayBlockingQueue.poll());
        //等待超过两秒九退出
        System.out.println(arrayBlockingQueue.poll(2,TimeUnit.SECONDS));
}

线程池

  • 线程池的好处

1.降低资源的消耗

2.提高响应速度

3.方便管理

线程可以复用,可以控制最大并发量,管理线程

线程池:三大方法,7大参数,4种拒绝策略

Executors创建线程池

    @Test
    public  void ExecutorsTest () throws InterruptedException {
    
    
            //Executors工具类,三大方法
            ExecutorService threadPool = Executors.newSingleThreadExecutor();//单个线程
            for (int i = 0; i < 10; i++) {
    
    
                //使用了线程池之后,使用线程池来创建线程
                threadPool.execute(()->{
    
    
                    System.out.println(Thread.currentThread().getName()+"ok");
                });
            }
            //线程池用完,程序结束,关闭线程池
            try {
    
    
                threadPool.shutdown();
            } catch (Exception e) {
    
    
                e.printStackTrace();
            } finally {
    
    
            }

    }

手动创建线程池

    public static void main(String[] args) {
    
    
        //Executors工具类,三大方法
//        ExecutorService threadPool = Executors.newSingleThreadExecutor();//单个线程
//        ExecutorService threadPool = Executors.newFixedThreadPool(5);//创建一个固定大小得线程池
//        ExecutorService threadPool = Executors.newCachedThreadPool();//可伸缩,线程数可变
        //自定义线程池,工作


        //七个参数
//    public ThreadPoolExecutor(int corePoolSize,//核心线程池大小
//        int maximumPoolSize,//最大核心线程大小
//        long keepAliveTime,//超时了没人用就会释放
//        TimeUnit unit,//超时单位
//        BlockingQueue<Runnable> workQueue,//阻塞队列
//        ThreadFactory threadFactory,//线程工厂,创建线程,一般不用动
//        RejectedExecutionHandler handler) {//拒绝策略

        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(2,
                5,
                3,
                TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(3),
                Executors.defaultThreadFactory(),
//                new ThreadPoolExecutor.AbortPolicy()//银行满了还有人进来,不处理这个人,抛出异常
//                new ThreadPoolExecutor.DiscardPolicy()//队列满了不会抛出异常,丢掉任务
//                new ThreadPoolExecutor.CallerRunsPolicy()//哪里来的去哪里
                new ThreadPoolExecutor.DiscardOldestPolicy()//队列满了,尝试去和最早得竞争,也不会抛出异常
        );
        //最大承载:队列+max值
        for (int i = 0; i < 12; i++) {
    
    
            //使用了线程池之后,使用线程池来创建线程
            threadPoolExecutor.execute(()->{
    
    
                System.out.println(Thread.currentThread().getName()+"ok");
            });
        }
        //线程池用完,程序结束,关闭线程池
        try {
    
    
            threadPoolExecutor.shutdown();
        } catch (Exception e) {
    
    
            e.printStackTrace();
        } finally {
    
    
        }
    }

输出结果

pool-1-thread-1ok
pool-1-thread-4ok
pool-1-thread-5ok
pool-1-thread-3ok
pool-1-thread-2ok
pool-1-thread-5ok
pool-1-thread-4ok
pool-1-thread-1ok

猜你喜欢

转载自blog.csdn.net/qq_50665031/article/details/125891887