山西自助建站费用低,天津市建设工程定额管理站网站,医院网站建设运营方案,如何提升顾客的体验和服务目录
一、为什么要有多线程#xff1f;
1、线程与进程
2、多线程的应用场景
3、小结编辑
二、多线程中的两个概念#xff08;并发和并行#xff09;
1、并发
2、并行
3、小结
三、多线程的三种实现方式
1、继承Thread类的方式进行实现
2、实现Runnable接口的方…目录
一、为什么要有多线程
1、线程与进程
2、多线程的应用场景
3、小结编辑
二、多线程中的两个概念并发和并行
1、并发
2、并行
3、小结
三、多线程的三种实现方式
1、继承Thread类的方式进行实现
2、实现Runnable接口的方式进行实现
3、利用Callable接口和Future接口方式的实现
4、多线程三种实现方式对比
四、常见的成员方法
1、get/setName方法 -- 线程名字
2、currentThread方法 -- 获取当前线程对象
3、sleep方法 -- 线程休眠
4、set/getPriority方法 -- 线程优先级
5、setDaemon方法 -- 守护线程
6、yield方法 -- 礼让线程
7、join方法 -- 插入线程
8、线程的生命周期
五、线程安全的问题
1、练习设计一个程序模拟电影院卖票
2、买票引发的安全问题
2.1、重复票的由来线程在执行代码的过程中CPU的执行权随时有可能被抢走
2.2、出现了超出范围的票和上面的原因相同
3、安全问题的解决办法 -- 同步代码块
4、同步代码块中的两个小细节
4.1、细节1synchronized要写在循环的里面编辑
4.2、细节2synchronized中的锁对象一定是唯一的
5、同步方法
6、StringBuilder和StringBuffer的区别
7、Lock锁手动加锁、释放锁编辑
7.1、Lock使用不规范造成的两个安全问题
六、死锁
七、生产者和消费者等待唤醒机制
1、消费者等待
2、生产者等待编辑
3、常见方法wait/notify/notifyAll
4、消费者与生产者代码实现
4.1、Cook.java
4.2、Desk.java
4.3、Foodie.java
4.4、ThreadDemo.java
5、阻塞队列方式另一种等待唤醒机制编辑
5.1、阻塞队列的继承结构编辑
5.2、阻塞队列实现等待唤醒机制
7、多线程的6中状态
八、综合练习
1、多线程练习1卖电影票编辑
2、多线程练习2送礼品
3、多线程练习3打印奇数数字编辑
4、多线程练习4抢红包
5、多线程练习5抽奖箱抽奖
6、多线程练习6多线程统计并求最大值
7、多线程练习7多线程之间的比较
8、多线程练习8多线程阶段大作业
九、线程池
1、吃饭买碗的故事
1.1、问题编辑
1.2、解决方案
2、以前写多线程的弊端编辑
3、线程池的核心原理
4、线程池的代码实现
4.1、Executors工具类编辑
4.2、线程复用示例
4.3、创建一个有上限的线程池
5、自定义线程池ThreadPoolExecutor
5.1、任务拒绝策略编辑
5.2、代码实现编辑
5.3、小结编辑
6、最大并行数
6.1、什么是最大并行数编辑
6.2、向Java虚拟机返回可用处理器的数目
7、线程池多大才合适
编辑
十、多线程的额外扩展内容准备面试时可以再突击学习资料可见《多线程额外扩展.md》 一、为什么要有多线程
1、线程与进程
进程进程是程序的基本执行实体
举例在任务管理器中一个软件运行之后它就是一个进程
线程简单理解线程就说应用软件中互相独立可以同时运行的功能 单线程程序所有的都在一个线程中执行耗时长 2、多线程的应用场景 3、小结
二、多线程中的两个概念并发和并行
1、并发 2、并行 以2核4线程为例如果计算机中只要4条线程那么它是不用切换的但如果线程越来越多那么这个红线就会在多个线程之间随机的进行切换
3、小结 三、多线程的三种实现方式 1、继承Thread类的方式进行实现 自己定义一个类继承Thread并重写run方法
创建子类的对象并启动线程 2、实现Runnable接口的方式进行实现 自己定义一个类实现Runnable接口并重新里面的run方法 public class MyRun implements Runnable {Overridepublic void run() {//书写线程要执行的代码for (int i 0; i 100; i) {//获取当前线程的对象/*Thread t Thread.currentThread();System.out.println(t.getName()HelloWorld);*/System.out.println(Thread.currentThread().getName()HelloWorld);}}
} package com.yaqi.a02threadcase2;public class ThreadDemo {public static void main(String[] args) {/*多线程的第二种启动方式* 1.自己定义一个类实现Runnable接口* 2.重写里面的run方法* 3.创建自己的类的对象* 4.创建一个Thread类的对象并开启线程*///创建MyRun的对象//表示多线程要执行的任务MyRun mr new MyRun();//创建线程对象Thread t1 new Thread(mr);Thread t2 new Thread(mr);//给线程设置名字t1.setName(线程1);t1.setName(线程2);//开启线程t1.start();t2.start();}
}结果
3、利用Callable接口和Future接口方式的实现 package com.yaqi.a03threadcase3;import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;public class ThreadDemo {public static void main(String[] args) throws ExecutionException, InterruptedException {/** 多线程的第三种实现方式* 特点可以获取到多线程运行的结果** 1. 创建一个类MyCallable实现Callable接口* 2. 重写call 是有返回值的表示多线程运行的结果** 3. 创建MyCallable的对象表示多线程要执行的任务* 4. 创建FutureTask的对象作用管理多线程运行的结果* 5. 创建Thread类的对象并启动表示线程* *///创建MyCallable的对象表示多线程要执行的任务MyCallable mc new MyCallable();//创建FutureTask的对象作用管理多线程运行的结果FutureTaskInteger ft new FutureTask(mc);//创建线程的对象Thread t1 new Thread(ft);//启动线程t1.start();//获取多线程运行的结果Integer result ft.get();System.out.println(result);}
}package com.yaqi.a03threadcase3;import java.util.concurrent.Callable;public class MyCallable implements CallableInteger {Overridepublic Integer call() throws Exception {//求1~100之间的和int sum 0;for (int i 1; i 100; i) {sum sum i;}return sum;}
}4、多线程三种实现方式对比 四、常见的成员方法 1、get/setName方法 -- 线程名字
默认名字的由来 序号自增 /*String getName() 返回此线程的名称void setName(String name) 设置线程的名字构造方法也可以设置名字细节1、如果我们没有给线程设置名字线程也是有默认的名字的格式Thread-XX序号从0开始的2、如果我们要给线程设置名字可以用set方法进行设置也可以构造方法设置*/
//1.创建线程的对象MyThread t1 new MyThread(飞机);MyThread t2 new MyThread(坦克);//2.开启线程t1.start();t2.start(); MyThread
package com.yaqi.a04threadmethod1;public class MyThread extends Thread{public MyThread() {}public MyThread(String name) {super(name);}Overridepublic void run() {for (int i 0; i 100; i) {try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(getName() i);}}
}2、currentThread方法 -- 获取当前线程对象 static Thread currentThread() 获取当前线程的对象
细节当JVM虚拟机启动之后会自动的启动多条线程其中有一条线程就叫做main线程他的作用就是去调用main方法并执行里面的代码在以前我们写的所有的代码其实都是运行在main线程当中 //哪条线程执行到这个方法此时获取的就是哪条线程的对象Thread t Thread.currentThread();String name t.getName();System.out.println(name);//main
3、sleep方法 -- 线程休眠 static void sleep(long time) 让线程休眠指定的时间单位为毫秒
细节1、哪条线程执行到这个方法那么哪条线程就会在这里停留对应的时间2、方法的参数就表示睡眠的时间单位毫秒1 秒 1000毫秒3、当时间到了之后线程会自动的醒来继续执行下面的其他代码 System.out.println(11111111111);Thread.sleep(5000);System.out.println(22222222222);
4、set/getPriority方法 -- 线程优先级 没有设置优先级则默认为5优先级越高抢到CPU的概率就越高
最小1最大10默认5 5、setDaemon方法 -- 守护线程
两个线程执行的代码不同守护线程是陆续结束的所以守护线程也叫做备胎线程 应用场景 6、yield方法 -- 礼让线程 但是只是尽可能的均匀不是绝对的 7、join方法 -- 插入线程 插入线程将土豆插入到main线程之前只有当土豆线程执行完毕才会轮到main线程 8、线程的生命周期 五、线程安全的问题
1、练习设计一个程序模拟电影院卖票 出现了超出票范围或者重复票的情况 2、买票引发的安全问题 相同的票出现多次
出现了超出范围的票
2.1、重复票的由来线程在执行代码的过程中CPU的执行权随时有可能被抢走 2.2、出现了超出范围的票和上面的原因相同 3、安全问题的解决办法 -- 同步代码块 示例代码 结果
4、同步代码块中的两个小细节
4.1、细节1synchronized要写在循环的里面
4.2、细节2synchronized中的锁对象一定是唯一的 5、同步方法 示例代码 将同步代码块改成同步方法
6、StringBuilder和StringBuffer的区别 两个类的方法都是相同的
但是StringBuffer是线程安全的它里面所有的方法都是线程同步的 StringBulider代码单线程的不需要考虑多线程当中数据安全的情况 StringBuffer多线程环境下需要考虑数据安全则选择StringBuffer
7、Lock锁手动加锁、释放锁
7.1、Lock使用不规范造成的两个安全问题 Ⅰ、重复票以及超出范围票 我们在使用Thread类实现多线程时创建自己的类一定要注意锁对象需要唯一即在相关变量前加上static关键字
Ⅱ、程序无法正常终止 这是由于当满足条件时循环直接被终止导致lock锁没有被释放 Ⅲ、正确代码标准写法
即将容易产生异常的代码块放入try…catch中 六、死锁
死锁是指两个或两个以上的进程在执行过程中由于竞争资源或者由于彼此通信而造成的一种阻塞的现象若无外力作用它们都将无法推进下去。 注意事项千万不要让两个锁嵌套起来 七、生产者和消费者等待唤醒机制
生产者消费者模式是一个十分经典的多线程协作的模式 1、消费者等待 2、生产者等待
3、常见方法wait/notify/notifyAll 4、消费者与生产者代码实现
4.1、Cook.java
public class Cook extends Thread{Overridepublic void run() {/** 1. 循环* 2. 同步代码块* 3. 判断共享数据是否到了末尾到了末尾* 4. 判断共享数据是否到了末尾没有到末尾执行核心逻辑* */while (true){synchronized (com.yaqi.a13waitandnotify.Desk.lock){if(com.yaqi.a13waitandnotify.Desk.count 0){break;}else{//判断桌子上是否有食物if(com.yaqi.a13waitandnotify.Desk.foodFlag 1){//如果有就等待try {com.yaqi.a13waitandnotify.Desk.lock.wait();} catch (InterruptedException e) {e.printStackTrace();}}else{//如果没有就制作食物System.out.println(厨师做了一碗面条);//修改桌子上的食物状态com.yaqi.a13waitandnotify.Desk.foodFlag 1;//叫醒等待的消费者开吃com.yaqi.a13waitandnotify.Desk.lock.notifyAll();}}}}}
}4.2、Desk.java
package com.yaqi.a13waitandnotify;public class Desk {/** 作用控制生产者和消费者的执行** *///是否有面条 0没有面条 1有面条public static int foodFlag 0;//总个数public static int count 10;//锁对象public static Object lock new Object();}4.3、Foodie.java public class Foodie extends Thread{Overridepublic void run() {/** 1. 循环* 2. 同步代码块* 3. 判断共享数据是否到了末尾到了末尾* 4. 判断共享数据是否到了末尾没有到末尾执行核心逻辑* */while(true){synchronized (com.yaqi.a13waitandnotify.Desk.lock){if(com.yaqi.a13waitandnotify.Desk.count 0){break;}else{//先判断桌子上是否有面条if(com.yaqi.a13waitandnotify.Desk.foodFlag 0){//如果没有就等待try {com.yaqi.a13waitandnotify.Desk.lock.wait();//让当前线程跟锁进行绑定} catch (InterruptedException e) {e.printStackTrace();}}else{//把吃的总数-1com.yaqi.a13waitandnotify.Desk.count--;//如果有就开吃System.out.println(吃货在吃面条还能再吃 com.yaqi.a13waitandnotify.Desk.count 碗);//吃完之后唤醒厨师继续做com.yaqi.a13waitandnotify.Desk.lock.notifyAll();//修改桌子的状态com.yaqi.a13waitandnotify.Desk.foodFlag 0;}}}}}
}4.4、ThreadDemo.java
package com.yaqi.a13waitandnotify;public class ThreadDemo {public static void main(String[] args) {/*** 需求完成生产者和消费者等待唤醒机制的代码* 实现线程轮流交替执行的效果** *///创建线程的对象Cook c new Cook();Foodie f new Foodie();//给线程设置名字c.setName(厨师);f.setName(吃货);//开启线程c.start();f.start();}
} 5、阻塞队列方式另一种等待唤醒机制
5.1、阻塞队列的继承结构
5.2、阻塞队列实现等待唤醒机制
Cook.java
package com.yaqi.a14waitandnotify;import java.util.concurrent.ArrayBlockingQueue;public class Cook extends Thread{ArrayBlockingQueueString queue;public Cook(ArrayBlockingQueueString queue) {this.queue queue;}Overridepublic void run() {while(true){//不断的把面条放到阻塞队列当中try {queue.put(面条);System.out.println(厨师放了一碗面条);} catch (InterruptedException e) {e.printStackTrace();}}}
}put方法的源码中实现了Lock锁 Foodie.java
take方法的底层也是有锁的 import java.util.concurrent.ArrayBlockingQueue;public class Foodie extends Thread{ArrayBlockingQueueString queue;public Foodie(ArrayBlockingQueueString queue) {this.queue queue;}Overridepublic void run() {while(true){//不断从阻塞队列中获取面条try {String food queue.take();System.out.println(food);} catch (InterruptedException e) {e.printStackTrace();}}}
}ThreadDemo.java
package com.yaqi.a14waitandnotify;import java.util.concurrent.ArrayBlockingQueue;public class ThreadDemo {public static void main(String[] args) {/*** 需求利用阻塞队列完成生产者和消费者等待唤醒机制的代码* 细节* 生产者和消费者必须使用同一个阻塞队列** *///1.创建阻塞队列的对象ArrayBlockingQueueString queue new ArrayBlockingQueue(1);//2.创建线程的对象并把阻塞队列传递过去Cook c new Cook(queue);Foodie f new Foodie(queue);//3.开启线程c.start();f.start();}
}
打印语句是在锁的外面的但是不会对数据造成影响只是影响了控制台的打印阅读体验
7、多线程的6中状态 八、综合练习
1、多线程练习1卖电影票
2、多线程练习2送礼品 3、多线程练习3打印奇数数字
4、多线程练习4抢红包
package com.yaqi.test4case1;import java.util.Random;public class MyThread extends Thread{//共享数据//100块分成了3个包static double money 100;static int count 3;//最小的中奖金额static final double MIN 0.01;Overridepublic void run() {//同步代码块synchronized (MyThread.class){if(count 0){//判断共享数据是否到了末尾已经到末尾System.out.println(getName() 没有抢到红包);}else{//判断共享数据是否到了末尾没有到末尾//定义一个变量表示中奖的金额double prize 0;if(count 1){//表示此时是最后一个红包//就无需随机剩余所有的钱都是中奖金额prize money;}else{//表示第一次第二次随机Random r new Random();//100 元 3个包//第一个红包99.98//100 - (3-1) * 0.01double bounds money - (count - 1) * MIN;prize r.nextDouble(bounds);if(prize MIN){prize MIN;}}//从money当中去掉当前中奖的金额money money - prize;//红包的个数-1count--;//本次红包的信息进行打印System.out.println(getName() 抢到了 prize 元);}}}
}测试类
package com.yaqi.test4case1;public class Test {public static void main(String[] args) {/*微信中的抢红包也用到了多线程。假设100块分成了3个包现在有5个人去抢。其中红包是共享数据。5个人是5条线程。打印结果如下XXX抢到了XXX元XXX抢到了XXX元XXX抢到了XXX元XXX没抢到XXX没抢到*///创建线程的对象MyThread t1 new MyThread();MyThread t2 new MyThread();MyThread t3 new MyThread();MyThread t4 new MyThread();MyThread t5 new MyThread();//给线程设置名字t1.setName(小A);t2.setName(小QQ);t3.setName(小哈哈);t4.setName(小诗诗);t5.setName(小丹丹);//启动线程t1.start();t2.start();t3.start();t4.start();t5.start();}
}精确运算BigDecimal
package com.yaqi.test4case2;import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Random;public class MyThread extends Thread{//总金额static BigDecimal money BigDecimal.valueOf(100.0);//个数static int count 3;//最小抽奖金额static final BigDecimal MIN BigDecimal.valueOf(0.01);Overridepublic void run() {synchronized (MyThread.class){if(count 0){System.out.println(getName() 没有抢到红包);}else{//中奖金额BigDecimal prize;if(count 1){prize money;}else{//获取抽奖范围double bounds money.subtract(BigDecimal.valueOf(count-1).multiply(MIN)).doubleValue();Random r new Random();//抽奖金额prize BigDecimal.valueOf(r.nextDouble(bounds));}//设置抽中红包小数点保留两位四舍五入prize prize.setScale(2,RoundingMode.HALF_UP);//在总金额中去掉对应的钱money money.subtract(prize);//红包少了一个count--;//输出红包信息System.out.println(getName() 抽中了 prize 元);}}}
}5、多线程练习5抽奖箱抽奖 package com.yaqi.test5;import java.util.ArrayList;
import java.util.Collections;public class MyThread extends Thread {ArrayListInteger list;public MyThread(ArrayListInteger list) {this.list list;}Overridepublic void run() {//1.循环//2.同步代码块//3.判断//4.判断while (true) {synchronized (MyThread.class) {if (list.size() 0) {break;} else {//继续抽奖Collections.shuffle(list);int prize list.remove(0);System.out.println(getName() 又产生了一个 prize 元大奖);}}try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}}}
}测试类
import java.util.ArrayList;
import java.util.Collections;public class Test {public static void main(String[] args) {/*有一个抽奖池,该抽奖池中存放了奖励的金额,该抽奖池中的奖项为 {10,5,20,50,100,200,500,800,2,80,300,700};创建两个抽奖箱(线程)设置线程名称分别为“抽奖箱1”“抽奖箱2”随机从抽奖池中获取奖项元素并打印在控制台上,格式如下:每次抽出一个奖项就打印一个(随机)抽奖箱1 又产生了一个 10 元大奖抽奖箱1 又产生了一个 100 元大奖抽奖箱1 又产生了一个 200 元大奖抽奖箱1 又产生了一个 800 元大奖抽奖箱2 又产生了一个 700 元大奖.....*///创建奖池ArrayListInteger list new ArrayList();Collections.addAll(list,10,5,20,50,100,200,500,800,2,80,300,700);//创建线程MyThread t1 new MyThread(list);MyThread t2 new MyThread(list);//设置名字t1.setName(抽奖箱1);t2.setName(抽奖箱2);//启动线程t1.start();t2.start();}
}6、多线程练习6多线程统计并求最大值 示例代码一在练习5的基础上进行修改
package com.yaqi.test6case1;import java.util.ArrayList;
import java.util.Collections;public class MyThread extends Thread {ArrayListInteger list;public MyThread(ArrayListInteger list) {this.list list;}//线程一static ArrayListInteger list1 new ArrayList();//线程二static ArrayListInteger list2 new ArrayList();Overridepublic void run() {while (true) {synchronized (MyThread.class) {if (list.size() 0) {if(抽奖箱1.equals(getName())){System.out.println(抽奖箱1 list1);}else {System.out.println(抽奖箱2 list2);}break;} else {//继续抽奖Collections.shuffle(list);int prize list.remove(0);if(抽奖箱1.equals(getName())){list1.add(prize);}else {list2.add(prize);}}}try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}}}
}示例代码二升级版--线程栈示例一可以用但不好
改进后这里只需要一个ArrayList就搞定了 package com.yaqi.test6case2;import java.util.ArrayList;
import java.util.Collections;public class MyThread extends Thread {ArrayListInteger list;public MyThread(ArrayListInteger list) {this.list list;}Overridepublic void run() {ArrayListInteger boxList new ArrayList();//1 //2while (true) {synchronized (MyThread.class) {if (list.size() 0) {System.out.println(getName() boxList);break;} else {//继续抽奖Collections.shuffle(list);int prize list.remove(0);boxList.add(prize);}}try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}}}
}测试类
package com.yaqi.test6case2;import java.util.ArrayList;
import java.util.Collections;public class Test {public static void main(String[] args) {/*有一个抽奖池,该抽奖池中存放了奖励的金额,该抽奖池中的奖项为 {10,5,20,50,100,200,500,800,2,80,300,700};创建两个抽奖箱(线程)设置线程名称分别为“抽奖箱1”“抽奖箱2”随机从抽奖池中获取奖项元素并打印在控制台上,格式如下:每次抽的过程中不打印抽完时一次性打印(随机) 在此次抽奖过程中抽奖箱1总共产生了6个奖项。分别为10,20,100,500,2,300最高奖项为300元总计额为932元在此次抽奖过程中抽奖箱2总共产生了6个奖项。分别为5,50,200,800,80,700最高奖项为800元总计额为1835元*///创建奖池ArrayListInteger list new ArrayList();Collections.addAll(list,10,5,20,50,100,200,500,800,2,80,300,700);//创建线程MyThread t1 new MyThread(list);MyThread t2 new MyThread(list);//设置名字t1.setName(抽奖箱1);t2.setName(抽奖箱2);//启动线程t1.start();t2.start();}
}示例二内存图讲解
每个线程都有自己独立的空间 7、多线程练习7多线程之间的比较 示例代码难点在于如何获取两个线程中的最大值★
调用多线程的第三种方式Callable来实现可以返回结果
MyCallable.java
package com.yaqi.test7;import java.util.ArrayList;
import java.util.Collections;
import java.util.concurrent.Callable;public class MyCallable implements CallableInteger {ArrayListInteger list;public MyCallable(ArrayListInteger list) {this.list list;}Overridepublic Integer call() throws Exception {ArrayListInteger boxList new ArrayList();//1 //2while (true) {synchronized (MyCallable.class) {if (list.size() 0) {System.out.println(Thread.currentThread().getName() boxList);break;} else {//继续抽奖Collections.shuffle(list);int prize list.remove(0);boxList.add(prize);}}Thread.sleep(10);}//把集合中的最大值返回if(boxList.size() 0){return null;}else{return Collections.max(boxList);}}
} package com.yaqi.test7;import java.util.ArrayList;
import java.util.Collections;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;public class Test {public static void main(String[] args) throws ExecutionException, InterruptedException {/*有一个抽奖池,该抽奖池中存放了奖励的金额,该抽奖池中的奖项为 {10,5,20,50,100,200,500,800,2,80,300,700};创建两个抽奖箱(线程)设置线程名称分别为 抽奖箱1, 抽奖箱2随机从抽奖池中获取奖项元素并打印在控制台上,格式如下:在此次抽奖过程中抽奖箱1总共产生了6个奖项分别为10,20,100,500,2,300最高奖项为300元总计额为932元在此次抽奖过程中抽奖箱2总共产生了6个奖项分别为5,50,200,800,80,700最高奖项为800元总计额为1835元在此次抽奖过程中,抽奖箱2中产生了最大奖项,该奖项金额为800元核心逻辑获取线程抽奖的最大值看成是线程运行的结果以上打印效果只是数据模拟,实际代码运行的效果会有差异*///创建奖池ArrayListInteger list new ArrayList();Collections.addAll(list,10,5,20,50,100,200,500,800,2,80,300,700);//创建多线程要运行的参数对象MyCallable mc new MyCallable(list);//创建多线程运行结果的管理者对象//线程一FutureTaskInteger ft1 new FutureTask(mc);//线程二FutureTaskInteger ft2 new FutureTask(mc);//创建线程对象Thread t1 new Thread(ft1);Thread t2 new Thread(ft2);//设置名字t1.setName(抽奖箱1);t2.setName(抽奖箱2);//开启线程t1.start();t2.start();Integer max1 ft1.get();Integer max2 ft2.get();System.out.println(max1);System.out.println(max2);//在此次抽奖过程中,抽奖箱2中产生了最大奖项,该奖项金额为800元if(max1 null){System.out.println(在此次抽奖过程中,抽奖箱2中产生了最大奖项,该奖项金额为max2元);}else if(max2 null){System.out.println(在此次抽奖过程中,抽奖箱1中产生了最大奖项,该奖项金额为max1元);}else if(max1 max2){System.out.println(在此次抽奖过程中,抽奖箱1中产生了最大奖项,该奖项金额为max1元);}else if(max1 max2){System.out.println(在此次抽奖过程中,抽奖箱2中产生了最大奖项,该奖项金额为max2元);}else{System.out.println(两者的最大奖项是一样的);}}
}8、多线程练习8多线程阶段大作业
九、线程池
1、吃饭买碗的故事
1.1、问题
1.2、解决方案
买个碗柜买了碗之后不摔存入碗柜中
2、以前写多线程的弊端
3、线程池的核心原理
当有新的任务出现且线程池线程不足时会新建线程以满足需求其中最大线程的数量可以自行设置 4、线程池的代码实现 4.1、Executors工具类
示例代码
MyRunnable.java:
package com.yaqi.a01threadpool1;public class MyRunnable implements Runnable{Overridepublic void run() {for (int i 1; i 100; i) {System.out.println(Thread.currentThread().getName() --- i);}}
}测试类
package com.yaqi.a01threadpool1;import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class MyThreadPoolDemo {public static void main(String[] args) throws InterruptedException {/*public static ExecutorService newCachedThreadPool() 创建一个没有上限的线程池public static ExecutorService newFixedThreadPool (int nThreads) 创建有上限的线程池*///1.获取线程池对象ExecutorService pool1 Executors.newFixedThreadPool(3);//2.提交任务pool1.submit(new MyRunnable());pool1.submit(new MyRunnable());pool1.submit(new MyRunnable());pool1.submit(new MyRunnable());pool1.submit(new MyRunnable());pool1.submit(new MyRunnable());//3.销毁线程池//pool1.shutdown();}
}4.2、线程复用示例
package com.yaqi.a02threadpool2;import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;public class MyThreadPoolDemo1 {public static void main(String[] args){/*ThreadPoolExecutor threadPoolExecutor new ThreadPoolExecutor(核心线程数量,最大线程数量,空闲线程最大存活时间,任务队列,创建线程工厂,任务的拒绝策略);参数一核心线程数量 不能小于0参数二最大线程数 不能小于0最大数量 核心线程数量参数三空闲线程最大存活时间 不能小于0参数四时间单位 用TimeUnit指定参数五任务队列 不能为null参数六创建线程工厂 不能为null参数七任务的拒绝策略 不能为null*/ThreadPoolExecutor pool new ThreadPoolExecutor(3, //核心线程数量能小于06, //最大线程数不能小于0最大数量 核心线程数量60,//空闲线程最大存活时间TimeUnit.SECONDS,//时间单位new ArrayBlockingQueue(3),//任务队列Executors.defaultThreadFactory(),//创建线程工厂new ThreadPoolExecutor.AbortPolicy()//任务的拒绝策略);}
}
4.3、创建一个有上限的线程池 5、自定义线程池ThreadPoolExecutor 5.1、任务拒绝策略
以下面示例为例它会将任务4抛弃将任务10加入
5.2、代码实现
package com.yaqi.a02threadpool2;import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;public class MyThreadPoolDemo1 {public static void main(String[] args){/*ThreadPoolExecutor threadPoolExecutor new ThreadPoolExecutor(核心线程数量,最大线程数量,空闲线程最大存活时间,任务队列,创建线程工厂,任务的拒绝策略);参数一核心线程数量 不能小于0参数二最大线程数 不能小于0最大数量 核心线程数量参数三空闲线程最大存活时间 不能小于0参数四时间单位 用TimeUnit指定参数五任务队列 不能为null参数六创建线程工厂 不能为null参数七任务的拒绝策略 不能为null*/ThreadPoolExecutor pool new ThreadPoolExecutor(3, //核心线程数量能小于06, //最大线程数不能小于0最大数量 核心线程数量60,//空闲线程最大存活时间TimeUnit.SECONDS,//时间单位new ArrayBlockingQueue(3),//任务队列Executors.defaultThreadFactory(),//创建线程工厂new ThreadPoolExecutor.AbortPolicy()//任务的拒绝策略);}
} 5.3、小结
6、最大并行数
6.1、什么是最大并行数 6.2、向Java虚拟机返回可用处理器的数目 7、线程池多大才合适 可以通过thread dump来计算CPU的计算时间和等待时间 十、多线程的额外扩展内容准备面试时可以再突击学习资料可见《多线程额外扩展.md》