o2o网站建设要多少钱,c 网站开发视频,东莞网络营销推广渠道,知识付费网站制作1. 为什么要有多线程#xff1f;
线程#xff1a;线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中#xff0c;是进程中实际运行单位。
进程#xff1a;进程是程序的基本执行实体。 什么是多线程#xff1f; 有了多线程#xff0c;我们就可以让程序同时做…1. 为什么要有多线程
线程线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中是进程中实际运行单位。
进程进程是程序的基本执行实体。 什么是多线程 有了多线程我们就可以让程序同时做多件事情。 多线程的作用 提高效率 多线程的应用场景 只要你想让多个事件同时运行就需要多线程 比如软件中的耗时操作、所有的聊天软件、所有的服务器。
2. 多线程的两个概念
并发在同一时刻有多个指令在单个 CPU 上交替执行
并行在同一时刻有多个指令在多个 CPU 上同时执行
3. 多线程的实现方式
1. 继承 Thread 类的方法进行实现2. 实现 Runnable 接口的方式进行实现3. 利用 Callable 接口和 Future 接口方式实现多线程实现方式1-代码示例
public class MyThread extends Thread{Overridepublic void run() {// 线程要执行的代码for (int i 0; i 100; i) {System.out.println(getName() hello world);}}
}public class ThreadDemo {public static void main(String[] args) {/*** 多线程的第一种启动方式* 1. 自己定义一个类继承 Thread 类* 2. 重写 run 方法* 3. 创建子类对象并启动线程*/MyThread t1 new MyThread();MyThread t2 new MyThread();t1.setName(线程1);t2.setName(线程2);// 开启线程t1.start();t2.start();}
}多线程实现方式2-代码示例
public class MyRun implements Runnable{Overridepublic void run() {// 线程要执行的代码for (int i 0; i 100; i) {// 获取当前线程对象System.out.println(Thread.currentThread().getName() hello world);}}
}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(线程一);t2.setName(线程二);// 开启线程t1.start();t2.start();}
}多线程实现方式3-代码示例
public class MyCallable implements CallableInteger {Overridepublic Integer call() throws Exception {// 求 1-100 之间的和int sum 0;for (int i 0; i 100; i) {sum sum i;}return sum;}
}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);}
}4. 常见的成员方法
setName currentThread sleep
public class MyThread extends Thread{public MyThread() {}public MyThread(String name) {super(name);}Overridepublic void run() {for (int i 0; i 100; i) {System.out.println(getName() i);}}
}public class ThreadDemo {public static void main(String[] args) throws InterruptedException {/*** void setName(String name) 设置线程的名字构造方法也可以设置名字* 细节* 1、如果我们没有给线程名字线程也有默认的名字的* 格式Thread-XX 序号从 0 开始的* 2、如果我们要给线程设置名字可以用 set 方法进行设置也可以用构造方法设置** static Thread currentThread() 获取当前线程对象* 细节* 当 JVM 虚拟机启动后会自动启动多条线程* 当其中有一条线程就叫做 main 线程* 它的主要作用发就是调用 main 方法并执行里面的代码* 在以前我们写的所有代码其实就是运行在 main 线程当中。* static void sleep(long time) 让线程休眠指定的时间单位为毫秒* 细节* 1、那条线程执行到这个方法那么哪条线程就会停留对应的时间* 2、方法的参数就表示睡眠的时间单位毫秒1秒 1000毫秒* 3、当时间到了之后线程就会自动醒来继续执行下面的其他代码**///setName/*// 1. 创建线程对象MyThread t1 new MyThread(飞机);MyThread t2 new MyThread(坦克);// 2. 开启线程t1.start();t2.start();*/// 哪条线程执行到这个方法此时获取的就是哪条线程的镀锡/*Thread t Thread.currentThread();String name t.getName();System.out.println(name); // main*/// sleep/*System.out.println(111111111111111);Thread.sleep(5000);System.out.println(222222222222222);*/}
}setPriority getPriority
public class MyRunnable implements Runnable{Overridepublic void run() {for (int i 0; i 100; i) {System.out.println(Thread.currentThread().getName() --- i);}}
}public class ThreadDemo {public static void main(String[] args) {/*** setPriority(int newPriority) 设置线程的优先级* final int getPriority() 获取线程的优先级*/// 创建线程要执行的参数对象MyRunnable mr new MyRunnable();// 创建线程对象Thread t1 new Thread(mr, 飞机);Thread t2 new Thread(mr, 坦克);t1.setPriority(1);t2.setPriority(10);t1.start();t2.start();}
}守护线程
public class MyThread1 extends Thread{Overridepublic void run() {for (int i 0; i 10; i) {System.out.println(getName() i);}}
}public class MyThread2 extends Thread{Overridepublic void run() {for (int i 0; i 100; i) {System.out.println(getName() i);}}
}public class ThreadDemo {public static void main(String[] args) {/*** final void setDaemon(boolean on) 设置为守护线程* 细节* 当其他的非守护线程执行完毕之后守护线程就会陆续结束* 通俗易懂* 当女神线程结束了那么备胎也没有存在的必要了*/MyThread1 t1 new MyThread1();MyThread2 t2 new MyThread2();t1.setName(女神);t2.setName(备胎);// 把第二个线程设置为守护线程备胎线程t2.setDaemon(true);t1.start();t2.start();}
}礼让线程
public class MyThread extends Thread{Overridepublic void run() {for (int i 0; i 100; i) {System.out.println(getName() i);// 表示出让当前 CPU 的执行权让出执行权的线程也会重新参与抢夺。Thread.yield();}}
}public class ThreadDemo {public static void main(String[] args) {/*** public static void yield() 出让线程/礼让线程*/MyThread t1 new MyThread();MyThread t2 new MyThread();t1.setName(飞机);t2.setName(坦克);t1.start();t2.start();}
}插入线程
public class MyThread extends Thread{Overridepublic void run() {for (int i 0; i 100; i) {System.out.println(getName() i);}}
}public class ThreadDemo {public static void main(String[] args) throws InterruptedException {/*** public final void join() 插入线程/插队线程*/MyThread t new MyThread();t.setName(土豆);t.start();// 把 t 线程插入到当前线程之前。// t土豆// 当前线程maint.join();// 执行在 main 线程中的方法for (int i 0; i 10; i) {System.out.println(main 线程 i);}}
}线程的生命周期 5. 线程安全问题
public class MyThread extends Thread{// static 表示这个类所有的对象都共享 ticketint ticket 0; // 0 ~ 99Overridepublic void run() {while (true) {if (ticket 100) {try {sleep(100);} catch (InterruptedException e) {e.printStackTrace();}ticket;System.out.println(getName() 正在卖 ticket 张票);} else {break;}}}
}public class ThreadDemo {public static void main(String[] args) {/*** 需求* 某电影院目前正在上映国产大片共 100 张票而它有 3 个窗口卖票请设计一个程序模拟该电影院卖票。*/// 创建线程对象MyThread t1 new MyThread();MyThread t2 new MyThread();MyThread t3 new MyThread();// 起名字t1.setName(窗口一);t2.setName(窗口二);t3.setName(窗口三);// 开启线程t1.start();t2.start();t3.start();}
}上面的代码存在以下问题
超卖线程1、2、3都有可能在同时查看剩余票数时都看到还有可卖的票于是同时执行买票操作。卖出相同的票因为在线程1、2、3都有可能同一时间进行买票操作
同步代码块解决线程安全问题
格式
synchronized (锁) {操作共享数据的代码
}特点1锁默认打开有一个线程进去了锁会自动关闭
特点2里面的代码全部执行完毕线程出来锁自动打开
修改之后的线程代码
public class MyThread extends Thread{// static 表示这个类所有的对象都共享 ticketstatic int ticket 0; // 0 ~ 99// 加 static 保证 obj 是唯一的。锁对象要保证是唯一的static Object obj new Object();Overridepublic void run() {while (true) {// 同步代码块synchronized (obj) { // 这里的 obj 也可以替换成 MyThread.class(MyThread 的字节码文件)因为 MyThread 的字节码文件也是唯一的。 if (ticket 100) {try {sleep(10);} catch (InterruptedException e) {e.printStackTrace();}ticket;System.out.println(getName() 正在卖 ticket 张票);} else {break;}}}}
}注意锁要加在 while 循环的里面如果加在循环的外面某个线程抢到锁后会一直执行循环内的代码直到这个线程把所有的票买完。因为线程抢到票之后就算其它线程也抢到票也只能在循环锁外面等着。
同步方法
格式修饰符 synchronized 返回值类型 方法名(方法参数) {…}
特点1同步方法是锁住方法里面的代码
特点2锁对象不能自己指定
非静态this静态当前类的字节码文件对象
StringBuffer 与 StringBuilder 的线程安全区别 StringBuffer 是线程安全的。因为在 StringBuffer 中有 synchronized 关键字。 而 StringBuilder 则不是线程安全的。 那对 StringBuffer 和 StringBuilder 我们如何选择 代码是单线程的不涉及多线程、线程安全的问题那么选择 StringBuilder 就好了。如果是多线程设计线程安全问题那么可以选择 StringBuffer 。
6. 死锁
6.1 锁 lock 示例代码 // 创建锁对象
Lock lock new ReentrantLock();
// 设置锁
lock.lock();
// 释放锁
lock.unlock();锁的应用 public class MyThread extends Thread{static int ticket 0;// 加 static 使每个线程都用统一把锁static Lock lock new ReentrantLock();Overridepublic void run() {// 1. 循环while (true) {// 2. 同步代码块lock.lock();try {// 3. 判断if (ticket 100) {break;} else {// 4. 判断Thread.sleep(10);ticket;System.out.println(getName() 在卖第 ticket 张票);}} catch (InterruptedException e) {e.printStackTrace();} finally {lock.unlock();}}}
}public class ThreadDemo {public static void main(String[] args) {/*** 需求* 某电影院目前正在上映国产大片共 100 张票而它有 3 个窗口卖票请设计一个程序模拟该电影院卖票。** 利用同步方法完成**/MyThread t1 new MyThread();MyThread t2 new MyThread();MyThread t3 new MyThread();t1.setName(窗口1);t2.setName(窗口2);t3.setName(窗口3);t1.start();t2.start();t3.start();}
}6.2 死锁
死锁示例代码
public class MyThread extends Thread{static Object objA new Object();static Object objB new Object();Overridepublic void run() {// 1. 循环while (true) {if (线程A.equals(getName())) {synchronized (objA) {System.out.println(线程 A 拿到了 A 锁准备拿 B 锁);synchronized (objB) {System.out.println(线程 A 拿到了 B 锁顺利执行完一轮);}}} else if (线程B.equals(getName())) {if (线程B.equals(getName())) {synchronized (objB) {System.out.println(线程 B 拿到了 B 锁准备拿 A 锁);synchronized (objA) {System.out.println(线程 B 拿到了 A 锁顺利执行完一轮);}}}}}}
}public class ThreadDemo {public static void main(String[] args) {/*** 死锁*/MyThread t1 new MyThread();MyThread t2 new MyThread();t1.setName(线程A);t2.setName(线程B);t1.start();t2.start();}
}执行这个代码就会出现死锁的情况。
7. 生产者和消费者
7.1 等待唤醒机制
等待唤醒机制思路Desk、Cook、Foodie
示例代码写法一
public class Desk {/*** 作用控制生产者和消费者的执行*/// 是否有面条 0没有面条 1有面条public static int foodFlog 0;// 总个数public static int count 10;// 锁对象public static Object lock new Object();
}public class Cook extends Thread{Overridepublic void run() {/*** 1. 循环* 2. 同步代码块* 3. 判断共享数据是否到了尾声到了尾声* 4. 判断共享数据是否到了尾声没有到尾声执行核心逻辑*/while (true) {synchronized (Desk.lock) {if (Desk.count 0) {break;} else {// 判断桌子上是否有食物// 如果有就等待if (Desk.foodFlog 1) {try {Desk.lock.wait();} catch (InterruptedException e) {e.printStackTrace();}} else {// 如果没有就制作食物System.out.println(厨师做了一碗面条);// 修改桌子上的食物状态Desk.foodFlog 1;// 叫醒等待的消费者开吃Desk.lock.notifyAll();}}}}}
}public class Foodie extends Thread{Overridepublic void run() {/*** 1. 循环* 2. 同步代码块* 3. 判断共享数据是否到了末尾到了末尾* 4. 判断共享数据是否到了末尾没到末尾执行核心逻辑*/while (true) {synchronized (Desk.lock) {if (Desk.count 0) {break;} else {// 先判断桌子上是否有面条if (Desk.foodFlog 0) {// 如果没有就等待try {Desk.lock.wait(); // 让当前线程跟锁进行绑定} catch (InterruptedException e) {e.printStackTrace();}} else {// 把吃的总数 -1Desk.count--;// 如果有就开吃System.out.println(吃货正在吃面条还能再吃 Desk.count 碗);// 吃完之后唤醒厨师继续做Desk.lock.notifyAll();// 修改桌子的状态Desk.foodFlog 0;}}}}}
}示例代码写法二 — 阻塞队列方式实现
阻塞队列的继承结构
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();}}}
}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();}}}
}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.2 线程的状态 但是再 JVM 里面是没有定义运行状态的
8. 练习
8.1 多线程实现抢红包
public class MyThread extends Thread{// 共享数据// 100 块分成了3个红包static double money 100;static int count 3;// 最小的中奖金额static final double MIN 0;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();double bounds money - (count - 1) * MIN;prize r.nextDouble(bounds);if (prize MIN) {prize MIN;}}// 从 money 中去掉当前中奖的金额money money - prize;// 红包的个数 -1count--;// 本次红包的信息进行打印System.out.println(getName() 抢到了 prize 元);}}}
}public class Test {public static void main(String[] args) {/*** 微信中的抢红包也用了多线程。* 假设100块分成了3个包现在有5个人去抢。* 其中红包是共享数据。* 5个人是5条线程* 打印结果如下* 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(小B);t3.setName(小C);t4.setName(小D);t5.setName(小E);// 启动线程t1.start();t2.start();t3.start();t4.start();t5.start();}
}8.2 多线程实现抽奖
public class MyThread extends Thread{ArrayListInteger list;public MyThread(ArrayListInteger list) {this.list list;}Overridepublic void run() { // 1 // 2// 循环// 同步代码块// 判断// 判断while (true) {synchronized (MyThread.class) {if (list.size() 0) {break;} else {// 继续抽奖Collections.shuffle(list);int price list.remove(0);System.out.println(getName() 又产生了一个 price 元大奖);}}try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}}}
}public class Test {public static void main(String[] args) {/*** 有一个抽奖池,该抽奖池中存放了奖励的金额,该抽奖池中的奖项为{18,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, 18,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();}
}8.3 多线程统计并求最大值解法1
public class MyThread extends Thread{ArrayListInteger list;public MyThread(ArrayListInteger list) {this.list list;}// 线程1static ArrayListInteger list1 new ArrayList();// 线程2static ArrayListInteger list2 new ArrayList();Overridepublic void run() { // 1 // 2// 循环// 同步代码块// 判断// 判断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 price list.remove(0);if (抽奖箱1.equals(getName())) {list1.add(price);} else {list2.add(price);}}}try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}}}
}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, 18,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();}
}8.4 多线程统计并求最大值解法2
public class MyThread extends Thread{ArrayListInteger list;public MyThread(ArrayListInteger list) {this.list list;}Overridepublic void run() {ArrayListInteger boxList new ArrayList();while (true) {synchronized (MyThread.class) {if (list.size() 0) {System.out.println(getName() boxList);break;} else {// 继续抽奖Collections.shuffle(list);int price list.remove(0);boxList.add(price);}}try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}}}
}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, 18,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();}
}8.4 多线程之间的比较
public class MyCallable implements CallableInteger {ArrayListInteger list;public MyCallable(ArrayListInteger list) {this.list list;}Overridepublic Integer call() throws Exception {ArrayListInteger boxList new ArrayList();while (true) {synchronized (MyCallable.class) {if (list.size() 0) {System.out.println(Thread.currentThread().getName() boxList);break;} else {// 继续抽奖Collections.shuffle(list);int price list.remove(0);boxList.add(price);}}Thread.sleep(10);}// 把集合中的最大值返回if (boxList.size() 0) {return null;} else {return Collections.max(boxList);}}
}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元System.out.println(在此次抽奖过程中,抽奖箱(ft1.get()800?t1.getName():t2.getName())中产生了最大奖项,该奖项金额为800元);}
}9. 线程池
9.1 以前写多线程的弊端
弊端1用到线程就得创建弊端2用完之后线程就消失
9.3 线程池主要核心原理
创建一个池子池子中是空的提交任务时池子会创建新的线程对象任务执行完毕线程归还给池子下回再次提交任务时不需要创建新的线程直接复用已有的线程即可。但是如果提交任务的时候池子中没有空闲的线程也无法创建新的线程任务就会排队等待。
9.3 线程池代码实现
创建线程池提交任务所有任务全部执行完毕关闭线程池
newCachedThreadPool 演示
public class MyThreadPoolDemo {public static void main(String[] args) throws InterruptedException {/*** public static ExecutorService newCachedThreadPool() 创建一个没有上限的线程池* public static ExecutorService newFixedThreadPool(int nThreads) 创建有上线的线程池*/// newCachedThreadPool 演示// 1. 获取线程池的对象ExecutorService pool1 Executors.newCachedThreadPool();Thread.sleep(1000);// 2. 提交任务pool1.submit(new MyRunnable());Thread.sleep(1000);pool1.submit(new MyRunnable());Thread.sleep(1000);pool1.submit(new MyRunnable());Thread.sleep(1000);pool1.submit(new MyRunnable());Thread.sleep(1000);pool1.submit(new MyRunnable());// 3. 销毁线程池
// pool1.shutdown();}
}public class MyRunnable implements Runnable{Overridepublic void run() {System.out.println(Thread.currentThread().getName() ---);}
}运行效果可以看到会一直在服用线程池中的线程1。
newFixedThreadPool 演示
public class MyThreadPoolDemo {public static void main(String[] args) throws InterruptedException {/*** public static ExecutorService newCachedThreadPool() 创建一个没有上限的线程池* public static ExecutorService newFixedThreadPool(int nThreads) 创建有上线的线程池*/// newFixedThreadPool 演示// 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());// 3. 销毁线程池
// pool1.shutdown();}
}public class MyRunnable implements Runnable{Overridepublic void run() {for (int i 1; i 100; i) {System.out.println(Thread.currentThread().getName() --- i);}}
}在运行的结果可以看到虽然 new 的线程大于3个但是实际生成的线程只有3个。
或者我们可以使用 DEBUG 的方式查看结果
在长度为3的线程池中创建了4个线程之后线程池的长度为3在外面的等待的任务数为1。
9.4 自定义线程池详解 注意Java 默认的任务拒绝策略是 AbortPolicy默认策略丢弃任务并抛出 PejectdExecutionException 异常
拒接策略 创建自定义线程的构造方法参数解析 自定义线程池小结
当核心线程满时再提交任务就会排队当核心线程满队伍满时会创建临时线程当核心线程满时队伍满临时线程满时会触发任务策略
学习视频https://www.bilibili.com/video/BV1LG4y1T7n2?p1vd_source6108736e361d963b64f872fefb8bc1e7