JUC Day02
文章总结自B站狂神说JAVA
1.Callable
优点:可以有返回值,可以抛出异常。
代码测试:
/**
* @date 2020/7/27 8:04
* callable 测试
*/
public class CallableDemo01 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
// 启动callable
MyThread myThread = new MyThread();
// 适配类
FutureTask futureTask = new FutureTask(myThread);
// 启动线程
new Thread(futureTask,"A").start(); // 有缓存
// 获取callable的返回接口
Integer integer = (Integer) futureTask.get();//get方法可能会参送阻塞,一般放到最后,或者使用异步进行处理.
System.out.println(integer);
}
}
class MyThread implements Callable<Integer >{
@Override
public Integer call() throws Exception {
System.out.println("Hello Callable");
return 1024;
}
}
2. 常用的辅助类
2.1 CountDownLatch
类似于减法计数器,设定一个总数,每执行一个线程-1;当普通线程数量归零时执行指定的线程。
/**
* @date 2020/7/27 8:23
* 辅助类:
* 1. CountDownLatch 减法计数器
*/
public class CountDownLatchDemo01 {
public static void main(String[] args) {
// 设定线程的总数为 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();
}
try {
// 等待计数器归零再向下执行.
countDownLatch.await();
System.out.println("线程全部执行完毕");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
2.2 CyclicBarrier
类似与一个加法计数器,当线程增加到达指定的值时执行线程。
/**
* @date 2020/7/27 8:32
* 辅助类:
* 2. CyclicBarrier 加法计数器
*/
public class CyclicBarrierDemo01 {
public static void main(String[] args) {
// 创建CyclicBarrier
CyclicBarrier cyclicBarrier = new CyclicBarrier(7,()->{
System.out.println("线程达到指定的值,我开始运行嗷");
});
for (int i = 0; i <= 10; i++) {
new Thread(()->{
try {
System.out.println("第"+Thread.currentThread().getName());
// 当线程数量符合条件时执行.
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
},String.valueOf(i)).start();
}
}
}
2.3 Semaphore
类似于一个通行证,用来指定最多运行多少个线程, 每当一个线程运行完毕时,正在等待的线程再运行。一般用来限流,多个线程共享资源。
/**
* @date 2020/7/27 8:47
* 辅助类:
* 3. Semaphore 通行证
*/
public class SemaphoreDemo01 {
public static void main(String[] args) {
// 最多同时运行的线程数量
Semaphore semaphore = new Semaphore(3);
for (int i = 6; i > 0; i--) {
new Thread(()->{
try {
// 得到
semaphore.acquire();
System.out.println(Thread.currentThread().getName()+"`启动");
TimeUnit.SECONDS.sleep(2);
System.out.println(Thread.currentThread().getName()+"`dead");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
// 释放
semaphore.release();
}
},String.valueOf(i)).start();
}
}
}
3. 读写锁
如果写入的过程中其它的线程也执行写入,当前写入线程就会变慢,所以使用读写锁:读可以被多个线程同时读,写的时候只能由一个线程写。独占锁:只有一个线程能够使用;共享锁:多个线程共享锁。读锁是共享锁,写锁是独占锁。
/**
* @date 2020/7/27 8:57
* 读写锁
*/
public class ReadWriteLockDemo01 {
public static void main(String[] args) {
MyCache myCache = new MyCache();
// 写入
for (int i = 0; i < 5; i++) {
final int temp = i;
myCache.put(temp+"",temp+"");
new Thread(()->{},String.valueOf(i)).start();
}
// 读取
for (int i = 0; i < 5; i++) {
final int temp = i;
myCache.get(temp+"");
new Thread(()->{},String.valueOf(i)).start();
}
}
}
class MyCache{
private volatile Map<String ,Object> map = new HashMap<>();
// 创建读写锁对象:
private ReadWriteLock lock = new ReentrantReadWriteLock();
// 存->写
public void put(String key,Object value){
// 在写入的时候只能由一个线程去写
lock.writeLock().lock();
try {
System.out.println(Thread.currentThread().getName()+"------------写入------------------:"+key);
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();
}
}
}
4.阻塞队列
4.1BlockingQueue
- 抛出异常:当队列满了或者为空的时候再次执行方法会抛出异常
/**
* @date 2020/7/27 9:21
* BlockingQueue 抛出异常
*/
public class Demo01 {
public static void main(String[] args) {
test1();
}
public static void test1(){
// 参送为 队列的大小
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());
// java.lang.IllegalStateException: Queue full 队列满了
System.out.println(arrayBlockingQueue.add("D"));
System.out.println(arrayBlockingQueue.remove());
System.out.println(arrayBlockingQueue.remove());
System.out.println(arrayBlockingQueue.remove());
// java.lang.IllegalStateException: Queue full 队列为空
System.out.println(arrayBlockingQueue.remove());
}
}
- 有返回值不抛出异常:当队列满了再执行添加会返回false,队列为空再执行移除会返回null
/**
* @date 2020/7/27 9:21
* BlockingQueue 不抛出异常 有返回值
*/
public class Demo02 {
public static void main(String[] args) {
test2();
}
public static void test2(){
// 参送为 队列的大小
ArrayBlockingQueue arrayBlockingQueue = new ArrayBlockingQueue<>(3);
System.out.println(arrayBlockingQueue.offer("A"));
System.out.println(arrayBlockingQueue.offer("B"));
System.out.println(arrayBlockingQueue.offer("C"));
// 获取队列首位
System.out.println(arrayBlockingQueue.peek());
// 返回 false 不抛出异常
System.out.println(arrayBlockingQueue.offer("D"));
System.out.println(arrayBlockingQueue.poll());
System.out.println(arrayBlockingQueue.poll());
System.out.println(arrayBlockingQueue.poll());
// 队列为空返回 null
System.out.println(arrayBlockingQueue.poll());
}
}
- 一直等待处于阻塞:当队列满了,会一直等待直到能够进去为止;
/**
* @date 2020/7/27 9:21
* BlockingQueue 一直等待 阻塞
*/
public class Demo03 {
public static void main(String[] args) {
test3();
}
public static void test3() {
// 参送为 队列的大小
ArrayBlockingQueue arrayBlockingQueue = new ArrayBlockingQueue<>(3);
// 存----> 存不进去会一直阻塞,直到进去为止
try {
arrayBlockingQueue.put("a");
arrayBlockingQueue.put("b");
arrayBlockingQueue.put("c");
arrayBlockingQueue.put("d");
} catch (InterruptedException e) {
e.printStackTrace();
}
// 取--> 当取不到是也会一直阻塞,直到取到为止
try {
System.out.println(arrayBlockingQueue.take());
System.out.println(arrayBlockingQueue.take());
System.out.println(arrayBlockingQueue.take());
System.out.println(arrayBlockingQueue.take());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
- 超时退出等待:设定一个时间,等到超过时间就退出不再等待。
/**
* @date 2020/7/27 9:21
* BlockingQueue 一超退出等待
*/
public class Demo04 {
public static void main(String[] args) {
test4();
}
public static void test4() {
// 参送为 队列的大小
ArrayBlockingQueue arrayBlockingQueue = new ArrayBlockingQueue<>(3);
// 存
arrayBlockingQueue.offer("A");
arrayBlockingQueue.offer("B");
arrayBlockingQueue.offer("C");
try {
// 设置超时时间超过时间就直接退出
arrayBlockingQueue.offer("D",2, TimeUnit.SECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
}
arrayBlockingQueue.poll();
arrayBlockingQueue.poll();
arrayBlockingQueue.poll();
try {
// 设置超时时间超过时间就直接退出
arrayBlockingQueue.poll(2,TimeUnit.SECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
4.2 SynchornizedQueue
同步队列,没有容量。必须进去一个元素等待取出来再往里面放元素。
/**
* @date 2020/7/27 10:13
* 同步队列Synchronized,队列中同时最多存在一个元素,必须先取出来才能再次存.
*/
public class Demo01 {
public static void main(String[] args) {
BlockingQueue<String> synchronousQueue = new SynchronousQueue();
new Thread(() -> {
try {
System.out.println(Thread.currentThread().getName() + " -> put 1");
synchronousQueue.put("1");
System.out.println(Thread.currentThread().getName() + " -> put 2");
synchronousQueue.put("2");
System.out.println(Thread.currentThread().getName() + " -> put 3");
synchronousQueue.put("3");
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "T1").start();
new Thread(() -> {
try {
TimeUnit.SECONDS.sleep(3);
System.out.println(Thread.currentThread().getName() +synchronousQueue.take());
TimeUnit.SECONDS.sleep(3);
System.out.println(Thread.currentThread().getName() +synchronousQueue.take());
TimeUnit.SECONDS.sleep(3);
System.out.println(Thread.currentThread().getName() +synchronousQueue.take());
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "T1").start();
}
}
5. 线程池
事先奖准备好的资源放在一块,用就拿走,用完就换回来。
优势:
- 降低资源消耗,提高响应速度
- 方便管理
- 复用
5.1 三大方法
- newSingleThreadExecutor 单一线程池,只能由一个线程
/**
* @date 2020/7/27 10:35
* Executors:
* 1. newSingleThreadExecutor()
*/
public class Demo01 {
public static void main(String[] args) {
// 得到一个线程的线程池
ExecutorService service = Executors.newSingleThreadExecutor();
try {
// 使用线程池创建线程
for (int i = 0; i < 10; i++) {
service.execute(()->{
System.out.println(Thread.currentThread().getName()+"----------------------");
});
}
}catch (Exception e){
e.printStackTrace();
}finally {
// 程序结束关闭线程池
service.shutdown();
}
}
}
- newFixedThreadPool 固定线程池,指定固定的线程数量
/**
* @date 2020/7/27 10:35
* Executors
* 2. newFixedThreadPool
*/
public class Demo02 {
public static void main(String[] args) {
// 得到固定数量的线程池
ExecutorService service = Executors.newFixedThreadPool(5);
try {
// 使用线程池创建线程
for (int i = 0; i < 10; i++) {
service.execute(()->{
System.out.println(Thread.currentThread().getName()+"----------------------");
});
}
}catch (Exception e){
e.printStackTrace();
}finally {
// 程序结束关闭线程池
service.shutdown();
}
}
}
- newCachedThreadPool 根据情况来设定线程数量
/**
* @date 2020/7/27 10:35
* Executors
* 3. newCachedThreadPool
*/
public class Demo03 {
public static void main(String[] args) {
// 根据实际情况得到线程的数量
ExecutorService service = Executors.newCachedThreadPool();
try {
// 使用线程池创建线程
for (int i = 0; i < 10; i++) {
service.execute(()->{
System.out.println(Thread.currentThread().getName()+"----------------------");
});
}
}catch (Exception e){
e.printStackTrace();
}finally {
// 程序结束关闭线程池
service.shutdown();
}
}
}
5.2 七大参数
上述三种创建线程池的方法实际上都是调用的相同的方法ThreadPoolExecutor
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory
ThreadPoolExecutor ) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
threadFactory, defaultHandler);
}
参数
- corePoolSize 核心线程池大小
- maximumPoolSize 最大核心线程池大小
- keepAliveTime 超时释放
- unit 超时单位
- workQueue 阻塞队列
- threadFactory 线程工厂
- defaultHandler 拒绝策略
5.3 自定义线程池
阿里巴巴开发手册推荐使用底层的ThreadPoolExecutor
方法来创建线程。
/**
* @date 2020/7/27 14:01
* 自定义线程池
*/
public class Demo04 {
public static void main(String[] args) {
ExecutorService service = new ThreadPoolExecutor(
2,
Runtime.getRuntime().availableProcessors(), // 获取cpu核心数
3,
TimeUnit.SECONDS,
new LinkedBlockingDeque<>(3),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy());
try {
// 线程最大承受 = 最大线程数量 + 队列数量
for (int i = 0; i < 9; i++) {
service.execute(()->{
System.out.println(Thread.currentThread().getName()+"---->");
});
}
}catch (Exception e){
e.printStackTrace();
}finally {
service.shutdown();
}
}
}
5.4 四种拒绝策略
5.4.1 AbortPolicy
默认的拒绝策略,当线程数量超过最大的承受范围时会抛出异常。
java.util.concurrent.RejectedExecutionException: Task pool.Demo04$$Lambda$1/558638686@6d03e736 rejected from java.util.concurrent.ThreadPoolExecutor@568db2f2[Running, pool size = 5, active threads = 0, queued tasks = 0, completed tasks = 8]
at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2063)
at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:830)
at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1379)
at pool.Demo04.main(Demo04.java:21)
5.4.2 CallerRunsPolicy
当超过线程池的最大承受范围时,由进入时的线程执行。
pool-1-thread-2---->
pool-1-thread-3---->
pool-1-thread-1---->
pool-1-thread-3---->
main---->
pool-1-thread-2---->
pool-1-thread-1---->
pool-1-thread-4---->
pool-1-thread-5---->
5.4.3 DiscardPolicy
队列满了会丢掉任务但是不会抛出异常。
pool-1-thread-2---->
pool-1-thread-3---->
pool-1-thread-1---->
pool-1-thread-3---->
pool-1-thread-1---->
pool-1-thread-2---->
pool-1-thread-5---->
pool-1-thread-4---->
5.4.4 DiscardOldestPolicy
队列满了尝试去和第一个调用的线程竞争,也不会去抛出异常。
5.5 CPU密集型与IO密集型
在创建线程池时,需要指定最大核心线程池大小;指定方法分为IO密集型和CPU密集型。
- CPU密集型:几核就设定为几,获取CPU核心数:
Runtime.getRuntime().availableProcessors()
。 - IO密集型:因为IO消耗资源大,所以在设定核心线程池大小时要大于IO的的线程数量就行。
6. 函数式接口
函数式接口:只有一个方法的接口。
6.1 Function -> 函数型接口
有两个参数,输入值和返回值。
/**
* @date 2020/7/27 14:34
* 函数型接口 --- function
*/
public class Demo01 {
public static void main(String[] args) {
// 工具类,返回输入的值,有一个输入参数,一个输出参数。
Function function = new Function<String ,String >() {
@Override
public String apply(String s) {
return s;
}
};
// lambda 表达式简写函数型接口。
Function function1 = (str)->{return str;};
System.out.println(function1.apply("1"));
System.out.println(function.apply("12342"));
}
}
6.2 Predicate -> 断定型接口
有一个输入参数,返回值为布尔值。
/**
* @date 2020/7/27 14:42
* 断定型接口 --- Predicate
* 有一个输入参数,返回一个布尔值。
*/
public class Demo02 {
public static void main(String[] args) {
// 判断字符串是否为空
Predicate predicate = new Predicate<String>(){
@Override
public boolean test(String o) {
return o.isEmpty();
}
};
// lambda 简化断定型接口
Predicate<String > predicate1 = (str) ->{return str.isEmpty()};
System.out.println(predicate.test("1412"));
}
}
6.3 Consumer -> 消费型接口
没有返回值只有参数
/**
* @date 2020/7/27 14:51
* 消费型接口 -> Consumer
* 没有返回值,只有参数
*/
public class Demo03 {
public static void main(String[] args) {
Consumer<String > consumer = new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
};
// lambda 表达式 简写
Consumer<String > consumer1 = (s)->{ System.out.println(s); };
consumer1.accept("ss");
consumer.accept("sout");
}
}
6.4 Supplier -> 供给型接口
没有参数只有返回值。
/**
* @date 2020/7/27 14:51
* 供给型接口 -> Supplier
* 没有参数只有返回值。
*/
public class Demo04 {
public static void main(String[] args) {
Supplier<String > stringSupplier = new Supplier<String>() {
@Override
public String get() {
System.out.println("get");
return "fas";
}
};
// lambda 简写
Supplier<String > supplier = ()->{return "fasfasgas";};
supplier.get();
stringSupplier.get();
}
}