导图社区 JUC详解
这是一篇关于JUC详解的思维导图,主要内容有1、JUC是什么2、Lock接口3、Java8之lambda表达式复习4、线程间通信5、线程间定制化调用通信等。
编辑于2022-07-16 10:40:44JUC详解
1、JUC是什么
java.util.concurrent在并发编程中使用的工具类

进程/线程回顾
1进程/线程是什么?
进程:进程是一个具有一定独立功能的程序关于某个数据集合的一次运行活动。它是操作系统动态执行的基本单元,在传统的操作系统中,进程既是基本的分配单元,也是基本的执行单元。 线程:通常在一个进程中可以包含若干个线程,当然一个进程中至少有一个线程,不然没有存在的意义。线程可以利用进程所拥有的资源,在引入线程的操作系统中,通常都是把进程作为分配资源的基本单位,而把线程作为独立运行和独立调度的基本单位,由于线程比进程更小,基本上不拥有系统资源,故对它的调度所付出的开销就会小得多,能更高效的提高系统多个程序间并发执行的程度。
2进程/线程例子?
使用QQ,查看进程一定有一个QQ.exe的进程,我可以用qq和A文字聊天,和B视频聊天,给C传文件,给D发一段语言,QQ支持录入信息的搜索。 大四的时候写论文,用word写论文,同时用QQ音乐放音乐,同时用QQ聊天,多个进程。 word如没有保存,停电关机,再通电后打开word可以恢复之前未保存的文档,word也会检查你的拼写,两个线程:容灾备份,语法检查
3线程状态?
Thread.State public enum State { /** * Thread state for a thread which has not yet started. */ NEW,(新建) /** * Thread state for a runnable thread. A thread in the runnable * state is executing in the Java virtual machine but it may * be waiting for other resources from the operating system * such as processor. */ RUNNABLE,(准备就绪) /** * Thread state for a thread blocked waiting for a monitor lock. * A thread in the blocked state is waiting for a monitor lock * to enter a synchronized block/method or * reenter a synchronized block/method after calling * {@link Object#wait() Object.wait}. */ BLOCKED,(阻塞) /** * Thread state for a waiting thread. * A thread is in the waiting state due to calling one of the * following methods: * * {@link Object#wait() Object.wait} with no timeout * {@link #join() Thread.join} with no timeout * {@link LockSupport#park() LockSupport.park} * * * A thread in the waiting state is waiting for another thread to * perform a particular action. * * For example, a thread that has called Object.wait() * on an object is waiting for another thread to call * Object.notify() or Object.notifyAll() on * that object. A thread that has called Thread.join() * is waiting for a specified thread to terminate. */ WAITING,(不见不散) /** * Thread state for a waiting thread with a specified waiting time. * A thread is in the timed waiting state due to calling one of * the following methods with a specified positive waiting time: * * {@link #sleep Thread.sleep} * {@link Object#wait(long) Object.wait} with timeout * {@link #join(long) Thread.join} with timeout * {@link LockSupport#parkNanos LockSupport.parkNanos} * {@link LockSupport#parkUntil LockSupport.parkUntil} * */ TIMED_WAITING,(过时不候) /** * Thread state for a terminated thread. * The thread has completed execution. */ TERMINATED;(终结) }
4 wait/sleep的区别?
wait/sleep 功能都是当前线程暂停,有什么区别? wait放开手去睡,放开手里的锁 sleep握紧手去睡,醒了手里还有锁
5什么是并发?什么是并行?
并发:同一时刻多个线程在访问同一个资源,多个线程对一个点 例子:小米9今天上午10点,限量抢购 春运抢票 电商秒杀... 并行:多项工作一起执行,之后再汇总 例子:泡方便面,电水壶烧水,一边撕调料倒入桶中
2、Lock接口
复习Synchronized
多线程编程模板上
1、线程 操作 资源类
2、高内聚低耦合
实现步骤
1、创建资源类
2、资源类里创建同步方法、同步代码块
例子卖票程序
SaleTicket.java
Lock
是什么
 Lock implementations provide more extensive locking operations than can be obtained using synchronized methods and statements. They allow more flexible structuring, may have quite different properties, and may support multiple associated Condition objects. 锁实现提供了比使用同步方法和语句可以获得的更广泛的锁操作。它们允许更灵活的结构,可能具有非常不同的属性,并且可能支持多个关联的条件对象。
Lock接口的实现 ReentrantLock可重入锁

如何使用
class X { private final ReentrantLock lock = new ReentrantLock(); // ... public void m() { lock.lock(); // block until condition holds try { // ... method body } finally { lock.unlock() } } } synchronized与Lock的区别 两者区别: 1.首先synchronized是java内置关键字,在jvm层面,Lock是个java类; 2.synchronized无法判断是否获取锁的状态,Lock可以判断是否获取到锁; 3.synchronized会自动释放锁(a 线程执行完同步代码会释放锁 ;b 线程执行过程中发生异常会释放锁),Lock需在finally中手工释放锁(unlock()方法释放锁),否则容易造成线程死锁; 4.用synchronized关键字的两个线程1和线程2,如果当前线程1获得锁,线程2线程等待。如果线程1阻塞,线程2则会一直等待下去,而Lock锁就不一定会等待下去,如果尝试获取不到锁,线程可以不用一直等待就结束了; 5.synchronized的锁可重入、不可中断、非公平,而Lock锁可重入、可判断、可公平(两者皆可) 6.Lock锁适合大量同步的代码的同步问题,synchronized锁适合代码少量的同步问题。
创建线程方式
继承Thread
publicclass SaleTicket extends Thread java是单继承,资源宝贵,要用接口方式
不能这样写
new Thread()
Thread t1 = new Thread(); t1.start();
不能这样写
第三种
  Thread(Runnable target, String name)
实现runnable方法
新建类实现runnable接口
classMyThreadimplementsRunnable//新建类实现runnable接口 new Thread(newMyThread,...) 这种方法会新增类,有更新更好的方法
匿名内部类
new Thread(new Runnable() { @Override publicvoid run() { } }, "your thread name").start(); 这种方法不需要创建新的类,可以new接口
lambda表达式
new Thread(() -> { }, "your thread name").start(); 这种方法代码更简洁精炼
代码
package com.atguigu.thread; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; class Ticket //实例例eld +method { private int number=30; /* //1同步 public synchronized void sale() {//2同步 synchronized(this) {} if(number > 0) { System.out.println(Thread.currentThread().getName()+"卖出"+(number--)+"\t 还剩number); } }*/ // Lock implementations provide more extensive locking operations // than can be obtained using synchronized methods and statements. private Lock lock = new ReentrantLock();//List list = new ArrayList() public void sale() { lock.lock(); try { if(number > 0) { System.out.println(Thread.currentThread().getName()+"卖出"+(number--)+"\t 还剩number); } } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } } /** * * @Description:卖票程序个售票出 0张票 @author xiale * 笔记:J里面如何 1 多线程编-上 1.1 线程 (资里源类 * 1.2 高内聚 / public class SaleTicket { public static void main(String[] args)//main所有程序 Ticket ticket = new Ticket(); //Thread(Runnable target, String name) Allocates a new Thread object. new Thread(() -> {for (int i = 1; i new Thread(() -> {for (int i = 1; i new Thread(() -> {for (int i = 1; i /* new Thread(new Runnable() { @Override public void run() { for (int i = 1; i { ticket.sale(); } } }, "AA").start(); new Thread(new Runnable() { @Override public void run() { for (int i = 1; i { ticket.sale(); } } }, "BB").start(); new Thread(new Runnable() { @Override public void run() { for (int i = 1; i { ticket.sale(); } } }, "CC").start(); */ } } //1 class MyThread implements Runnable //2 匿名内部类 //3 laExpress
3、Java8之lambda表达式复习
lambda表达式
什么是Lambda
Lambda 是一个匿名函数,我们可以把 Lambda 表达式理解为是一段可以传递的代码(将代码 像数据一样进行传递)。可以写出更简洁、更 灵活的代码。作为一种更紧凑的代码风格,使 Java的语言表达能力得到了提升。 Lambda 表达式在Java 语言中引入了一个新的语法元 素和操作符。这个操作符为 “->” , 该操作符被称 为 Lambda 操作符或剪头操作符。它将 Lambda 分为 两个部分: 左侧:指定了 Lambda 表达式需要的所有参数 右侧:指定了 Lambda 体,即 Lambda 表达式要执行 的功能
查看例子:LambdaDemo
要求
接口只有一个方法
lambda表达式,如果一个接口只有一个方法,我可以把方法名省略 Foo foo = () -> {System.out.println("****hello lambda");};
写法
拷贝小括号(),写死右箭头->,落地大括号{...}
函数式接口
lambda表达式,必须是函数式接口,必须只有一个方法 如果接口只有一个方法java默认它为函数式接口。 为了正确使用Lambda表达式,需要给接口加个注解:@FunctionalInterface 如有两个方法,立刻报错 Runnable接口为什么可以用lambda表达式? 
接口里是否能有实现方法?
default方法
接口里在java8后容许有接口的实现,default方法默认实现 default int div(int x,int y) { return x/y; } 接口里default方法可以有几个?
静态方法实现
静态方法实现:接口新增 public static int sub(int x,int y){ return x-y; } 可以有几个? 注意静态的叫类方法,能用foo去调吗?要改成Foo
代码
package com.atguigu.thread; @FunctionalInterface interface Foo{ // public void sayHello() ; // public void say886() ; publicint add(intx,inty); defaultint div(intx,inty) { returnx/y; } publicstaticint sub(intx,inty) { returnx-y; } } /** * * @Description: Lambda Express-----> 函数式编程 * 1 拷贝小括号(形参列表),写死右箭头 ->,落地大括号 {方法实现} * 2 有且只有一个public方法@FunctionalInterface注解增强定义 * 3 default方法默认实现 * 4 静态方法实现 */ publicclass LambdaDemo { publicstaticvoid main(String[] args) { // Foofoo = new Foo() { // @Override // public void sayHello() { // System.out.println("Hello!!"); // } // // @Override // public void say886() { // // TODO Auto-generated method stub // // } // }; // foo.sayHello(); // System.out.println("============"); // foo = ()->{System.out.println("Hello!! lambda !!");}; // foo.sayHello(); Foo foo = (x,y)->{ System.out.println("Hello!! lambda !!"); returnx+y; }; intresult = foo.add(3,5); System.out.println("******result="+result); System.out.println("******result div="+foo.div(10, 2)); System.out.println("******result sub="+Foo.sub(10, 2)); } }
4、线程间通信
面试题:两个线程打印
两个线程,一个线程打印1-52,另一个打印字母A-Z打印顺序为12A34B...5152Z, 要求用线程间通信
例子:NotifyWaitDemo
线程间通信:1、生产者+消费者2、通知等待唤醒机制
多线程编程模板中
1、判断
2、干活
3、通知
synchronized实现
代码
package com.atguigu.thread; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; class ShareDataOne//资源类 { private int number = 0;//初始值为零的一个变量 public synchronized void increment() throws InterruptedException { //1判断 if(number !=0 ) { this.wait(); } //2干活 ++number; System.out.println(Thread.currentThread().getName()+"\t"+number); //3通知 this.notifyAll(); } public synchronized void decrement() throws InterruptedException { // 1判断 if (number == 0) { this.wait(); } // 2干活 --number; System.out.println(Thread.currentThread().getName() + "\t" + number); // 3通知 this.notifyAll(); } } /** * * @Description: *现在两个线程, * 可以操作初始值为零的一个变量, * 实现一个线程对该变量加1,一个线程对该变量减1, * 交替,来10轮。 * @author xialei * * * 笔记:Java里面如何进行工程级别的多线程编写 * 1 多线程变成模板(套路)-----上 * 1.1 线程 操作 资源类 * 1.2 高内聚 低耦合 * 2 多线程变成模板(套路)-----下 * 2.1 判断 * 2.2 干活 * 2.3 通知 */ public class NotifyWaitDemoOne { public static void main(String[] args) { ShareDataOne sd = new ShareDataOne(); new Thread(() -> { for (int i = 1; i try { sd.increment(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }, "A").start(); new Thread(() -> { for (int i = 1; i try { sd.decrement(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }, "B").start(); } } /* * * * 2 多线程变成模板(套路)-----下 * 2.1 判断 * 2.2 干活 * 2.3 通知 * 3 防止虚假唤醒用while * * * */
换成4个线程
换成4个线程会导致错误,虚假唤醒 原因:在java多线程判断时,不能用if,程序出事出在了判断上面, 突然有一添加的线程进到if了,突然中断了交出控制权, 没有进行验证,而是直接走下去了,加了两次,甚至多次
解决办法
解决虚假唤醒:查看API,java.lang.Object  中断和虚假唤醒是可能产生的,所以要用loop循环,if只判断一次,while是只要唤醒就要拉回来再判断一次。if换成while
代码
package com.atguigu.thread; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import org.omg.IOP.Codec; class ShareData//资源类 { private int number = 0;//初始值为零的一个变量 public synchronized void increment() throws InterruptedException { //判断 while(number!=0) { this.wait(); } //干活 ++number; System.out.println(Thread.currentThread().getName()+" \t "+number); //通知 this.notifyAll();; } public synchronized void decrement() throws InterruptedException { //判断 while(number!=1) { this.wait(); } //干活 --number; System.out.println(Thread.currentThread().getName()+" \t "+number); //通知 this.notifyAll(); } } /** * * @Description: *现在两个线程, * 可以操作初始值为零的一个变量, * 实现一个线程对该变量加1,一个线程对该变量减1, * 交替,来10轮。 * @author xialei * * * 笔记:Java里面如何进行工程级别的多线程编写 * 1 多线程变成模板(套路)-----上 * 1.1 线程 操作 资源类 * 1.2 高内聚 低耦合 * 2 多线程变成模板(套路)-----下 * 2.1 判断 * 2.2 干活 * 2.3 通知 */ public class NotifyWaitDemo { public static void main(String[] args) { ShareData sd = new ShareData(); new Thread(() -> { for (int i = 1; i try { sd.increment(); } catch (InterruptedException e) { e.printStackTrace(); } } }, "A").start(); new Thread(() -> { for (int i = 1; i try { sd.decrement(); } catch (InterruptedException e) { e.printStackTrace(); } } }, "B").start(); new Thread(() -> { for (int i = 1; i try { sd.increment(); } catch (InterruptedException e) { e.printStackTrace(); } } }, "C").start(); new Thread(() -> { for (int i = 1; i try { sd.decrement(); } catch (InterruptedException e) { e.printStackTrace(); } } }, "D").start(); } } /* * * * 2 多线程变成模板(套路)-----下 * 2.1 判断 * 2.2 干活 * 2.3 通知 * 3 防止虚假唤醒用while * * * */
原理图

多线程编程模板下
1、注意多线程之间的虚假唤醒
java8新版实现
对标实现
 
Condition
Condition:查看API,java.util.concurrent  class BoundedBuffer { final Lock lock = new ReentrantLock(); final Condition notFull = lock.newCondition(); final Condition notEmpty = lock.newCondition(); final Object[] items = new Object[100]; int putptr, takeptr, count; public void put(Object x) throws InterruptedException { lock.lock(); try { while (count == items.length) notFull.await(); items[putptr] = x; if (++putptr == items.length) putptr = 0; ++count; notEmpty.signal(); } finally { lock.unlock(); } }
代码
package com.atguigu.thread; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import org.omg.IOP.Codec; class ShareData//资源类 { private int number = 0;//初始值为零的一个变量 private Lock lock = new ReentrantLock(); private Condition condition = lock.newCondition(); public void increment() throws InterruptedException { lock.lock(); try { //判断 while(number!=0) { condition.await(); } //干活 ++number; System.out.println(Thread.currentThread().getName()+" \t "+number); //通知 condition.signalAll(); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } public void decrement() throws InterruptedException { lock.lock(); try { //判断 while(number!=1) { condition.await(); } //干活 --number; System.out.println(Thread.currentThread().getName()+" \t "+number); //通知 condition.signalAll(); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } /*public synchronized void increment() throws InterruptedException { //判断 while(number!=0) { this.wait(); } //干活 ++number; System.out.println(Thread.currentThread().getName()+" \t "+number); //通知 this.notifyAll();; } public synchronized void decrement() throws InterruptedException { //判断 while(number!=1) { this.wait(); } //干活 --number; System.out.println(Thread.currentThread().getName()+" \t "+number); //通知 this.notifyAll(); }*/ } /** * * @Description: *现在两个线程, * 可以操作初始值为零的一个变量, * 实现一个线程对该变量加1,一个线程对该变量减1, * 交替,来10轮。 * * * 笔记:Java里面如何进行工程级别的多线程编写 * 1 多线程变成模板(套路)-----上 * 1.1 线程 操作 资源类 * 1.2 高内聚 低耦合 * 2 多线程变成模板(套路)-----下 * 2.1 判断 * 2.2 干活 * 2.3 通知 */ public class NotifyWaitDemo { public static void main(String[] args) { ShareData sd = new ShareData(); new Thread(() -> { for (int i = 1; i try { sd.increment(); } catch (InterruptedException e) { e.printStackTrace(); } } }, "A").start(); new Thread(() -> { for (int i = 1; i try { sd.decrement(); } catch (InterruptedException e) { e.printStackTrace(); } } }, "B").start(); new Thread(() -> { for (int i = 1; i try { sd.increment(); } catch (InterruptedException e) { e.printStackTrace(); } } }, "C").start(); new Thread(() -> { for (int i = 1; i try { sd.decrement(); } catch (InterruptedException e) { e.printStackTrace(); } } }, "D").start(); } } /* * * * 2 多线程变成模板(套路)-----下 * 2.1 判断 * 2.2 干活 * 2.3 通知 * 3 防止虚假唤醒用while * * * */
5、线程间定制化调用通信
例子:ThreadOrderAccess
线程-调用-资源类
判断-干活-通知
1、有顺序通知,需要有标识位 2、有一个锁Lock,3把钥匙Condition 3、判断标志位 4、输出线程名+第几次+第几轮 5、修改标志位,通知下一个
代码
package com.atguigu.thread; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; class ShareResource { 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 { System.out.println(Thread.currentThread().getName()+"\t"+i+"\t totalLoopNumber: "+totalLoopNumber); } //3 通知 number = 2; c2.signal(); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } public void print10(int totalLoopNumber) { lock.lock(); try { //1 判断 while(number != 2) { //A 就要停止 c2.await(); } //2 干活 for (int i = 1; i { System.out.println(Thread.currentThread().getName()+"\t"+i+"\t totalLoopNumber: "+totalLoopNumber); } //3 通知 number = 3; c3.signal(); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } public void print15(int totalLoopNumber) { lock.lock(); try { //1 判断 while(number != 3) { //A 就要停止 c3.await(); } //2 干活 for (int i = 1; i { System.out.println(Thread.currentThread().getName()+"\t"+i+"\t totalLoopNumber: "+totalLoopNumber); } //3 通知 number = 1; c1.signal(); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } } /** * * @Description: * 多线程之间按顺序调用,实现A->B->C * 三个线程启动,要求如下: * * AA打印5次,BB打印10次,CC打印15次 * 接着 * AA打印5次,BB打印10次,CC打印15次 * ......来10轮 * */ public class ThreadOrderAccess { public static void main(String[] args) { ShareResource sr = new ShareResource(); new Thread(() -> { for (int i = 1; i { sr.print5(i); } }, "AA").start(); new Thread(() -> { for (int i = 1; i { sr.print10(i); } }, "BB").start(); new Thread(() -> { for (int i = 1; i { sr.print15(i); } }, "CC").start(); } }
6、多线程锁
例子:Lock_8
锁的8个问题
1 标准访问,先打印短信还是邮件 2 停4秒在短信方法内,先打印短信还是邮件 3 普通的hello方法,是先打短信还是hello 4 现在有两部手机,先打印短信还是邮件 5 两个静态同步方法,1部手机,先打印短信还是邮件 6 两个静态同步方法,2部手机,先打印短信还是邮件 7 1个静态同步方法,1个普通同步方法,1部手机,先打印短信还是邮件 8 1个静态同步方法,1个普通同步方法,2部手机,先打印短信还是邮件 运行答案: 1、短信 2、短信 3、Hello 4、邮件 5、短信 6、短信 7、邮件 8、邮件
8锁分析
A 一个对象里面如果有多个synchronized方法,某一个时刻内,只要一个线程去调用其中的一个synchronized方法了, 其它的线程都只能等待,换句话说,某一个时刻内,只能有唯一一个线程去访问这些synchronized方法 锁的是当前对象this,被锁定后,其它的线程都不能进入到当前对象的其它的synchronized方法 加个普通方法后发现和同步锁无关 换成两个对象后,不是同一把锁了,情况立刻变化。 synchronized实现同步的基础:Java中的每一个对象都可以作为锁。 具体表现为以下3种形式。 对于普通同步方法,锁是当前实例对象。 对于静态同步方法,锁是当前类的Class对象。 对于同步方法块,锁是Synchonized括号里配置的对象 当一个线程试图访问同步代码块时,它首先必须得到锁,退出或抛出异常时必须释放锁。 也就是说如果一个实例对象的非静态同步方法获取锁后,该实例对象的其他非静态同步方法必须等待获取锁的方法释放锁后才能获取锁, 可是别的实例对象的非静态同步方法因为跟该实例对象的非静态同步方法用的是不同的锁, 所以毋须等待该实例对象已获取锁的非静态同步方法释放锁就可以获取他们自己的锁。 所有的静态同步方法用的也是同一把锁——类对象本身, 这两把锁是两个不同的对象,所以静态同步方法与非静态同步方法之间是不会有竞态条件的。 但是一旦一个静态同步方法获取锁后,其他的静态同步方法都必须等待该方法释放锁后才能获取锁, 而不管是同一个实例对象的静态同步方法之间, 还是不同的实例对象的静态同步方法之间,只要它们同一个类的实例对象!
代码
package com.atguigu.thread; importjava.util.concurrent.TimeUnit; class Phone { publicsynchronizedvoid sendSMS() throws Exception { System.out.println("------sendSMS"); } publicsynchronizedvoid sendEmail() throws Exception { System.out.println("------sendEmail"); } publicvoid getHello() { System.out.println("------getHello"); } } /** * * @Description: 8锁 * 1 标准访问,先打印短信还是邮件 2 停4秒在短信方法内,先打印短信还是邮件 3 新增普通的hello方法,是先打短信还是hello 4 现在有两部手机,先打印短信还是邮件 5 两个静态同步方法,1部手机,先打印短信还是邮件 6 两个静态同步方法,2部手机,先打印短信还是邮件 7 1个静态同步方法,1个普通同步方法,1部手机,先打印短信还是邮件 8 1个静态同步方法,1个普通同步方法,2部手机,先打印短信还是邮件 * --------------------------------- * */ publicclass Lock_8 { publicstaticvoid main(String[] args) throws Exception { Phone phone = new Phone(); Phone phone2 = new Phone(); new Thread(() -> { try { phone.sendSMS(); } catch (Exception e) { e.printStackTrace(); } }, "AA").start(); Thread.sleep(100); new Thread(() -> { try { phone.sendEmail(); //phone.getHello(); //phone2.sendEmail(); } catch (Exception e) { e.printStackTrace(); } }, "BB").start(); } }
7、NotSafeDemo
需求
请举例说明集合类是不安全的
证明集合不安全
例子:NotSafeDemo
线程不安全错误
java.util.ConcurrentModificationException ArrayList在迭代的时候如果同时对其进行修改就会 抛出java.util.ConcurrentModificationException异常 并发修改异常
原理
List list = new ArrayList(); for (int i = 0; i 30 ; i++) { new Thread(()->{ list.add(UUID.randomUUID().toString().substring(0,8)); System.out.println(list); },String.valueOf(i)).start(); } 看ArrayList的源码 public boolean add(E e) { ensureCapacityInternal(size + 1); // Increments modCount!! elementData[size++] = e; return true; } 没有synchronized线程不安全
解决方案
Vector
List list = new Vector();  看Vector的源码 public synchronized boolean add(E e) { modCount++; ensureCapacityHelper(elementCount + 1); elementData[elementCount++] = e; return true; } 有synchronized线程安全
Collections
List list = Collections.synchronizedList(new ArrayList()); Collections提供了方法synchronizedList保证list是同步线程安全的 那HashMap,HashSet是线程安全的吗?也不是 所以有同样的线程安全方法 
写时复制
 List list = new CopyOnWriteArrayList();
写时复制
不加锁性能提升出错误,加锁数据一致性能下降
CopyOnWriteArrayList定义
A thread-safe variant of ArrayList in which all mutative operations (add, set, and so on) are implemented by making a fresh copy of the underlying array. CopyOnWriteArrayList是arraylist的一种线程安全变体, 其中所有可变操作(add、set等)都是通过生成底层数组的新副本来实现的。
举例:名单签到

CopyOnWrite理论
/** * Appends the specified element to the end of this list. * * @param e element to be appended to this list * @return {@code true} (as specified by {@link Collection#add}) */ public boolean add(E e) { final ReentrantLock lock = this.lock; lock.lock(); try { Object[] elements = getArray(); int len = elements.length; Object[] newElements = Arrays.copyOf(elements, len + 1); newElements[len] = e; setArray(newElements); return true; } finally { lock.unlock(); } } CopyOnWrite容器即写时复制的容器。往一个容器添加元素的时候,不直接往当前容器Object[]添加, 而是先将当前容器Object[]进行Copy,复制出一个新的容器Object[] newElements,然后向新的容器Object[] newElements里添加元素。 添加元素后,再将原容器的引用指向新的容器setArray(newElements)。 这样做的好处是可以对CopyOnWrite容器进行并发的读,而不需要加锁,因为当前容器不会添加任何元素。 所以CopyOnWrite容器也是一种读写分离的思想,读和写不同的容器。
扩展类比
HashSet
Set set = new HashSet();//线程不安全 Set set = new CopyOnWriteArraySet();//线程安全 HashSet底层数据结构是什么? HashMap ? 但HashSet的add是放一个值,而HashMap是放K、V键值对 public HashSet() { map = new HashMap(); } private static final Object PRESENT = new Object(); public boolean add(E e) { return map.put(e, PRESENT)==null; }
HashMap
Map map = new HashMap();//线程不安全 Map map = new ConcurrentHashMap();//线程安全
代码
package com.atguigu.gmall.jucdemo; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArraySet; /** * 请举例说明集合类是不安全的 */ public class NotSafeDemo { public static void main(String[] args) { Map map = new ConcurrentHashMap(); for (int i = 0; i 30 ; i++) { new Thread(()->{ map.put(Thread.currentThread().getName(),UUID.randomUUID().toString().substring(0,8)); System.out.println(map); },String.valueOf(i)).start(); } } private static void setNoSafe() { Set set = new CopyOnWriteArraySet(); for (int i = 0; i 30 ; i++) { new Thread(()->{ set.add(UUID.randomUUID().toString().substring(0,8)); System.out.println(set); },String.valueOf(i)).start(); } } private static void listNoSafe() { // List list = Arrays.asList("a","b","c"); // list.forEach(System.out::println); //写时复制 List list = new CopyOnWriteArrayList(); // new CopyOnWriteArrayList(); //Collections.synchronizedList(new ArrayList()); //new Vector();//new ArrayList(); for (int i = 0; i 30 ; i++) { new Thread(()->{ list.add(UUID.randomUUID().toString().substring(0,8)); System.out.println(list); },String.valueOf(i)).start(); } } } /** * 写时复制 CopyOnWrite容器即写时复制的容器。往一个容器添加元素的时候,不直接往当前容器Object[]添加, 而是先将当前容器Object[]进行Copy,复制出一个新的容器Object[] newElements,然后向新的容器Object[] newElements里添加元素。 添加元素后,再将原容器的引用指向新的容器setArray(newElements)。 这样做的好处是可以对CopyOnWrite容器进行并发的读,而不需要加锁,因为当前容器不会添加任何元素。 所以CopyOnWrite容器也是一种读写分离的思想,读和写不同的容器。 * * * * public boolean add(E e) { final ReentrantLock lock = this.lock; lock.lock(); try { Object[] elements = getArray(); int len = elements.length; Object[] newElements = Arrays.copyOf(elements, len + 1); newElements[len] = e; setArray(newElements); return true; } finally { lock.unlock(); } } */
8、Callable接口
是什么
面试题:获得多线程的方法几种?
(1)继承thread类(2)runnable接口 如果只回答这两个你连被问到juc的机会都没有 正确答案如下: 传统的是继承thread类和实现runnable接口, java5以后又有实现callable接口和java的线程池获得
函数式接口
 这是一个函数式接口,因此可以用作lambda表达式或方法引用的赋值对象。
与runnable对比
实现方法对比
创建新类MyThread实现runnable接口 class MyThread implements Runnable{ @Override public void run() { } } 新类MyThread2实现callable接口 class MyThread2 implements Callable{ @Override public Integer call() throws Exception { return 200; } } 面试题:callable接口与runnable接口的区别? 答:(1)是否有返回值 (2)是否抛异常 (3)落地方法不一样,一个是run,一个是call
怎么用
直接替换runnable是否可行?
 不可行,因为:thread类的构造方法根本没有Callable 
认识不同的人找中间人
这像认识一个不认识的同学,我可以找中间人介绍。 中间人是什么?java多态,一个类可以实现多个接口!!  FutureTask ft = new FutureTask(new MyThread()); new Thread(ft, "AA").start(); 运行成功后如何获得返回值?  ft.get();
FutureTask
是什么
未来的任务,用它就干一件事,异步调用 main方法就像一个冰糖葫芦,一个个方法由main串起来。 但解决不了一个问题:正常调用挂起堵塞问题  例子: (1)老师上着课,口渴了,去买水不合适,讲课线程继续,我可以单起个线程找班长帮忙买水, 水买回来了放桌上,我需要的时候再去get。 (2)4个同学,A算1+20,B算21+30,C算31*到40,D算41+50,是不是C的计算量有点大啊, FutureTask单起个线程给C计算,我先汇总ABD,最后等C计算完了再汇总C,拿到最终结果 (3)高考:会做的先做,不会的放在后面做
原理
在主线程中需要执行比较耗时的操作时,但又不想阻塞主线程时,可以把这些作业交给Future对象在后台完成, 当主线程将来需要时,就可以通过Future对象获得后台作业的计算结果或者执行状态。 一般FutureTask多用于耗时的计算,主线程可以在完成自己的任务后,再去获取结果。 仅在计算完成时才能检索结果;如果计算尚未完成,则阻塞 get 方法。一旦计算完成, 就不能再重新开始或取消计算。get方法而获取结果只有在计算完成时获取,否则会一直阻塞直到任务转入完成状态, 然后会返回结果或者抛出异常。 只计算一次 get方法放到最后
代码
import java.util.concurrent.Callable; import java.util.concurrent.FutureTask; import java.util.concurrent.TimeUnit; class MyThread implements Runnable{ @Override public void run() { } } class MyThread2 implements Callable{ @Override public Integer call() throws Exception { System.out.println(Thread.currentThread().getName()+"come in callable"); return 200; } } public class CallableDemo { public static void main(String[] args) throws Exception { //FutureTask futureTask = new FutureTask(new MyThread2()); FutureTask futureTask = new FutureTask(()->{ System.out.println(Thread.currentThread().getName()+" come in callable"); TimeUnit.SECONDS.sleep(4); return 1024; }); FutureTask futureTask2 = new FutureTask(()->{ System.out.println(Thread.currentThread().getName()+" come in callable"); TimeUnit.SECONDS.sleep(4); return 2048; }); new Thread(futureTask,"zhang3").start(); new Thread(futureTask2,"li4").start(); //System.out.println(futureTask.get()); //System.out.println(futureTask2.get()); //1、一般放在程序后面,直接获取结果 //2、只会计算结果一次 while(!futureTask.isDone()){ System.out.println("***wait"); } System.out.println(futureTask.get()); System.out.println(Thread.currentThread().getName()+" come over"); } } /** * * 在主线程中需要执行比较耗时的操作时,但又不想阻塞主线程时,可以把这些作业交给Future对象在后台完成, 当主线程将来需要时,就可以通过Future对象获得后台作业的计算结果或者执行状态。 一般FutureTask多用于耗时的计算,主线程可以在完成自己的任务后,再去获取结果。 仅在计算完成时才能检索结果;如果计算尚未完成,则阻塞 get 方法。一旦计算完成, 就不能再重新开始或取消计算。get方法而获取结果只有在计算完成时获取,否则会一直阻塞直到任务转入完成状态, 然后会返回结果或者抛出异常。 只计算一次 get方法放到最后 */
9、JUC强大的辅助类讲解
CountDownLatch 减少计数
例子:CountDownLatchDemo
原理
* CountDownLatch主要有两个方法,当一个或多个线程调用await方法时,这些线程会阻塞。 * 其它线程调用countDown方法会将计数器减1(调用countDown方法的线程不会阻塞), * 当计数器的值变为0时,因await方法阻塞的线程会被唤醒,继续执行。
代码
package com.atguigu.thread; import java.util.concurrent.CountDownLatch; /** * * @Description: * *让一些线程阻塞直到另一些线程完成一系列操作后才被唤醒。 * * CountDownLatch主要有两个方法,当一个或多个线程调用await方法时,这些线程会阻塞。 * 其它线程调用countDown方法会将计数器减1(调用countDown方法的线程不会阻塞), * 当计数器的值变为0时,因await方法阻塞的线程会被唤醒,继续执行。 * * 解释:6个同学陆续离开教室后值班同学才可以关门。 * * main主线程必须要等前面6个线程完成全部工作后,自己才能开干 */ public class CountDownLatchDemo { public static void main(String[] args) throws InterruptedException { CountDownLatch countDownLatch = new CountDownLatch(6); for (int i = 1; i { new Thread(() -> { System.out.println(Thread.currentThread().getName()+"\t 号同学离开教室"); countDownLatch.countDown(); }, String.valueOf(i)).start(); } countDownLatch.await(); System.out.println(Thread.currentThread().getName()+"\t****** 班长关门走人,main线程是班长"); } }
CyclicBarrier 循环栅栏
例子:CyclicBarrierDemo
原理
* CyclicBarrier * 的字面意思是可循环(Cyclic)使用的屏障(Barrier)。它要做的事情是, * 让一组线程到达一个屏障(也可以叫同步点)时被阻塞, * 直到最后一个线程到达屏障时,屏障才会开门,所有 * 被屏障拦截的线程才会继续干活。 * 线程进入屏障通过CyclicBarrier的await()方法。
代码
package com.atguigu.thread; import java.util.concurrent.BrokenBarrierException; import java.util.concurrent.CyclicBarrier; /** * * * CyclicBarrier * 的字面意思是可循环(Cyclic)使用的屏障(Barrier)。它要做的事情是, * 让一组线程到达一个屏障(也可以叫同步点)时被阻塞, * 直到最后一个线程到达屏障时,屏障才会开门,所有 * 被屏障拦截的线程才会继续干活。 * 线程进入屏障通过CyclicBarrier的await()方法。 * * 集齐7颗龙珠就可以召唤神龙 */ public class CyclicBarrierDemo { private static final int NUMBER = 7; public static void main(String[] args) { //CyclicBarrier(int parties, Runnable barrierAction) CyclicBarrier cyclicBarrier = new CyclicBarrier(NUMBER, ()->{System.out.println("*****集齐7颗龙珠就可以召唤神龙");}) ; for (int i = 1; i new Thread(() -> { try { System.out.println(Thread.currentThread().getName()+"\t 星龙珠被收集 "); cyclicBarrier.await(); } catch (InterruptedException | BrokenBarrierException e) { // TODO Auto-generated catch block e.printStackTrace(); } }, String.valueOf(i)).start(); } } }
Semaphore 信号灯
例子:SemaphoreDemo
原理
在信号量上我们定义两种操作: * acquire(获取) 当一个线程调用acquire操作时,它要么通过成功获取信号量(信号量减1), * 要么一直等下去,直到有线程释放信号量,或超时。 * release(释放)实际上会将信号量的值加1,然后唤醒等待的线程。 * * 信号量主要用于两个目的,一个是用于多个共享资源的互斥使用,另一个用于并发线程数的控制。
代码
package com.atguigu.thread; import java.util.Random; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; /** * * @Description: TODO(这里用一句话描述这个类的作用) * * 在信号量上我们定义两种操作: * acquire(获取) 当一个线程调用acquire操作时,它要么通过成功获取信号量(信号量减1), * 要么一直等下去,直到有线程释放信号量,或超时。 * release(释放)实际上会将信号量的值加1,然后唤醒等待的线程。 * * 信号量主要用于两个目的,一个是用于多个共享资源的互斥使用,另一个用于并发线程数的控制。 */ public class SemaphoreDemo { public static void main(String[] args) { Semaphore semaphore = new Semaphore(3);//模拟3个停车位 for (int i = 1; i { new Thread(() -> { try { semaphore.acquire(); System.out.println(Thread.currentThread().getName()+"\t 抢到了车位"); TimeUnit.SECONDS.sleep(new Random().nextInt(5)); System.out.println(Thread.currentThread().getName()+"\t------- 离开"); } catch (InterruptedException e) { e.printStackTrace(); }finally { semaphore.release(); } }, String.valueOf(i)).start(); } } }
10、ReentrantReadWriteLock 读写锁
例子:ReadWriteLockDemo
类似案例
红蜘蛛
缓存
问题例子
import java.util.HashMap; import java.util.Map; import java.util.concurrent.TimeUnit; class MyCache{ private volatile Map map = new HashMap(); public void put(String key,Object value){ System.out.println(Thread.currentThread().getName()+"\t 正在写"+key); //暂停一会儿线程 try {TimeUnit.MILLISECONDS.sleep(300);} catch (InterruptedException e) {e.printStackTrace(); } map.put(key,value); System.out.println(Thread.currentThread().getName()+"\t 写完了"+key); } public Object get(String key){ Object result = null; System.out.println(Thread.currentThread().getName()+"\t 正在读"+key); try {TimeUnit.MILLISECONDS.sleep(300);} catch (InterruptedException e) {e.printStackTrace(); } result = map.get(key); System.out.println(Thread.currentThread().getName()+"\t 读完了"+result); return result; } } public class ReadWriteLockDemo { public static void main(String[] args) { MyCache myCache = new MyCache(); for (int i = 1; i 5; i++) { final int num = i; new Thread(()->{ myCache.put(num+"",num+""); },String.valueOf(i)).start(); } for (int i = 1; i 5; i++) { final int num = i; new Thread(()->{ myCache.get(num+""); },String.valueOf(i)).start(); } } }
代码
import java.util.HashMap; import java.util.Map; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; class MyCache { private volatile Map map = new HashMap(); private ReadWriteLock rwLock = new ReentrantReadWriteLock(); public void put(String key, Object value) { rwLock.writeLock().lock(); try { System.out.println(Thread.currentThread().getName() + "\t 正在写" + key); //暂停一会儿线程 try { TimeUnit.MILLISECONDS.sleep(300); } catch (InterruptedException e) { e.printStackTrace(); } map.put(key, value); System.out.println(Thread.currentThread().getName() + "\t 写完了" + key); System.out.println(); } catch (Exception e) { e.printStackTrace(); } finally { rwLock.writeLock().unlock(); } } public Object get(String key) { rwLock.readLock().lock(); Object result = null; try { System.out.println(Thread.currentThread().getName() + "\t 正在读" + key); try { TimeUnit.MILLISECONDS.sleep(300); } catch (InterruptedException e) { e.printStackTrace(); } result = map.get(key); System.out.println(Thread.currentThread().getName() + "\t 读完了" + result); } catch (Exception e) { e.printStackTrace(); } finally { rwLock.readLock().unlock(); } return result; } } public class ReadWriteLockDemo { public static void main(String[] args) { MyCache myCache = new MyCache(); for (int i = 1; i 5; i++) { final int num = i; new Thread(() -> { myCache.put(num + "", num + ""); }, String.valueOf(i)).start(); } for (int i = 1; i 5; i++) { final int num = i; new Thread(() -> { myCache.get(num + ""); }, String.valueOf(i)).start(); } } }
11、BlockingQueueDemo 阻塞队列
例子:BlockingQueueDemo
栈与队列
栈:先进后出,后进先出
队列:先进先出
阻塞队列
阻塞:必须要阻塞/不得不阻塞 阻塞队列是一个队列,在数据结构中起的作用如下图:  线程1往阻塞队列里添加元素,线程2从阻塞队列里移除元素 当队列是空的,从队列中获取元素的操作将会被阻塞 当队列是满的,从队列中添加元素的操作将会被阻塞 试图从空的队列中获取元素的线程将会被阻塞,直到其他线程往空的队列插入新的元素 试图向已满的队列中添加新元素的线程将会被阻塞,直到其他线程从队列中移除一个或多个元素或者完全清空,使队列变得空闲起来并后续新增
阻塞队列的用处
在多线程领域:所谓阻塞,在某些情况下会挂起线程(即阻塞),一旦条件满足,被挂起的线程又会自动被唤起 为什么需要BlockingQueue 好处是我们不需要关心什么时候需要阻塞线程,什么时候需要唤醒线程,因为这一切BlockingQueue都给你一手包办了 在concurrent包发布以前,在多线程环境下,我们每个程序员都必须去自己控制这些细节,尤其还要兼顾效率和线程安全,而这会给我们的程序带来不小的复杂度。
架构梳理、种类分析
架构介绍

种类分析
ArrayBlockingQueue:由数组结构组成的有界阻塞队列。
LinkedBlockingQueue:由链表结构组成的有界(但大小默认值为integer.MAX_VALUE)阻塞队列。
PriorityBlockingQueue:支持优先级排序的无界阻塞队列。
DelayQueue:使用优先级队列实现的延迟无界阻塞队列。
SynchronousQueue:不存储元素的阻塞队列,也即单个元素的队列。
LinkedTransferQueue:由链表组成的无界阻塞队列。
LinkedBlockingDeque:由链表组成的双向阻塞队列。
BlockingQueue核心方法
 抛出异常 当阻塞队列满时,再往队列里add插入元素会抛IllegalStateException:Queue full 当阻塞队列空时,再往队列里remove移除元素会抛NoSuchElementException 特殊值 插入方法,成功ture失败false 移除方法,成功返回出队列的元素,队列里没有就返回null 一直阻塞 当阻塞队列满时,生产者线程继续往队列里put元素,队列会一直阻塞生产者线程直到put数据or响应中断退出 当阻塞队列空时,消费者线程试图从队列里take元素,队列会一直阻塞消费者线程直到队列可用 超时退出 当阻塞队列满时,队列会阻塞生产者线程一定时间,超过限时后生产者线程会退出
代码
import java.util.ArrayList; import java.util.List; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; import java.util.concurrent.TimeUnit; /** * 阻塞队列 */ public class BlockingQueueDemo { public static void main(String[] args) throws InterruptedException { // List list = new ArrayList(); BlockingQueue blockingQueue = new ArrayBlockingQueue(3); //第一组 // System.out.println(blockingQueue.add("a")); // System.out.println(blockingQueue.add("b")); // System.out.println(blockingQueue.add("c")); // System.out.println(blockingQueue.element()); //System.out.println(blockingQueue.add("x")); // System.out.println(blockingQueue.remove()); // System.out.println(blockingQueue.remove()); // System.out.println(blockingQueue.remove()); // System.out.println(blockingQueue.remove()); // 第二组 // System.out.println(blockingQueue.offer("a")); // System.out.println(blockingQueue.offer("b")); // System.out.println(blockingQueue.offer("c")); // System.out.println(blockingQueue.offer("x")); // System.out.println(blockingQueue.poll()); // System.out.println(blockingQueue.poll()); // System.out.println(blockingQueue.poll()); // System.out.println(blockingQueue.poll()); // 第三组 // blockingQueue.put("a"); // blockingQueue.put("b"); // blockingQueue.put("c"); // //blockingQueue.put("x"); // System.out.println(blockingQueue.take()); // System.out.println(blockingQueue.take()); // System.out.println(blockingQueue.take()); // System.out.println(blockingQueue.take()); // 第四组 System.out.println(blockingQueue.offer("a")); System.out.println(blockingQueue.offer("b")); System.out.println(blockingQueue.offer("c")); System.out.println(blockingQueue.offer("a",3L, TimeUnit.SECONDS)); } }
12、ThreadPool线程池
例子:MyThreadPoolDemo
为什么用线程池
例子: 10年前单核CPU电脑,假的多线程,像马戏团小丑玩多个球,CPU需要来回切换。 现在是多核电脑,多个线程各自跑在独立的CPU上,不用切换效率高。 线程池的优势: 线程池做的工作只要是控制运行的线程数量,处理过程中将任务放入队列,然后在线程创建后启动这些任务,如果线程数量超过了最大数量,超出数量的线程排队等候,等其他线程执行完毕,再从队列中取出任务来执行。 它的主要特点为:线程复用;控制最大并发数;管理线程。 第一:降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的销耗。 第二:提高响应速度。当任务到达时,任务可以不需要等待线程创建就能立即执行。 第三:提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会销耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。
线程池如何使用
架构说明
Java中的线程池是通过Executor框架实现的,该框架中用到了Executor,Executors,ExecutorService,ThreadPoolExecutor这几个类 
编码实现
Executors.newFixedThreadPool(int)
public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue()); } newFixedThreadPool创建的线程池corePoolSize和maximumPoolSize值是相等的,它使用的是LinkedBlockingQueue
执行长期任务性能好,创建一个线程池, 一池有N个固定的线程,有固定线程数的线程
Executors.newSingleThreadExecutor()
public static ExecutorService newSingleThreadExecutor() { return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue())); } newSingleThreadExecutor 创建的线程池corePoolSize和maximumPoolSize值都是1,它使用的是LinkedBlockingQueue
一个任务一个任务的执行,一池一线程
Executors.newCachedThreadPool()
public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue()); } newCachedThreadPool创建的线程池将corePoolSize设置为0,将maximumPoolSize设置为Integer.MAX_VALUE,它使用的是SynchronousQueue,也就是说来了任务就创建线程运行,当线程空闲超过60秒,就销毁线程。
执行很多短期异步任务,线程池根据需要创建新线程, 但在先前构建的线程可用时将重用它们。可扩容,遇强则强
代码
import java.util.Arrays; import java.util.List; import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** * 线程池 * Arrays * Collections * Executors */ public class MyThreadPoolDemo { public static void main(String[] args) { //List list = new ArrayList(); //List list = Arrays.asList("a","b"); //固定数的线程池,一池五线程 // ExecutorService threadPool = Executors.newFixedThreadPool(5); //一个银行网点,5个受理业务的窗口 // ExecutorService threadPool = Executors.newSingleThreadExecutor(); //一个银行网点,1个受理业务的窗口 ExecutorService threadPool = Executors.newCachedThreadPool(); //一个银行网点,可扩展受理业务的窗口 //10个顾客请求 try { for (int i = 1; i 10; i++) { threadPool.execute(()->{ System.out.println(Thread.currentThread().getName()+"\t 办理业务"); }); } } catch (Exception e) { e.printStackTrace(); } finally { threadPool.shutdown(); } } }
ThreadPoolExecutor底层原理

线程池几个重要参数
7大参数
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) { if (corePoolSize 0 || maximumPoolSize 0 || maximumPoolSize 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; }
1、corePoolSize:线程池中的常驻核心线程数
2、maximumPoolSize:线程池中能够容纳同时 执行的最大线程数,此值必须大于等于1
3、keepAliveTime:多余的空闲线程的存活时间 当前池中线程数量超过corePoolSize时,当空闲时间 达到keepAliveTime时,多余线程会被销毁直到 只剩下corePoolSize个线程为止
4、unit:keepAliveTime的单位
5、workQueue:任务队列,被提交但尚未被执行的任务
6、threadFactory:表示生成线程池中工作线程的线程工厂, 用于创建线程,一般默认的即可
7、handler:拒绝策略,表示当队列满了,并且工作线程大于 等于线程池的最大线程数(maximumPoolSize)时如何来拒绝 请求执行的runnable的策略
线程池底层工作原理
 以下重要:以下重要:以下重要:以下重要:以下重要:以下重要: 1、在创建了线程池后,开始等待请求。 2、当调用execute()方法添加一个请求任务时,线程池会做出如下判断: 2.1如果正在运行的线程数量小于corePoolSize,那么马上创建线程运行这个任务; 2.2如果正在运行的线程数量大于或等于corePoolSize,那么将这个任务放入队列; 2.3如果这个时候队列满了且正在运行的线程数量还小于maximumPoolSize,那么还是要创建非核心线程立刻运行这个任务; 2.4如果队列满了且正在运行的线程数量大于或等于maximumPoolSize,那么线程池会启动饱和拒绝策略来执行。 3、当一个线程完成任务时,它会从队列中取下一个任务来执行。 4、当一个线程无事可做超过一定的时间(keepAliveTime)时,线程会判断: 如果当前运行的线程数大于corePoolSize,那么这个线程就被停掉。 所以线程池的所有任务完成后,它最终会收缩到corePoolSize的大小。
线程池用哪个?生产中如设置合理参数
线程池的拒绝策略
是什么
等待队列已经排满了,再也塞不下新任务了 同时, 线程池中的max线程也达到了,无法继续为新任务服务。 这个是时候我们就需要拒绝策略机制合理的处理这个问题。
JDK内置的拒绝策略
AbortPolicy(默认):直接抛出RejectedExecutionException异常阻止系统正常运行
CallerRunsPolicy:“调用者运行”一种调节机制,该策略既不会抛弃任务,也不 会抛出异常,而是将某些任务回退到调用者,从而降低新任务的流量。
DiscardOldestPolicy:抛弃队列中等待最久的任务,然后把当前任务加人队列中 尝试再次提交当前任务。
DiscardPolicy:该策略默默地丢弃无法处理的任务,不予任何处理也不抛出异常。 如果允许任务丢失,这是最好的一种策略。
以上内置拒绝策略均实现了 RejectedExecutionHandle接口
在工作中单一的/固定数的/可变的三种创建线程池的方法哪个用的多?超级大坑
答案是一个都不用,我们工作中只能使用自定义的
Executors中JDK已经给你提供了,为什么不用?

在工作中如何使用线程池,是否自定义过线程池
代码
import java.util.Arrays; import java.util.List; import java.util.concurrent.*; /** * 线程池 * Arrays * Collections * Executors */ public class MyThreadPoolDemo { public static void main(String[] args) { ExecutorService threadPool = new ThreadPoolExecutor( 2, 5, 2L, TimeUnit.SECONDS, new ArrayBlockingQueue(3), Executors.defaultThreadFactory(), //new ThreadPoolExecutor.AbortPolicy() //new ThreadPoolExecutor.CallerRunsPolicy() //new ThreadPoolExecutor.DiscardOldestPolicy() new ThreadPoolExecutor.DiscardOldestPolicy() ); //10个顾客请求 try { for (int i = 1; i 10; i++) { threadPool.execute(() -> { System.out.println(Thread.currentThread().getName() + "\t 办理业务"); }); } } catch (Exception e) { e.printStackTrace(); } finally { threadPool.shutdown(); } } private static void threadPool() { //List list = new ArrayList(); //List list = Arrays.asList("a","b"); //固定数的线程池,一池五线程 // ExecutorService threadPool = Executors.newFixedThreadPool(5); //一个银行网点,5个受理业务的窗口 // ExecutorService threadPool = Executors.newSingleThreadExecutor(); //一个银行网点,1个受理业务的窗口 ExecutorService threadPool = Executors.newCachedThreadPool(); //一个银行网点,可扩展受理业务的窗口 //10个顾客请求 try { for (int i = 1; i 10; i++) { threadPool.execute(() -> { System.out.println(Thread.currentThread().getName() + "\t 办理业务"); }); } } catch (Exception e) { e.printStackTrace(); } finally { threadPool.shutdown(); } } }
13、Java8之流式计算复习
例子:StreamDemo
函数式接口
java.util.function

java内置核心四大函数式接口

实例
//R apply(T t);函数型接口,一个参数,一个返回值 Function function = t ->{return t.length();}; System.out.println(function.apply("abcd")); //boolean test(T t);断定型接口,一个参数,返回boolean Predicate predicate = t->{return t.startsWith("a");}; System.out.println(predicate.test("a")); // void accept(T t);消费型接口,一个参数,没有返回值 Consumer consumer = t->{ System.out.println(t); }; consumer.accept("javaXXXX"); //T get(); 供给型接口,无参数,有返回值 Supplier supplier =()->{return UUID.randomUUID().toString();}; System.out.println(supplier.get());
Stream流
What
流(Stream) 到底是什么呢? 是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。 “集合讲的是数据,流讲的是计算!”
Why
特点
Stream 自己不会存储元素
Stream 不会改变源对象。相反, 他们会返回一个持有结果的新Stream。
Stream 操作是延迟执行的。这意味着 他们会等到需要结果的时候才执行。
How
阶段
创建一个Stream:一个数据源(数组、集合)
中间操作:一个中间操作,处理数据源数据
终止操作:一个终止操作,执行中间操作链,产生结果
源头=>中间流水线=>结果
package com.atguigu.juc.study; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import java.util.Arrays; import java.util.List; import java.util.UUID; import java.util.function.*; @Data @NoArgsConstructor @AllArgsConstructor class User { private Integer id; private String userName; private int age; } /** * @create 2019-02-26 22:24 * * 题目:请按照给出数据,找出同时满足 * 偶数ID且年龄大于24且用户名转为大写且用户名字母倒排序 * 最后只输出一个用户名字 */ public class StreamDemo { public static void main(String[] args) { User u1 = new User(11,"a",23); User u2 = new User(12,"b",24); User u3 = new User(13,"c",22); User u4 = new User(14,"d",28); User u5 = new User(16,"e",26); List list = Arrays.asList(u1,u2,u3,u4,u5); list.stream().filter(p -> { return p.getId() % 2 == 0; }).filter(p -> { return p.getAge() > 24; }).map(f -> { return f.getUserName().toUpperCase(); }).sorted((o1, o2) -> { return o2.compareTo(o1); }).limit(1).forEach(System.out::println); // R apply(T t); Function function = t -> {return t.length();}; System.out.println(function.apply("abc")); // boolean test(T t); Predicate predicate = t -> {return t.startsWith("a");}; System.out.println(predicate.test("a")); //void accept(T t); Consumer consumer = t -> {System.out.println(t);}; consumer.accept("java1018"); // T get(); Supplier supplier = () -> {return UUID.randomUUID().toString();}; System.out.println(supplier.get());; } }
14、分支合并框架
例子:ForkJoinDemo
原理
Fork:把一个复杂任务进行分拆,大事化小 Join:把分拆任务的结果进行合并 
相关类
ForkJoinPool
 分支合并池 类比=> 线程池
ForkJoinTask
 ForkJoinTask 类比=> FutureTask
RecursiveTask
 递归任务:继承后可以实现递归(自己调自己)调用的任务 class Fibonacci extends RecursiveTask { final int n; Fibonacci(int n) { this.n = n; } Integer compute() { if (n return n; Fibonacci f1 = new Fibonacci(n - 1); f1.fork(); Fibonacci f2 = new Fibonacci(n - 2); return f2.compute() + f1.join(); } }
实例
import java.util.concurrent.ExecutionException; import java.util.concurrent.ForkJoinPool; import java.util.concurrent.ForkJoinTask; import java.util.concurrent.RecursiveTask; class MyTask extends RecursiveTask{ private static final Integer ADJUST_VALUE = 10; private int begin; private int end; private int result; public MyTask(int begin, int end) { this.begin = begin; this.end = end; } @Override protected Integer compute() { if((end - begin)ADJUST_VALUE){ for(int i =begin;i end;i++){ result = result + i; } }else{ int middle = (begin + end)/2; MyTask task01 = new MyTask(begin,middle); MyTask task02 = new MyTask(middle+1,end); task01.fork(); task02.fork(); result = task01.join() + task02.join(); } return result; } } /** * 分支合并例子 * ForkJoinPool * ForkJoinTask * RecursiveTask */ public class ForkJoinDemo { public static void main(String[] args) throws ExecutionException, InterruptedException { MyTask myTask = new MyTask(0,100); ForkJoinPool forkJoinPool = new ForkJoinPool(); ForkJoinTask forkJoinTask = forkJoinPool.submit(myTask); System.out.println(forkJoinTask.get()); forkJoinPool.shutdown(); } }
15、异步回调
例子:CompletableFutureDemo
原理
同步、异步、异步回调 
实例
import java.util.concurrent.CompletableFuture; public class CompletableFutureDemo { public static void main(String[] args) throws Exception { //同步,异步,异步回调 //同步 // CompletableFuture completableFuture1 = CompletableFuture.runAsync(()->{ // System.out.println(Thread.currentThread().getName()+"\t completableFuture1"); // }); // completableFuture1.get(); //异步回调 CompletableFuture completableFuture2 = CompletableFuture.supplyAsync(()->{ System.out.println(Thread.currentThread().getName()+"\t completableFuture2"); int i = 10/0; return 1024; }); completableFuture2.whenComplete((t,u)->{ System.out.println("-------t="+t); System.out.println("-------u="+u); }).exceptionally(f->{ System.out.println("-----exception:"+f.getMessage()); return 444; }).get(); } }