多线程(十九) 进程 :进程是程序的基本执行实体。
线程 :线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中实际运作单位,简单理解为在应用软件中互相独立,可以同时运行的功能。
有了多线程 ,可让程序同时做多件事情,多线程 可以提高效率,若想让多个事情同时运行就需要用到多线程,比如:软件中的耗时操作,所有的聊天软件,所有的服务器。
并发 :在同一时刻,有多个指令在单个CPU上交替执行。
并行 :在同一时刻,有多个指令在单个CPU上同时执行。
线程的调度
抢占式调度 :指优先让可运行池中优先级高的线程占用CPU,优先级并不绝对,只不过优先级越高,占用CPU的概率越高。
分时调度 :又称非抢占式调度,指让所有线程轮流获得cpu的使用权。
多线程的实现方式 线程是程序中的执行线程,java虚拟机允许应用程序并发地运行多个执行线程。
继承Thread类的方式进行实现
实现Runnable接口的方式进行实现
利用Callable接口和Future接口方式实现
继承Thread类 步骤 :
定义一个类继承Thread
重写run方法
创建子类对象,并启动线程(利用start方法启动,run方法会自动调用)
范例 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 //Test.java public class Test { public static void main(String[] args) { Function f1=new Function(); Function f2=new Function(); f1.setName("线程1"); f2.setName("线程2"); f1.start(); f2.start(); } } //Function.java public class Function extends Thread { @Override public void run() { for (int i = 0; i < 10; i++) { System.out.println(getName()+"执行"); } } } /* 线程2执行 线程2执行 线程2执行 线程2执行 线程1执行 线程2执行 线程1执行 线程2执行 线程2执行 线程2执行 线程2执行 线程2执行 线程1执行 线程1执行 线程1执行 线程1执行 线程1执行 线程1执行 线程1执行 线程1执行 */
实现Runnable接口 步骤 :
定义一个类实现Runnable接口
重写里面的run方法
创建自己类的对象
创建一个Thread类的对象,并开启线程
范例 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 //Test.java public class Test { public static void main(String[] args) { //创建function对象,表示多线程要执行的任务 Function f=new Function(); //创建线程对象 Thread t1=new Thread(f); Thread t2=new Thread(f); //给线程设置名字 t1.setName("线程1"); t2.setName("线程2"); //开启线程 t1.start(); t2.start(); } } //Function.java public class Function implements Runnable { //线程要执行的代码 @Override public void run() { //获取当前线程的对象 Thread t=Thread.currentThread(); for (int i=0;i<10;i++) { System.out.println(t.getName()+"执行"); } } } /* 线程1执行 线程1执行 线程2执行 线程1执行 线程2执行 线程2执行 线程2执行 线程2执行 线程2执行 线程2执行 线程2执行 线程2执行 线程2执行 线程1执行 线程1执行 线程1执行 线程1执行 线程1执行 线程1执行 线程1执行 */
Callable接口和Future接口方式实现 特点 :可获取多线程运行的结果
步骤 :
创建一个类MyCallable类实现Callable接口,其中泛型便是多线程运行的结果类型
重写Call方法(具有返回值,表示多线程运行的结果)
创建MyCallable的对象(表示多线程要执行的任务)
创建FutureTask的对象(作用为管理多线程运行的结果)
创建Thread对象,并启动(表示线程)
范例 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 //MyCallable.java import java.util.concurrent.Callable; public class MyCallable implements Callable<Integer> { @Override public Integer call() throws Exception { int sum=0; for(int i=1;i<=100;i++){ sum+=i; } return sum; } } // import java.util.concurrent.ExecutionException; import java.util.concurrent.FutureTask; public class Test { public static void main(String[] args) throws ExecutionException, InterruptedException { //创建一个类MyCallable实现Callable接口 MyCallable mc=new MyCallable(); //创建FutureTask的对象(作用为管理多线程运行的结果) FutureTask<Integer> ft=new FutureTask<>(mc); //创建线程的对象 Thread t=new Thread(ft); //启动线程 t.start(); //获取多线程运行的结果 Integer integer = ft.get(); System.out.println(integer); } } /* 5050 */
三种方式对比
继承Thread类
优点 :编程比较简单,可直接使用Thread类的方法
缺点 :可扩展性差,不能再继承其他类
实现Runnable接口
优点 :扩展性强,实现该接口的同时还可以继承其他类
缺点 :编程相对复杂,不能直接使用Thread类中的方法
实现Callable接口
优点 :扩展性强,实现该接口的同时还可以继承其他类,可获取多线程运行的结果
缺点 :编程相对复杂,不能直接使用Thread类中的方法
常见成员方法 getName String getName()
说明 :返回此线程的名称,若没有给线程设置名字,线程也是有默认名字的,默认名字格式:Thread-X(X序号,从0开始)
setName void setName(String name)
说明 :设置线程名字(构造方法也可以设置名字)
currentThread static Thread currentThread()
说明 :获取当前线程的对象,当虚拟机启动后,会自动启用多条线程,其中一条叫做main线程,它的作用便是调用main方法,并执行其中代码。
sleep static void sleep(long time)
说明 :让线程休眠指定的时间,单位为毫秒(1秒等于1000毫秒),哪条线程执行到这个方法,那么哪条线程就会在这里停留对应的时间。
setPriority setPriority(int newPriority)
说明 :设置线程的优先级,优先级最高位10,最低为1,默认为5
getPriority final int getPriority()
说明 :获取线程的优先级
setDaemon final void setDaemon(boolean on)
说明 :设置守护线程,当其他非守护线程执行完毕后,守护线程会陆续结束。
yield public static void yield()
说明 :出让线程/礼让线程
join public final void join()
说明 :插入线程/插队线程。表示把调用该方法的线程插入到当前线程之前
范例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 //MyThread.java public class MyThread extends Thread { public MyThread() { } public MyThread(String name) { super(name); } @Override public void run() { try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(getName()); } } //Test.java public class Test { public static void main(String[] args){ //getName默认 MyThread mt1=new MyThread(); mt1.start(); //构造方法设置 MyThread mt2=new MyThread("线程2"); mt2.start(); //通过setName设置后的getName MyThread mt3=new MyThread(); mt3.setName("线程3"); mt3.start(); //currentThread Thread t=Thread.currentThread(); System.out.println(t.getName()); } } /* main 线程2 Thread-0 线程3 */
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 //MyThread.java public class MyThread implements Runnable { @Override public void run() { for (int i=0;i<10;i++) { System.out.println(Thread.currentThread().getName()+":"+i); } } } //Test.java public class Test { public static void main(String[] args){ MyThread mt=new MyThread(); Thread t1=new Thread(mt,"线程1"); Thread t2=new Thread(mt,"线程2"); System.out.println("默认"); System.out.println(t1.getPriority()); System.out.println(t2.getPriority()); System.out.println("修该后"); t1.setPriority(1); t2.setPriority(10); System.out.println(t1.getPriority()); System.out.println(t2.getPriority()); t1.start(); t2.start(); } } /* 默认 5 5 修该后 1 10 线程2:0 线程2:1 线程1:0 线程2:2 线程1:1 线程1:2 线程2:3 线程1:3 线程2:4 线程1:4 线程2:5 线程2:6 线程2:7 线程2:8 线程2:9 线程1:5 线程1:6 线程1:7 线程1:8 线程1:9 */
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 //Test.java public class Test { public static void main(String[] args){ MyThread1 mt1=new MyThread1(); MyThread2 mt2=new MyThread2(); mt1.setName("守护线程"); mt2.setName("非守护线程"); mt1.setDaemon(true); mt1.start(); mt2.start(); } } //MyThread1.java public class MyThread1 extends Thread{ @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println(getName()+i); } } } //MyThread2.java public class MyThread2 extends Thread{ @Override public void run() { for (int i = 0; i < 10; i++) { System.out.println(getName()+i); } } } /* 守护线程0 非守护线程0 非守护线程1 守护线程1 非守护线程2 守护线程2 非守护线程3 非守护线程4 非守护线程5 非守护线程6 非守护线程7 非守护线程8 非守护线程9 守护线程3 守护线程4 守护线程5 守护线程6 守护线程7 */
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 //Test.java public class Test { public static void main(String[] args){ MyThread mt1=new MyThread(); MyThread mt2=new MyThread(); mt1.setName("线程一"); mt2.setName("线程二"); mt1.start(); mt2.start(); } } //MyThread.java public class MyThread extends Thread{ @Override public void run() { for (int i = 0; i < 10; i++) { System.out.println(getName()+i); Thread.yield(); } } } /* 线程一0 线程二0 线程二1 线程一1 线程一2 线程一3 线程二2 线程一4 线程一5 线程二3 线程二4 线程二5 线程一6 线程二6 线程一7 线程二7 线程一8 线程一9 线程二8 线程二9 */
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 //Test.java public class Test { public static void main(String[] args) throws InterruptedException { MyThread mt=new MyThread(); mt.setName("线程一"); mt.start(); mt.join(); for (int i = 0; i < 10; i++) { System.out.println("main线程"+i); } } } //MyThread.java public class MyThread extends Thread{ @Override public void run() { for (int i = 0; i < 10; i++) { System.out.println(getName()+i); Thread.yield(); } } } /* 线程一0 线程一1 线程一2 线程一3 线程一4 线程一5 线程一6 线程一7 线程一8 线程一9 main线程0 main线程1 main线程2 main线程3 main线程4 main线程5 main线程6 main线程7 main线程8 main线程9 */
线程的生命周期 新建状态NEW :创建线程对象。调用start方法后变成就绪状态
就绪状态RUNNABLE :有执行资格,没执行权。不停的抢CPU
阻塞状态BLOCKED :无法获得锁对象
等待状态WAITING :wait方法
计时状态TIMED_WAITING :sleep方法
死亡状态TERMINATED :若run执行完毕,线程死亡,变成垃圾
同步代码块 把操作共享数据的代码锁起来。
特点 :
锁默认打开,有一个线程进去,缩自动关闭
里面的代码全部执行完毕,线程出来,缩自动打开
格式 :
1 2 3 4 synchronized(锁){ 操作共享数据的代码 } //锁对象一定要是唯一的,一般写当前类的字节码文件。
范例 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 //某电影院目前上映的甲电影,共有10张票,有3个窗口售卖,设计一个程序模拟该电影院买票 //Test.java public class Test { public static void main(String[] args) throws InterruptedException { MyThread mt1=new MyThread(); mt1.setName("窗口1"); MyThread mt2=new MyThread(); mt2.setName("窗口2"); MyThread mt3=new MyThread(); mt3.setName("窗口3"); mt1.start(); mt2.start(); mt3.start(); } } //MyThread.java public class MyThread extends Thread{ static int ticketNum=1; @Override public void run() { while (true) { synchronized (MyThread.class) { if (ticketNum <=10) { try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(getName() + "卖出第" + ticketNum + "张票"); ticketNum++; } else { break; } } } } } /* 窗口1卖出第1张票 窗口1卖出第2张票 窗口3卖出第3张票 窗口3卖出第4张票 窗口3卖出第5张票 窗口3卖出第6张票 窗口3卖出第7张票 窗口3卖出第8张票 窗口2卖出第9张票 窗口2卖出第10张票 */
同步方法 就是把synchronized关键字加到方法上。
格式 :
修饰符 synchronized 返回值类型 方法名(方法参数){…}
特点 :
同步方法是锁住方法里面所有的代码
锁对象不能自己指定。
非静态方法:this
静态方法:当前类的字节码文件对象
范例 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 //某电影院目前上映的甲电影,共有10张票,有3个窗口售卖,设计一个程序模拟该电影院买票,同步方法实现 //Test.java 1public class Test { public static void main(String[] args){ MyThread myThread=new MyThread(); Thread t1=new Thread(myThread); Thread t2=new Thread(myThread); Thread t3=new Thread(myThread); t1.setName("窗口1"); t2.setName("窗口2"); t3.setName("窗口3"); t1.start(); t2.start(); t3.start(); } } //MyThread.java public class MyThread implements Runnable{ int ticketNum=0; @Override public void run() { while (true){ if (method()){ break; } } } private synchronized boolean method(){ if(ticketNum==10){ return true; }else { try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } ticketNum++; System.out.println(Thread.currentThread().getName()+ "卖出第" + ticketNum + "张票"); } return false; } } /* 窗口1卖出第1张票 窗口1卖出第2张票 窗口1卖出第3张票 窗口1卖出第4张票 窗口1卖出第5张票 窗口1卖出第6张票 窗口1卖出第7张票 窗口1卖出第8张票 窗口3卖出第9张票 窗口2卖出第10张票 */
lock锁 Lock中提供获得锁 和释放锁 的方法。
void lock(); 获得锁
void unlock(); 释放锁
Lock是接口不能直接实例化,这里采用它的实现类ReentrantLock来实例化。
ReentrantLock的构造方法:
ReentrantLock(); 创建一个ReentrantLock的实例。
范例 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 //某电影院目前上映的甲电影,共有10张票,有3个窗口售卖,设计一个程序模拟该电影院买票,Lock实现 //Test.java public class Test { public static void main(String[] args) throws InterruptedException { MyThread mt1=new MyThread(); mt1.setName("窗口1"); MyThread mt2=new MyThread(); mt2.setName("窗口2"); MyThread mt3=new MyThread(); mt3.setName("窗口3"); mt1.start(); mt2.start(); mt3.start(); } } //MyThread.java import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class MyThread extends Thread{ static int ticketNum=1; static Lock lock=new ReentrantLock(); @Override public void run() { while (true){ lock.lock(); try { if (ticketNum <=100) { Thread.sleep(10); System.out.println(getName() + "卖出第" + ticketNum + "张票"); ticketNum++; } else { break; } } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } } } }
注 :写锁的时候不要让锁嵌套,否则可能会死锁。
等待唤醒机制 方式一 等待唤醒机制 (生产者消费者模式)是多个线程之间的一种协作机制。
思路 :
此处将消费者比喻成吃货,生产者比喻成厨师。
消费者 :消费数据
判断桌子上是否有食物
若无,等待
若有,开吃
吃完后,唤醒厨师继续做
生产者 :生产数据
判断桌子上是否有食物
若有,等待
若无,做饭
把食物放到桌子上
唤醒等待的消费者开吃
常见方法 wait void wait()
说明 :当前线程等待,直到被其他线程唤醒
notify void notify()
说明 :随机唤醒单个线程
notifyAll void notifyAll()
说明 :唤醒所有线程
范例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 //Desk.java public class Desk { public static int count=10;//吃货能吃的饭的数量 public static Object lock=new Object();//锁 public static int state=0;//桌子的状态,若无食物为0,若有食物为1 } //Cookie.java public class Cookie extends Thread { /* 1. 循环 2.同步代码块 3.判断共享数据是否到了尽头(到了尽头) 4.判断共享数据是否到了尽头(没到尽头,执行核心逻辑) */ @Override public void run() { while(true){ synchronized (Desk.lock){ if(Desk.count==0){ break; }else {//判断桌子上是否有食物 if(Desk.state==0){//无食物,厨师做饭 System.out.println("厨师做饭"); //桌子上有了食物,状态设为1 Desk.state=1; //唤醒吃货 Desk.lock.notifyAll(); }else{//若有,则等待 try { Desk.lock.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } } } } //Foodie.java public class Foodie extends Thread{ /* 1. 循环 2.同步代码块 3.判断共享数据是否到了尽头(到了尽头) 4.判断共享数据是否到了尽头(没到尽头,执行核心逻辑) */ @Override public void run() { while (true){ synchronized (Desk.lock){ if (Desk.count==0){ break; }else { //判断桌子是否有食物 if(Desk.state==0){ try {//若无,等待 Desk.lock.wait(); } catch (InterruptedException e) { e.printStackTrace(); } }else{//若有,开吃 //先将饭量-1 Desk.count--; System.out.println("吃货还能吃"+Desk.count+"顿饭"); //吃饭后,桌子上没有事物,状态设为0 Desk.state=0; //唤醒厨师 Desk.lock.notifyAll(); } } } } } } //Test.java public class Test { public static void main(String[] args) { Cookie cookie=new Cookie(); Foodie foodie=new Foodie(); cookie.start(); foodie.start(); } } /* 厨师做饭 吃货还能吃9顿饭 厨师做饭 吃货还能吃8顿饭 厨师做饭 吃货还能吃7顿饭 厨师做饭 吃货还能吃6顿饭 厨师做饭 吃货还能吃5顿饭 厨师做饭 吃货还能吃4顿饭 厨师做饭 吃货还能吃3顿饭 厨师做饭 吃货还能吃2顿饭 厨师做饭 吃货还能吃1顿饭 厨师做饭 吃货还能吃0顿饭 */
方式二 阻塞队列实现等待唤醒机制。
阻塞队列继承结构 阻塞队列实现Iterable,Collection,Queue,BlockingQueue接口,有两个实现类:
ArrayBlockingQueue :底层是数组,有界
LinkedBlockingQueue :底层是链表,无界,但不是真的无界,最大为int最大值
思路 :
此处将消费者比喻成吃货,生产者比喻成厨师。
厨师
构造方法中接收一个阻塞队列对象
run方法中循环向阻塞队列添加食物
打印添加结果
吃货
构造方法中接收一个阻塞队列对象
run方法中循环获取阻塞队列的食物
打印获取结果
核心方法 put(anObject)
将参数放入队列,若放不进去会堵塞。
take()
取出第一个数据,取不到会堵塞。
注 :put,take方法里本身就有锁
范例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 //Test.java import java.util.concurrent.ArrayBlockingQueue; public class Test { public static void main(String[] args){ ArrayBlockingQueue<String> abq=new ArrayBlockingQueue<>(1);//创建阻塞对象,并设置容量 Cookie cookie=new Cookie(abq); Foodie foodie=new Foodie(abq); cookie.start(); foodie.start(); } } //Cookie.java import java.util.concurrent.ArrayBlockingQueue; public class Cookie extends Thread{ ArrayBlockingQueue<String> abq; public Cookie(ArrayBlockingQueue<String> abq){ this.abq=abq; } @Override public void run() { while (true){ try { abq.put("食物"); System.out.println("厨师放食物"); } catch (InterruptedException e) { e.printStackTrace(); } } } } //Foodie.java import java.util.concurrent.ArrayBlockingQueue; public class Foodie extends Thread{ ArrayBlockingQueue<String> abq; public Foodie(ArrayBlockingQueue<String> abq){ this.abq=abq; } @Override public void run() { while (true){ try { String s=abq.take(); System.out.println(s); } catch (InterruptedException e) { e.printStackTrace(); } } } }
练习 例1 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 /* 假设:100元分成了三个红包(均为整数),有5人去抢 其中红包是共享数据。 5个人5条线程 打印结果如下: XXX抢到了XX元 XXX抢到了XX元 XXX抢到了XX元 XXX没抢到 XXX没抢到 */ //Test.java public class Test { public static void main(String[] args) { RedPackage rp1=new RedPackage(); RedPackage rp2=new RedPackage(); RedPackage rp3=new RedPackage(); RedPackage rp4=new RedPackage(); RedPackage rp5=new RedPackage(); rp1.setName("线程1"); rp2.setName("线程2"); rp3.setName("线程3"); rp4.setName("线程4"); rp5.setName("线程5"); rp1.start(); rp2.start(); rp3.start(); rp4.start(); rp5.start(); } } //RedPackage.java import java.util.Random; public class RedPackage extends Thread{ static int sum=100; static int count=3; @Override public void run() { synchronized (RedPackage.class){ if(count==0){ System.out.println(getName()+"没抢到"); }else{ if(count==1){ int money=sum; System.out.println(getName()+"抢到了"+money+"元"); }else{ Random r=new Random(); int money=r.nextInt(sum-(count-1))+1; System.out.println(getName()+"抢到了"+money+"元"); sum=sum-money; } count--; } } } } /* 线程1抢到了3元 线程5抢到了40元 线程4抢到了57元 线程3没抢到 线程2没抢到 */
例2 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 /* 有一个抽奖池,该抽奖池中存放了奖励的金额,该抽奖池中的奖项为10,5,20,50,100,200,500,800,2,80,300,700 创建两个抽奖箱(线程)设置线程名分别为“抽奖箱1”和“抽奖箱2” 随机从抽奖池中获取奖项元素并打印在控制台上,格式如下: 在此次抽奖过程中,抽奖箱2共产生6个奖项 分别为:800,10,80,500,700,2最高奖项为800元,总计额为2092元 在此次抽奖过程中,抽奖箱1共产生6个奖项 分别为:5,100,50,300,20,200最高奖项为300元,总计额为675元 在此次抽奖过程中,抽奖箱2中产生最大奖项,该奖项金额为800元 */ //Lottery.java import java.util.ArrayList; import java.util.Collections; import java.util.concurrent.Callable; public class Lottery implements Callable<Integer> { ArrayList<Integer> moneyBox; public Lottery(ArrayList<Integer> moneyBox){ this.moneyBox=moneyBox; } @Override public Integer call() throws Exception { ArrayList<Integer> list=new ArrayList<>(); int sum=0; while (true){ synchronized (Lottery.class) { if (moneyBox.size() == 0) { print(list,Collections.max(list),sum); break; } else { Collections.shuffle(moneyBox); int money = moneyBox.remove(0); list.add(money); sum+=money; } } Thread.sleep(10); } if(list.size()==0){ return null; } return Collections.max(list); } private static void print(ArrayList<Integer> list,int max,int sum){ System.out.println("在此次抽奖过程中,"+Thread.currentThread().getName()+"共产生"+list.size()+"个奖项"); System.out.print("分别为:"); for(int i=0;i<list.size();i++){ System.out.print(list.get(i)); if(i< list.size()-1){ System.out.print(","); } } System.out.println("最高奖项为"+max+"元,总计额为"+sum+"元"); } } //Test.java 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 { ArrayList<Integer> moneyBox=new ArrayList<>(); Collections.addAll(moneyBox,10,5,20,50,100,200,500,800,2,80,300,700); Lottery lottery=new Lottery(moneyBox); FutureTask<Integer> ft1=new FutureTask<>(lottery); FutureTask<Integer> ft2=new FutureTask<>(lottery); Thread t1=new Thread(ft1); Thread t2=new Thread(ft2); t1.setName("抽奖箱1"); t2.setName("抽奖箱2"); t1.start(); t2.start(); int money; String name; if(ft1.get()>ft2.get()){ money=ft1.get(); name=t1.getName(); }else{ money=ft2.get(); name=t2.getName(); } System.out.println("在此次抽奖过程中,"+name+"产生最大值,最大值金额为"+money); } } /* 在此次抽奖过程中,抽奖箱1共产生6个奖项 分别为:80,100,50,5,10,700最高奖项为700元,总计额为945元 在此次抽奖过程中,抽奖箱2共产生6个奖项 分别为:500,200,20,300,2,800最高奖项为800元,总计额为1822元 在此次抽奖过程中,抽奖箱2产生最大值,最大值金额为800 */
线程池 核心原理
创建一个池子,池子中是空的
提交任务时,池子会创建新的线程对象,执行完毕后,线程归还给池子,下次再提交任务时,不需创建新线程,直接复用已有的线程即可
若提交任务时,池子中没有空闲线程,也无法创建新的线程,任务便会排队等待
代码实现步骤 :
创建线程池
提交任务
所有的任务全部执行完毕,关闭线程池
Executors Executors :线程池的工具类通过调用方法返回不同类型的线程池对象。
newCachedThreadPool 格式 :
public static ExecutorsService newCachedThreadPool()
说明 :创建一个几乎没有上限的线程池
newFixedThreadPool 格式 :
public static ExecutorsService newFixedThreadPool(int nThreads)
说明 :创建一个有上限的线程池
范例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 //Test.java import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class Test { public static void main(String[] args) throws InterruptedException { //1.获取线程池对象 ExecutorService pool= Executors.newCachedThreadPool(); //2.提交任务 pool.submit(new MyRunnable()); pool.submit(new MyRunnable()); pool.submit(new MyRunnable()); pool.submit(new MyRunnable()); pool.submit(new MyRunnable()); //3.销毁线程池 pool.shutdown(); } } //MyRunnable.java public class MyRunnable implements Runnable { @Override public void run() { System.out.println(Thread.currentThread().getName()); } } /* pool-1-thread-1 pool-1-thread-3 pool-1-thread-5 pool-1-thread-2 pool-1-thread-4 */
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 //MyRunnable.java public class MyRunnable implements Runnable { @Override public void run() { System.out.println(Thread.currentThread().getName()); } } import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; //Test.java public class Test { public static void main(String[] args) throws InterruptedException { //1.获取线程池对象 ExecutorService pool= Executors.newFixedThreadPool(3); //2.提交任务 pool.submit(new MyRunnable()); pool.submit(new MyRunnable()); pool.submit(new MyRunnable()); pool.submit(new MyRunnable()); pool.submit(new MyRunnable()); //3.销毁线程池 pool.shutdown(); } } /* pool-1-thread-2 pool-1-thread-1 pool-1-thread-3 pool-1-thread-1 pool-1-thread-2 */
自定义线程 步骤
创建一个空的池子
有任务提交时,线程池会创建线程去执行任务,执行完毕归还线程。
注 :不断提交任务,会有以下三个临界点
当核心线程满时,在提交任务就会排队
当核心线程满,队满时,会创建临时线程
当核心线程满,队满时,临时线程满时,会触发任务拒绝策略
核心元素(参数)
核心线程数量(不少于0)
线程池中最大线程数量(最大数量>=核心线程数量)
空闲时间(值)(不能小于0)
空闲时间(单位)(用TimeUnit绑定)
阻塞队列(不能为null)
创建线程工厂(不能为null)
要执行的任务过多时的解决方案(不能为null)
范例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.Executors; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; public class Test { public static void main(String[] args){ ThreadPoolExecutor pool=new ThreadPoolExecutor( 3,//核心线程数量(不少于0) 6,//线程池中最大线程数量(最大数量>=核心线程数量) 60,//空闲时间(值)(不能小于0) TimeUnit.SECONDS,//空闲时间(单位)(用TimeUnit绑定) new ArrayBlockingQueue<>(3),//阻塞队列(不能为null) Executors.defaultThreadFactory(),//创建线程工厂(不能为null) new ThreadPoolExecutor.AbortPolicy()//要执行的任务过多时的解决方案(不能为null) ); } }
线程池多大合适 最大并行数 :可用Runtime.getRuntime().availableProcessors()返回当前电脑的最大并行数
1 2 3 CPU密集型运算:即项目中CPU计算居多,线程池大小=最大并行数+1 I/O密集型运算:即项目中文件操作居多,线程池大小=最大并行数*期望CPU利用率*(总时间(CPU计算时间+等待时间)/CPU计算时间)