JUC编程——中

一、Callable ( 简单 )

在这里插入图片描述

  1. 可以有返回值
  2. 可以抛出异常
  3. 方法不同,run()/ call()

代码测试

在这里插入图片描述

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

package com.kuang.callable;

import java.util.concurrent.Callable;

import java.util.concurrent.ExecutionException;

import java.util.concurrent.FutureTask;

import java.util.concurrent.locks.ReentrantLock;

/**

 * 1、探究原理
 * 2、觉自己会用
 */

public class CallableTest {

    public static void main(String[] args) throws ExecutionException, 

InterruptedException {

        // new Thread(new Runnable()).start();

        // new Thread(new FutureTask()).start();

        // new Thread(new FutureTask( Callable )).start();

        new Thread().start(); // 怎么启动Callable

        MyThread thread = new MyThread();

        FutureTask futureTask = new FutureTask(thread); // 适配类

        new Thread(futureTask,"A").start();

        new Thread(futureTask,"B").start(); // 结果会被缓存,效率高

        Integer o = (Integer) futureTask.get(); //这个get 方法可能会产生阻塞!把他放到

最后
        // 或者使用异步通信来处理!
        System.out.println(o);

    }

}

class MyThread implements Callable {

    @Override

    public Integer call() {

        System.out.println("call()"); // 会打印几个call
        // 耗时的操作
        return 1024;

    }

}

细节:
1、有缓存
2、结果可能需要等待,会阻塞!

二、常用的辅助类(必会)

CountDownLatch

在这里插入图片描述

package com.kuang.add;

import java.util.concurrent.CountDownLatch;

// 计数器
public class CountDownLatchDemo {

    public static void main(String[] args) throws InterruptedException {

        // 总数是6,必须要执行任务的时候,再使用!
        CountDownLatch countDownLatch = new CountDownLatch(6);

        for (int i = 1; i <=6 ; i++) {

            new Thread(()->{

                System.out.println(Thread.currentThread().getName()+" Go out");

                countDownLatch.countDown(); // 数量-1
            },String.valueOf(i)).start();

        }

        countDownLatch.await(); // 等待计数器归零,然后再向下执行

        System.out.println("Close Door");

    }

}

原理:

countDownLatch.countDown();// 数量-1
countDownLatch.await(); // 等待计数器归零,然后再向下执行
每次有线程调用 countDown() 数量-1,假设计数器变为0,countDownLatch.await() 就会被唤醒,继续 执行!

CyclicBarrier

在这里插入图片描述
加法计数器

package com.kuang.add;

import java.util.concurrent.BrokenBarrierException;

import java.util.concurrent.CyclicBarrier;

public class CyclicBarrierDemo {

    public static void main(String[] args) {

        /**

         * 集齐7颗龙珠召唤神龙
         */

        // 召唤龙珠的线程
        CyclicBarrier cyclicBarrier = new CyclicBarrier(7,()->{

            System.out.println("召唤神龙成功!");
        });

        for (int i = 1; i <=7 ; i++) {

            final int temp = i;

            // lambda能操作到 i 吗
            new Thread(()->{

                System.out.println(Thread.currentThread().getName()+"收
集"+temp+"个龙珠");
                try {

                    cyclicBarrier.await(); // 等待
                } catch (InterruptedException e) {

                    e.printStackTrace();

                } catch (BrokenBarrierException e) {

                    e.printStackTrace();

                }

            }).start();

        }

    }

}

Semaphore

Semaphore:信号量
在这里插入图片描述
抢车位!
6车—3个停车位置

package com.kuang.add;

import java.util.concurrent.Semaphore;

import java.util.concurrent.TimeUnit;

public class SemaphoreDemo {

    public static void main(String[] args) {

        // 线程数量:停车位! 限流!
        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(); // release() 释放
                }

            },String.valueOf(i)).start();

        }

    }

}

原理:
semaphore.acquire() 获得,假设如果已经满了,等待,等待被释放为止!
semaphore.release(); 释放,会将当前的信号量释放 + 1,然后唤醒等待的线程!
作用: 多个共享资源互斥的使用!并发限流,控制最大的线程数!

三、读写锁

在这里插入图片描述

package com.kuang.rw;

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) {

        MyCache myCache = new MyCache();

        // 写入
        for (int i = 1; i <= 5 ; i++) {

            final int temp = i;

            new Thread(()->{

                myCache.put(temp+"",temp+"");

            },String.valueOf(i)).start();

        }

        

        // 读取
        for (int i = 1; i <= 5 ; i++) {

            final int temp = i;

            new Thread(()->{

                myCache.get(temp+"");

            },String.valueOf(i)).start();

        }

    }

}

// 加锁的
class MyCacheLock{

    private volatile Map<String,Object> map = new HashMap<>();

    // 读写锁: 更加细粒度的控制
    private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();

    private Lock lock = new ReentrantLock();

    // 存,写入的时候,只希望同时只有一个线程写
    public void put(String key,Object value){

        readWriteLock.writeLock().lock();

        try {

            System.out.println(Thread.currentThread().getName()+"写入"+key);
            map.put(key,value);

            System.out.println(Thread.currentThread().getName()+"写入OK");
        } catch (Exception e) {

            e.printStackTrace();

        } finally {

            readWriteLock.writeLock().unlock();

        }

    }

    // 取,读,所有人都可以读!
    public void get(String key){

        readWriteLock.readLock().lock();

        try {

            System.out.println(Thread.currentThread().getName()+"读取"+key);
            Object o = map.get(key);

            System.out.println(Thread.currentThread().getName()+"读取OK");
        } catch (Exception e) {

            e.printStackTrace();

        } finally {

            readWriteLock.readLock().unlock();

        }

    }

}

/**

 * 自定义缓存
 */

class MyCache{

    private volatile Map<String,Object> map = new HashMap<>();

    // 存,写
    public void put(String key,Object value){

        System.out.println(Thread.currentThread().getName()+"写入"+key);
        map.put(key,value);

        System.out.println(Thread.currentThread().getName()+"写入OK");
    }

    // 取,读
    public void get(String key){

        System.out.println(Thread.currentThread().getName()+"读取"+key);
        Object o = map.get(key);

        System.out.println(Thread.currentThread().getName()+"读取OK");
    }

}

四、阻塞队列

在这里插入图片描述
阻塞队列:
在这里插入图片描述
在这里插入图片描述
BlockingQueue BlockingQueue 不是新的东西
在这里插入图片描述
四组API

方式抛出异常有返回值,不抛出异常阻塞 等待超时等待
添加addoffer()put()offer(timenum.timeUnit)
移除removepoll()take()poll(timenum,timeUnit)
检测队首元素elementpeak()--
/**
 * 抛出异常
 */
public static void test1(){
    //需要初始化队列的大小
    ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);

    System.out.println(blockingQueue.add("a"));
    System.out.println(blockingQueue.add("b"));
    System.out.println(blockingQueue.add("c"));
    //抛出异常:java.lang.IllegalStateException: Queue full
    //System.out.println(blockingQueue.add("d"));
    System.out.println(blockingQueue.remove());
    System.out.println(blockingQueue.remove());
    System.out.println(blockingQueue.remove());
    //如果多移除一个
    //这也会造成 java.util.NoSuchElementException 抛出异常
    System.out.println(blockingQueue.remove());
}
=======================================================================================
/**
 * 不抛出异常,有返回值
 */
public static void test2(){
    ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
    System.out.println(blockingQueue.offer("a"));
    System.out.println(blockingQueue.offer("b"));
    System.out.println(blockingQueue.offer("c"));
    //添加 一个不能添加的元素 使用offer只会返回false 不会抛出异常
    System.out.println(blockingQueue.offer("d"));

    System.out.println(blockingQueue.poll());
    System.out.println(blockingQueue.poll());
    System.out.println(blockingQueue.poll());
    //弹出 如果没有元素 只会返回null 不会抛出异常
    System.out.println(blockingQueue.poll());
}
=======================================================================================
/**
 * 等待 一直阻塞
 */
public static void test3() throws InterruptedException {
    ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);

    //一直阻塞 不会返回
    blockingQueue.put("a");
    blockingQueue.put("b");
    blockingQueue.put("c");

    //如果队列已经满了, 再进去一个元素  这种情况会一直等待这个队列 什么时候有了位置再进去,程序不会停止
	//blockingQueue.put("d");

    System.out.println(blockingQueue.take());
    System.out.println(blockingQueue.take());
    System.out.println(blockingQueue.take());
    //如果我们再来一个  这种情况也会等待,程序会一直运行 阻塞
    System.out.println(blockingQueue.take());
}
=======================================================================================
/**
 * 等待 超时阻塞
 *  这种情况也会等待队列有位置 或者有产品 但是会超时结束
 */
public static void test4() throws InterruptedException {
    ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
    blockingQueue.offer("a");
    blockingQueue.offer("b");
    blockingQueue.offer("c");
    System.out.println("开始等待");
    blockingQueue.offer("d",2, TimeUnit.SECONDS);  //超时时间2s 等待如果超过2s就结束等待
    System.out.println("结束等待");
    System.out.println("===========取值==================");
    System.out.println(blockingQueue.poll());
    System.out.println(blockingQueue.poll());
    System.out.println(blockingQueue.poll());
    System.out.println("开始等待");
    blockingQueue.poll(2,TimeUnit.SECONDS); //超过两秒 我们就不要等待了
    System.out.println("结束等待");
}

同步队列
同步队列 没有容量,也可以视为容量为1的队列;
进去一个元素,必须等待取出来之后,才能再往里面放入一个元素;
put方法 和 take方法;
Synchronized 和 其他的BlockingQueue 不一样 它不存储元素;
put了一个元素,就必须从里面先take出来,否则不能再put进去值!
并且SynchronousQueue 的take是使用了lock锁保证线程安全的。

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.TimeUnit;

public class SynchronousQueueDemo {
    public static void main(String[] args) {
        BlockingQueue<String> blockingQueue = new SynchronousQueue<>();

        new Thread(()->{
            try {
                System.out.println(Thread.currentThread().getName() + "put 1");
                blockingQueue.put("1");
                System.out.println(Thread.currentThread().getName() + "put 2");
                blockingQueue.put("2");
                System.out.println(Thread.currentThread().getName() + "put 3");
                blockingQueue.put("3");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "T1").start();

        new Thread(()->{
            try {
                TimeUnit.SECONDS.sleep(3);
                System.out.println(Thread.currentThread().getName() + "==>" + blockingQueue.take());
                TimeUnit.SECONDS.sleep(3);
                System.out.println(Thread.currentThread().getName() + "==>" + blockingQueue.take());
                TimeUnit.SECONDS.sleep(3);
                System.out.println(Thread.currentThread().getName() + "==>" + blockingQueue.take());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        },"T2").start();
    }
}

输出结果(顺序不一定是一样的):

T1put 1
T2==>1
T1put 2
T2==>2
T1put 3
T2==>3

五、线程池

线程池:三大方式、七大参数、四种拒绝策略
池化技术
程序的运行,本质:占用系统的资源!我们需要去优化资源的使用 ===> 池化技术
线程池、JDBC的连接池、内存池、对象池 等等。。。。
资源的创建、销毁十分消耗资源
池化技术:事先准备好一些资源,如果有人要用,就来我这里拿,用完之后还给我,以此来提高效率。

线程池的好处:

  1. 降低资源的消耗;
  2. 提高响应的速度;
  3. 方便管理;
    线程复用、可以控制最大并发数、管理线程;

线程池:三大方法

  • ExecutorService threadPool =
    Executors.newSingleThreadExecutor();//单个线程
  • ExecutorService threadPool2 = Executors.newFixedThreadPool(5);
    //创建一个固定的线程池的大小
  • ExecutorService threadPool3 = Executors.newCachedThreadPool(); //可伸缩的

在这里插入图片描述

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

//Executors 工具类、3大方法
public class Demo01 {
    public static void main(String[] args) {
        ExecutorService threadPool = Executors.newSingleThreadExecutor();//单个线程
        //ExecutorService threadPool2 = Executors.newFixedThreadPool(5);//创建一个固定的线程池的大小
        //ExecutorService threadPool3 = Executors.newCachedThreadPool()//可伸缩的,遇强则强,遇弱则弱

        try {
            for (int i = 0; i < 100; i++) {
                // 使用了线程池之后,使用线程池来创建线程
                threadPool.execute(()->{
                    System.out.println(Thread.currentThread().getName() + "ok");
                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 线程池用完,程序结束,关闭线程池
            threadPool.shutdown();
        }
    }
}

线程池:七大参数

源码分析

public ThreadPoolExecutor(int corePoolSize,  //核心线程池大小
                          int maximumPoolSize, //最大的线程池大小
                          long keepAliveTime,  //超时了没有人调用就会释放
                          TimeUnit unit, //超时单位
                          BlockingQueue<Runnable> workQueue, //阻塞队列
                          ThreadFactory threadFactory, //线程工厂 创建线程的 一般不用动
                          RejectedExecutionHandler handler //拒绝策略
                         ) {
    if (corePoolSize < 0 ||
        maximumPoolSize <= 0 ||
        maximumPoolSize < corePoolSize ||
        keepAliveTime < 0)
        throw new IllegalArgumentException();
    if (workQueue == null || threadFactory == null || handler == null)
        throw new NullPointerException();
    this.corePoolSize = corePoolSize;
    this.maximumPoolSize = maximumPoolSize;
    this.workQueue = workQueue;
    this.keepAliveTime = unit.toNanos(keepAliveTime);
    this.threadFactory = threadFactory;
    this.handler = handler;
}

银行排队例子
在这里插入图片描述

手动创建一个线程池

package com.kuang.pool;

import java.util.concurrent.*;

// Executors 工具类、3大方法

/**

 * new ThreadPoolExecutor.AbortPolicy() // 银行满了,还有人进来,不处理这个人的,抛出异

常
 * new ThreadPoolExecutor.CallerRunsPolicy() // 哪来的去哪里!
 * new ThreadPoolExecutor.DiscardPolicy() //队列满了,丢掉任务,不会抛出异常!
 * new ThreadPoolExecutor.DiscardOldestPolicy() //队列满了,尝试去和最早的竞争,也不会

抛出异常!
 */

public class Demo01 {

    public static void main(String[] args) {

        // 自定义线程池!工作 ThreadPoolExecutor
        ExecutorService threadPool = new ThreadPoolExecutor(

                2,

                5,

                3,

                TimeUnit.SECONDS,

                new LinkedBlockingDeque<>(3),

                Executors.defaultThreadFactory(),

                new ThreadPoolExecutor.DiscardOldestPolicy());  //队列满了,尝试去和

最早的竞争,也不会抛出异常!
        try {

            // 最大承载:Deque + max
            // 超过 RejectedExecutionException
            for (int i = 1; i <= 9; i++) {

                // 使用了线程池之后,使用线程池来创建线程
                threadPool.execute(()->{

                    System.out.println(Thread.currentThread().getName()+" ok");

                });

            }

        } catch (Exception e) {

            e.printStackTrace();

        } finally {

            // 线程池用完,程序结束,关闭线程池
            threadPool.shutdown();

        }

    }

}

线程池:四种拒绝策略

  1. new ThreadPoolExecutor.AbortPolicy():
    //该拒绝策略为:银行满了,还有人进来,不处理这个人的,并抛出异常。超出最大承载,就会抛出异常:队列容量大小+maxPoolSize
  2. new ThreadPoolExecutor.CallerRunsPolicy(): //该拒绝策略为:哪来的去哪里
    main线程进行处理
  3. new ThreadPoolExecutor.DiscardPolicy(): //该拒绝策略为:队列满了,丢掉异常,不会抛出异常。
  4. new ThreadPoolExecutor.DiscardOldestPolicy():
    //该拒绝策略为:队列满了,尝试去和最早的进程竞争,不会抛出异常

如何设置线程池的大小

1、CPU密集型:电脑的核数是几核就选择几;选择maximunPoolSize的大小

package com.kuang.pool;

import java.util.concurrent.*;

public class Demo01 {

    public static void main(String[] args) {

        // 自定义线程池!工作 ThreadPoolExecutor

        // 最大线程到底该如何定义
        // 1、CPU 密集型,几核,就是几,可以保持CPu的效率最高!
        // 2、IO  密集型   > 判断你程序中十分耗IO的线程,
        // 程序   15个大型任务  io十分占用资源!

        // 获取CPU的核数
        System.out.println(Runtime.getRuntime().availableProcessors());

        ExecutorService threadPool = new ThreadPoolExecutor(

                2,

                Runtime.getRuntime().availableProcessors(),

                3,

                TimeUnit.SECONDS,

                new LinkedBlockingDeque<>(3),

                Executors.defaultThreadFactory(),

                new ThreadPoolExecutor.DiscardOldestPolicy());  //队列满了,尝试去和

最早的竞争,也不会抛出异常!
        try {

            // 最大承载:Deque + max
            // 超过 RejectedExecutionException
            for (int i = 1; i <= 9; i++) {

                // 使用了线程池之后,使用线程池来创建线程
                threadPool.execute(()->{

                    System.out.println(Thread.currentThread().getName()+" ok");

                });

            }

        } catch (Exception e) {

            e.printStackTrace();

        } finally {

            // 线程池用完,程序结束,关闭线程池
            threadPool.shutdown();

        }

    }

}

2、I/O密集型:
在程序中有15个大型任务,io十分占用资源;I/O密集型就是判断我们程序中十分耗I/O的线程数量,大约是最大I/O数的一倍到两倍之间。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值