多线程——

目录

一、为什么要有多线程?

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、细节1:synchronized要写在循环的里面​编辑

4.2、细节2:synchronized中的锁对象一定是唯一的

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的对象(作用管理多线程运行的结果)FutureTask<Integer> 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 Callable<Integer> {@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-X(X序号,从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、细节1:synchronized要写在循环的里面

4.2、细节2:synchronized中的锁对象一定是唯一的

 

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{ArrayBlockingQueue<String> queue;public Cook(ArrayBlockingQueue<String> 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{ArrayBlockingQueue<String> queue;public Foodie(ArrayBlockingQueue<String> 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.创建阻塞队列的对象ArrayBlockingQueue<String> 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 {ArrayList<Integer> list;public MyThread(ArrayList<Integer> 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 元大奖.....*///创建奖池ArrayList<Integer> 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 {ArrayList<Integer> list;public MyThread(ArrayList<Integer> list) {this.list = list;}//线程一static ArrayList<Integer> list1 = new ArrayList<>();//线程二static ArrayList<Integer> 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 {ArrayList<Integer> list;public MyThread(ArrayList<Integer> list) {this.list = list;}@Overridepublic void run() {ArrayList<Integer> 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元*///创建奖池ArrayList<Integer> 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 Callable<Integer> {ArrayList<Integer> list;public MyCallable(ArrayList<Integer> list) {this.list = list;}@Overridepublic Integer call() throws Exception {ArrayList<Integer> 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元核心逻辑:获取线程抽奖的最大值(看成是线程运行的结果)以上打印效果只是数据模拟,实际代码运行的效果会有差异*///创建奖池ArrayList<Integer> list = new ArrayList<>();Collections.addAll(list,10,5,20,50,100,200,500,800,2,80,300,700);//创建多线程要运行的参数对象MyCallable mc = new MyCallable(list);//创建多线程运行结果的管理者对象//线程一FutureTask<Integer> ft1 = new FutureTask<>(mc);//线程二FutureTask<Integer> 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》

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/690006.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

为什么有的代理IP速度比较慢?

“为什么有的IP代理速度比较慢&#xff1f;”随着数字化时代的不断发展&#xff0c;代理服务成为了许多网络操作的关键环节。然而&#xff0c;有时我们可能会遇到IP代理速度慢的问题&#xff0c;这可能会对我们的网络操作产生影响。让我们一起揭开这个谜团&#xff0c;探寻其中…

EMQX Enterprise 5.4 发布:OpenTelemetry 分布式追踪、OCPP 网关、Confluent 集成支持

EMQX Enterprise 5.4.0 版本已正式发布&#xff01; 新版本提供 OpenTelemetry 分布式追踪与日志集成功能&#xff0c;新增了开放充电协议 OCPP 协议接入能力&#xff0c;并为数据集成添加了 Confluent 支持。此外&#xff0c;新版本还进行了多项改进以及 BUG 修复&#xff0c…

AI提示工程实战:从零开始利用提示工程学习应用大语言模型【文末送书-19】

文章目录 背景什么是提示工程&#xff1f;从零开始&#xff1a;准备工作设计提示调用大语言模型 实际应用示例文字创作助手代码生成持续优化与迭代数据隐私与安全性可解释性与透明度总结 AI提示工程实战&#xff1a;从零开始利用提示工程学习应用大语言模型【文末送书-19】⛳粉…

SpringMVC 的参数绑定之list集合、Map

标签中name属性的值就是pojo类的属性名 参数绑定4 list [对象] <form action"teaupd.do" method"post"> <c:forEach items"${list}" var"tea" varStatus "status"> 教师编号&#xff1a;<input…

希尔排序算法

目录 ShellSort希尔排序 整体思路 图解分析 【1】预排序 单组排序 多组并排 【2】直接插入排序 关于gap取值 总代码实现 时间复杂度 ShellSort希尔排序 希尔排序法又称缩小增量法。 希尔排序法的基本思想是&#xff1a;先选定一个整数&#xff0c;把待排序文件中所有…

Vue3快速上手(七) ref和reactive对比

一、ref和reactive对比 表格形式更加直观吧&#xff1a; 项目refreactive是否支持基本类型支持不支持是否支持对象类型支持支持对象类型是否支持属性直接赋值不支持&#xff0c;需要.value支持是否支持直接重新分配对象支持&#xff0c;因为操作的.value不支持&#xff0c;需…

120 Linux C++ 通讯架构实战 nginx整体结构,nginx进程模型,nginx调整worker进程数量,nginx重载配置文件,热升级,关闭

一 nginx整体结构 1.1 master进程和worker进程概览&#xff08;父子关系&#xff09; 启动nginx&#xff0c;看到了master进程和 worker 进程。 ps -ef | grep nginx 第一列&#xff1a;进程所属的用户id 第二列&#xff1a;进程ID&#xff0c;也叫做PID&#xff0c;用来唯…

@arco.design Modal renderContent 增加样式

方式一&#xff1a;通过 h 函数 import { h } from vueMessage.error({content: () > {return h(div, {}, [手机号 , h(span, { style: { color: red } }, staffPhone), 已存在])}, })方式二&#xff1a;通过 jsx 方式 注意&#xff1a;lang 需要改为 jsx 或者 tsx <s…

OSQP文档学习

OSQP官方文档 1 QSQP简介 OSQP求解形式为的凸二次规划&#xff1a; x ∈ R n x∈R^n x∈Rn&#xff1a;优化变量 P ∈ S n P∈S^n_ P∈Sn​&#xff1a;半正定矩阵 特征 &#xff08;1&#xff09;高效&#xff1a;使用了一种自定义的基于ADMM的一阶方法&#xff0c;只需…

关于Sora的一些紧迫问题...

OpenAI Sora 概述 OpenAI最新的创新&#xff0c;Sora&#xff0c;在人工智能领域开辟了新的天地。Sora是一个文本到视频的扩散模型&#xff0c;可以将文本描述转化为逼真的视频内容。它解决了一个重大的技术挑战&#xff0c;即在视频中保持主体的一致性&#xff0c;即使它们暂…

Java 线程池的基本操作

Java 线程池的基本操作 package com.zhong.thread.threadpool;import java.util.concurrent.*;/*** ClassName : ThreadPool* Description : 线程池的基本操作* Author : zhx* Date: 2024-02-19 18:03*/ public class ThreadPool {public static void main(String[] args) {// …

C语言每日一题(59)左叶子之和

题目链接 力扣网404 左叶子之和 题目描述 给定二叉树的根节点 root &#xff0c;返回所有左叶子之和。 示例 1&#xff1a; 输入: root [3,9,20,null,null,15,7] 输出: 24 解释: 在这个二叉树中&#xff0c;有两个左叶子&#xff0c;分别是 9 和 15&#xff0c;所以返回 2…

基于SpringBoot的高校竞赛管理系统

基于SpringBoot的高校竞赛管理系统的设计与实现~ 开发语言&#xff1a;Java数据库&#xff1a;MySQL技术&#xff1a;SpringBootMyBatis工具&#xff1a;IDEA/Ecilpse、Navicat、Maven 系统展示 主页 个人中心 管理员界面 老师界面 摘要 高校竞赛管理系统是为了有效管理学校…

K8s进阶之路-命名空间级-服务发现 :

服务发现&#xff1a; Service&#xff08;东西流量&#xff09;&#xff1a;集群内网络通信、负载均衡&#xff08;四层负载&#xff09;内部跨节点&#xff0c;节点与节点之间的通信&#xff0c;以及pod与pod之间的通信&#xff0c;用Service暴露端口即可实现 Ingress&#…

Vscode python pyside6 制作视频播放器

一、界面如下 包含控件 qcombox、qtablewidget、qpushbotton、qverticalslider 二、运行代码 media_player.py import sysfrom PySide6 import QtWidgets from PySide6.QtWidgets import * from PySide6.QtMultimedia import * from PySide6.QtMultimediaWidgets import QVi…

林浩然与杨凌芸的Java List大冒险

林浩然与杨凌芸的Java List大冒险 Lin Haoran and Yang Lingyun’s Java List Adventure 在一个阳光明媚的日子&#xff0c;程序员界的“侠客”林浩然和他那聪明伶俐的同事兼好友杨凌芸正在Java王国里进行一场别开生面的大冒险。这次他们的目标是征服两个强大的List家族成员——…

图论之dfs与bfs的练习

dfs--深度优选搜索 bfs--广度优先搜索 迷宫问题--dfs 问题&#xff1a; 给定一个n*m的二维迷宫数组其中S是起点&#xff0c;T是终点&#xff0c;*是墙壁&#xff08;无法通过&#xff09;&#xff0c; .是道路 问从起点S出发沿着上下左右四个方向走&#xff0c;能否走到T点&a…

视频监控需求八问:视频智能分析/视频汇聚平台EasyCVR有何特性?

最近TSINGSEE青犀视频在与业内伙伴进行项目合作的过程中&#xff0c;针对安防监控可视化视频管理系统EasyCVR视频融合平台在电信运营商项目中的应用&#xff0c;进行了多方面的项目需求沟通。今天我们就该项目沟通为案例&#xff0c;来具体了解一下用户关心度较高的关于视频智能…

MySql报错:Statement violates GTID consistency: CREATE TABLE ... SELECT.解决

一 解决Statement violates GTID consistency: CREATE TABLE ... SELECT 问题&#xff1a; mysql在备份表时报错Statement violates GTID consistency: CREATE TABLE ... SELECT. CREATE TABLE tmp_fix0219 AS SELECT trade_no,ctime FROM charge_data_log 执行后&#xf…

SNMP——简单网络管理协议

Q 什么SNMP? A:SNMP是广泛应用于TCP/IP网络的网络管理标准协议&#xff0c;该协议能够支持网络管理系统&#xff0c;用以监测连接到网络上的设备是否有任何引起管理上关注的情况。SNMP采用轮询机制&#xff0c;提供最基本的功能集&#xff0c;适合小型、快速、低价格的环境使…