三种创建线程的方式:
继承Thread类
实现Runnable接口
实现Callable接口(了解)
创建线程方式1:继承Thread类
自定义继承Thread类的线程类
重写run()方法,线程体
创建线程对象,调用start()方法启动线程
注意:两个线程同时执行(异步),这里体现为交替输出
1 //创建线程:继承Thread类,重写run(),调用start() 2 public class Demo01 extends Thread{ 3 @Override 4 public void run() { 5 //run方法线程体 6 for (int i = 0; i < 2000; i++) { 7 System.out.println("aaaaaaaa"); 8 } 9 }10 11 public static void main(String[] args) {12 //新建线程对象13 Demo01 demo01 = new Demo01();14 //调用start()方法,开启线程15 demo01.start();16 for (int i = 0; i < 2000; i++) {17 System.out.println("bbbbbbbbb");18 }19 }20 }
Thread下载网络图片
通过Thread多线程下载网络图片
准备工作:下载commonsio包,导入com.lib,添加到库
1 //Thread练习,实现多线程同步下载网络图片 2 public class Demo02 extends Thread{ 3 private String url; //图片地址 4 private String name;//保存路径 5 //构造器 6 public Demo02(String url,String name){ 7 this.url = url; 8 this.name = name; 9 }10 //下载线程执行体11 @Override12 public void run() {13 WebDownloader webDownloader = new WebDownloader();14 webDownloader.downloader(url,name);15 System.out.println(name+" success");16 }17 18 public static void main(String[] args) {19 //unable to find valid certification path to requested target20 Demo02 t1 = new Demo02("https://","java1.jpg");21 Demo02 t2 = new Demo02("https://","java2.jpg");22 Demo02 t3 = new Demo02("https://","java3.jpg");23 t1.start();24 t2.start();25 t3.start();26 }27 }28 //下载器29 class WebDownloader{30 //下载方法31 public void downloader(String url,String name){32 try {33 //通过FileUtils.copyURLToFile把url转换为文件34 FileUtils.copyURLToFile(new URL(url),new File(name));35 } catch (IOException e) {36 e.printStackTrace();37 }38 }39 }
创建线程方式2:实现Runnable接口
定义MyRunnable类实现Runnable接口
实现run()方法,编写线程体
创建线程对象,调用start()方法启动线程
推荐使用,避免了单继承局限性,方便同一个对象被多个线程使用(多个代理)
1 //创建线程方式2:实现runnable接口,重写run方法,执行线程需要丢入runnable接口实现类,调用start方法 2 public class Demo03 implements Runnable{ 3 @Override 4 public void run() { 5 for (int i = 0; i < 20; i++) { 6 System.out.println("ccc"); 7 } 8 } 9 10 public static void main(String[] args) {11 //创建runnable接口的实现类对象12 Demo03 demo03 = new Demo03();13 //创建线程对象,通过线程对象来开启线程(代理)14 // Thread thread = new Thread(demo03);15 // thread.start();16 //以上两句可以简写成以下形式:17 new Thread(demo03).start();18 }19 }
例子:龟兔赛跑(使用同一条赛道)
1 //模拟龟兔赛跑 2 public class Demo05 implements Runnable{ 3 4 private static String winner; 5 6 @Override 7 public void run() { 8 for (int i = 0; i <= 100; i++) { 9 //模拟rabbit休息10 if (Thread.currentThread().getName().equals("rabbit") && i%10==0){11 try {12 Thread.sleep(1);13 } catch (InterruptedException e) {14 e.printStackTrace();15 }16 }17 //判断比赛是否结束18 boolean flag = gameOver(i);19 if (flag){20 break;21 }22 23 System.out.println(Thread.currentThread().getName()+"跑了"+i+"步");24 }25 }26 //判断是否完成比赛27 private boolean gameOver(int steps){28 if (winner != null){29 return true;30 } else {31 if (steps >= 100){32 winner = Thread.currentThread().getName();33 System.out.println(winner+" is winner");34 return true;35 }36 }37 return false;38 }39 40 public static void main(String[] args) {41 //启动线程42 Demo05 demo05 = new Demo05(); //创建赛道43 new Thread(demo05,"turtle").start();44 new Thread(demo05,"rabbit").start();45 }46 }
创建线程方式3:实现Callable接口
实现Callable接口需要返回值类型
重写call方法,抛出异常
创建目标对象
创建执行服务
提交执行
获取结果
关闭服务
例子:Callable改造下载网络图片
1 //线程创建方式3:实现callable接口 2 //callable的好处:1、可以定义返回值 2、可以抛出异常 3 //这里下载会出现异常,目前看来是证书的原因,尚未解决 4 public class TestCallable implements Callable<Boolean> { 5 6 private String url; //图片地址 7 private String name;//保存路径 8 //构造器 9 public TestCallable(String url,String name){10 this.url = url;11 this.name = name;12 }13 //下载线程执行体14 @Override15 public Boolean call() {16 WebDownloader webDownloader = new WebDownloader();17 webDownloader.downloader(url,name);18 System.out.println(name+" success");19 return true;20 }21 22 public static void main(String[] args) throws Exception{23 //unable to find valid certification path to requested target24 TestCallable t1 = new TestCallable("https://pic.cnblogs.com/avatar/2281056/20210224183025.png","java1.jpg");25 TestCallable t2 = new TestCallable("https://pic.cnblogs.com/avatar/2281056/20210224183025.png","java2.jpg");26 TestCallable t3 = new TestCallable("https://pic.cnblogs.com/avatar/2281056/20210224183025.png","java3.jpg");27 28 //创建执行服务29 ExecutorService ser = Executors.newFixedThreadPool(3);30 //提交执行(执行call方法)31 Future<Boolean> r1 = ser.submit(t1);32 Future<Boolean> r2 = ser.submit(t2);33 Future<Boolean> r3 = ser.submit(t3);34 //获取结果35 boolean rs1 = r1.get();36 boolean rs2 = r2.get();37 boolean rs3 = r3.get();38 //关闭服务39 ser.shutdownNow();40 }41 }42 43 //下载器44 class WebDownloader{45 //下载方法46 public void downloader(String url,String name){47 try {48 //通过FileUtils.copyURLToFile把url转换为文件49 FileUtils.copyURLToFile(new URL(url),new File(name));50 } catch (IOException e) {51 e.printStackTrace();52 }53 }54 }
静态代理模式
Thread类代理了Runnable接口
静态代理:线程底部的实现原理
1 //静态代理模式:1、真实对象和代理对象都要实现同一个接口 2、代理对象要代理真实角色 2 //代理的好处:代理对象可以完成很多真实对象做不了的事情,真实对象只需要专注自己的任务 3 public class StacticProxy { 4 5 public static void main(String[] args) { 6 You you = new You();//真实角色 7 WeddingCompany weddingCompany = new WeddingCompany(you); 8 weddingCompany.HappyMarry(); 9 //也可以通过以下方式实现10 //new WeddingCompany(new You()).HappyMarry();11 }12 13 }14 15 //定义接口16 interface Marry{17 void HappyMarry();18 }19 //继承接口并重写方法20 //结婚的人,真实角色21 class You implements Marry{22 @Override23 public void HappyMarry() {24 System.out.println("name");25 }26 }27 //婚庆公司,代理角色28 class WeddingCompany implements Marry{29 //代理目标,真实角色30 private Marry target;31 32 public WeddingCompany(Marry target) {33 this.target = target;34 }35 36 @Override37 public void HappyMarry() {38 before();39 this.target.HappyMarry();40 after();41 }42 43 private void after() {44 System.out.println("cleanup");45 }46 47 private void before() {48 System.out.println("setup");49 }50 }
Lambda表达式
使用lambda表达式的优点
避免匿名内部类定义过多
让代码更简洁,只保留核心的逻辑
函数式接口
只包含唯一一个抽象方法的接口就是一个函数式接口,可以通过lambda表达式创建接口的对象
只有函数式接口才可以使用lambda表达式
推导lambda表达式
1 //推导lambda表达式 2 //new Thread( (参数)->System.out.println("xxxx") ).start(); 3 public class TestLambda01 { 4 5 //3.静态内部类 6 static class Like2 implements ILike{ 7 @Override 8 public void lambda() { 9 System.out.println("Lambda2");10 }11 }12 13 public static void main(String[] args) {14 ILike like = new Like();15 like.lambda();16 like = new Like2();17 like.lambda();18 19 //4.局部内部类20 class Like3 implements ILike{21 @Override22 public void lambda() {23 System.out.println("Lambda3");24 }25 }26 like = new Like3();27 like.lambda();28 29 //5.匿名内部类(没有类的名称,必须借助接口或父类)30 like = new ILike() {31 @Override32 public void lambda() {33 System.out.println("Lambda4");34 }35 };36 like.lambda();37 38 //6.lambda简化(最终形式!)39 like = (/*参数*/) -> {40 System.out.println("Lambda5");41 };42 like.lambda();43 }44 45 }46 47 //1.定义一个函数式接口48 interface ILike{49 void lambda();50 }51 //2.实现类52 class Like implements ILike{53 @Override54 public void lambda() {55 System.out.println("Lambda");56 }57 }
简化lambda表达式
1 //简化lambda表达式 2 public class TestLambda02 { 3 public static void main(String[] args) { 4 //原始lambda表达式 5 Test t1 = null; 6 t1 = (int a)->{ 7 System.out.println("this is " + a); 8 }; 9 t1.speak(10);10 //简化1:简化参数类型(多个参数多行语句时推荐使用!)11 Test t2 = null; 12 t2 = (a)->{13 System.out.println("this is " + a);14 };15 t2.speak(20);16 //简化2:简化括号(只有一个参数时才能简化为以下形式)17 Test t3 = null; 18 t3 = a ->{19 System.out.println("this is " + a);20 };21 t3.speak(30);22 //简化3:简化{}(代码只有一行时才能简化为以下形式)23 Test t4 = null; 24 t4 = a -> System.out.println("this is " + a);25 t4.speak(40);26 }27 }28 //函数式接口29 interface Test{30 void speak(int a);31 }
lambda表达式在多线程中的应用
1 //Runnable接口的源码如下 2 //可以看出Runnable是函数式接口,只有一个run方法,通过lambda表达式可以方便地实现Runnable接口 3 public interface Runnable { 4 /** 5 * When an object implementing interface <code>Runnable</code> is used 6 * to create a thread, starting the thread causes the object's 7 * <code>run</code> method to be called in that separately executing 8 * thread. 9 * <p>10 * The general contract of the method <code>run</code> is that it may11 * take any action whatsoever.12 *13 * @see java.lang.Thread#run()14 */15 public abstract void run();16 }
线程状态
线程方法
方法 说明 setPriority(int newPriority) 改变线程优先级 static void sleep(long millis) 在指定的毫秒数内让当前正在执行的线程休眠 void join() 等待线程终止 static void yield() 暂停当前正在执行的线程对象,并执行其他线程 void interrup() 中断线程 boolean isAlive() 测试线程是否处于活动状态
线程停止
尽量使线程自己停止
1 //测试线程停止stop 2 //1.建议使线程正常停止--->利用次数 3 //2.建议使用标志位--->设置标志位,条件满足则终止 4 //3.不要使用stop或destroy等过时方法 5 public class TestStop implements Runnable{ 6 7 //1.设置标志位 8 private boolean flag = true; 9 10 @Override11 public void run() {12 int i = 0;13 while (flag){14 System.out.println("run...Thread" + i++);15 }16 }17 18 //2.设置公开的方法转换标志位,停止线程19 public void stop(){20 this.flag = false;21 }22 23 public static void main(String[] args) {24 TestStop testStop = new TestStop();25 26 new Thread(testStop).start();27 28 for (int i = 0; i < 1000; i++) {29 System.out.println("main" + i);30 if (i == 900){31 //3.调用stop方法切换标志位,使线程停止32 testStop.stop();33 System.out.println("Thread stop");34 }35 }36 }37 38 }
线程休眠
sleep(time)指定当前线程阻塞的毫秒数
sleep存在异常InterruptedException
sleep时间达到后线程进入就绪状态
每个对象都有一个锁,sleep不会释放锁
1 //模拟网络延时:放大问题的发生性 2 //线程不安全:多个线程同时操作了同一个对象(多个人抢到同一张票) 3 public class TestSleep implements Runnable{ 4 private int ticketNums = 10; 5 @Override 6 public void run() { 7 while (true){ 8 if (ticketNums <= 0) break; 9 //线程sleep方法模拟延时10 try {11 Thread.sleep(200);12 } catch (InterruptedException e) {13 e.printStackTrace();14 }15 //Thread.currentThread().getName() 获取当前线程的名字16 System.out.println(Thread.currentThread().getName()+" got "+ticketNums--);17 }18 }19 20 public static void main(String[] args) {21 Demo04 demo04 = new Demo04();22 23 new Thread(demo04,"user1").start();24 new Thread(demo04,"user2").start();25 new Thread(demo04,"user3").start();26 }27 }28 =============================================================================29 //模拟倒计时30 public class TestSleep2{31 32 public static void countDown() throws InterruptedException{33 int num = 10;34 while (true){35 Thread.sleep(1000);36 System.out.println(num--);37 if (num<0){38 break;39 }40 }41 }42 43 public static void main(String[] args) throws InterruptedException{44 //打印倒计时45 //countDown();46 47 //复习:打印当前系统时间48 Date startTime = new Date(System.currentTimeMillis()); //获取系统当前时间49 while (true){50 try {51 Thread.sleep(1000);52 System.out.println(new SimpleDateFormat("HH:mm:ss").format(startTime)); //SDF时间格式化打印53 startTime = new Date(System.currentTimeMillis()); //更新当前时间54 } catch (InterruptedException e) {55 e.printStackTrace();56 }57 }58 }59 60 }
线程礼让
礼让线程会使让钱正在执行的线程暂停,但不阻塞(从运行态转为就绪态),CPU重新调度
礼让有可能失败,因为只是将线程重新调度,可能会继续执行原本礼让的线程
1 //礼让线程 2 public class TestYield { 3 public static void main(String[] args) { 4 MyYield myYield = new MyYield(); 5 new Thread(myYield,"t1").start(); 6 new Thread(myYield,"t2").start(); 7 } 8 } 9 10 class MyYield implements Runnable{11 @Override12 public void run() {13 System.out.println("Thread " + Thread.currentThread().getName() +"start");14 Thread.yield(); //礼让15 System.out.println("Thread " + Thread.currentThread().getName() +"end");16 }17 }
线程合并
join合并线程,阻塞当前执行的其他线程
1 //线程合并 2 public class TestJoin implements Runnable{ 3 @Override 4 public void run() { 5 for (int i = 0; i < 100; i++) { 6 System.out.println("somebody cut in line " + i + " times"); 7 } 8 } 9 10 public static void main(String[] args) throws InterruptedException {11 TestJoin testJoin = new TestJoin();12 Thread thread = new Thread(testJoin);13 thread.start();14 15 for (int i = 0; i < 200; i++) {16 if (i == 100){17 thread.join();//插队,阻塞主线程18 }19 System.out.println("custom "+ i);20 }21 }22 23 }
查看线程状态
1 public class TestStates { 2 3 public static void main(String[] args) throws InterruptedException { 4 Thread thread = new Thread(()->{ 5 for (int i = 0; i < 5; i++) { 6 try { 7 Thread.sleep(1000); 8 } catch (Exception e) { 9 e.printStackTrace();10 }11 }12 System.out.println("Zzzzzzz");13 });14 15 Thread.State state = thread.getState();16 System.out.println(state); //还没调用start方法,线程处于创建态NEW17 18 thread.start(); //调用start后,进程进入就绪态,准备运行19 state = thread.getState();20 System.out.println(state); //此时线程为运行态RUNNABLE21 22 while (state != Thread.State.TERMINATED){ //只要线程不终止TERMINATED,就一直输出状态23 state = thread.getState();24 System.out.println(state); //5s内线程处于就绪等待状态TIMED_WAITING25 }26 27 thread.start(); //报错,已经结束的线程不能再次启动28 }29 30 }
线程优先级
Java通过线程调度器监控所有就绪态线程,按优先级决定执行对象
优先级范围为1~10,优先级越高先运行的机率越高,但最终实际运行的线程还是由CPU决定
1 public class TestPriority { 2 public static void main(String[] args) { 3 //主线程优先级默认5无法更改 4 //getPriority()获取优先级 5 System.out.println(Thread.currentThread().getName()+"---"+Thread.currentThread().getPriority()); 6 7 MyPriority myPriority = new MyPriority(); 8 Thread t1 = new Thread(myPriority); 9 Thread t2 = new Thread(myPriority);10 Thread t3 = new Thread(myPriority);11 Thread t4 = new Thread(myPriority);12 Thread t5 = new Thread(myPriority);13 Thread t6 = new Thread(myPriority);14 15 //在start前先设置优先级,再启动16 //setPriority()设置优先级17 t1.start();18 t2.setPriority(1);19 t2.start();20 t3.setPriority(3);21 t3.start();22 t4.setPriority(Thread.MAX_PRIORITY);//最大优先级1023 t4.start();24 t5.setPriority(Thread.MIN_PRIORITY);//最小优先级125 t5.start();26 t6.setPriority(Thread.NORM_PRIORITY);//中等优先级527 t6.start();28 }29 }30 31 class MyPriority implements Runnable{32 @Override33 public void run() {34 //打印优先级35 System.out.println(Thread.currentThread().getName()+"---"+Thread.currentThread().getPriority());36 }37 }
守护线程
线程分为用户线程和守护线程(daemon)
虚拟机必须确保用户线程执行完毕,不用等待守护线程执行完毕
1 public class TeatDaemon { 2 public static void main(String[] args) { 3 God god = new God(); 4 Person person = new Person(); 5 6 Thread thread = new Thread(god); 7 thread.setDaemon(true); //正常情况下默认为false,即用户线程,这里人为设置true 8 9 thread.start();10 11 new Thread(person).start();12 }13 }14 15 class God implements Runnable{16 @Override17 public void run() {18 while (true){19 System.out.println("God bless you");20 }21 }22 }23 class Person implements Runnable{24 @Override25 public void run() {26 for (int i = 0; i < 36500; i++) {27 System.out.println("alive");28 }29 System.out.println("end==============");30 }31 }
线程同步
线程同步的条件:队列+锁
锁机制synchronized:
一个线程用有所会导致其他需要此锁的线程挂起
如果一个优先级高的线程等待一个优先级低的线程释放锁,会导致优先级倒置
并发问题
1 //多个线程同时操作一个对象(买火车票) 2 //多个线程同时操作同一个资源,出现数据紊乱 3 public class Demo04 implements Runnable{ 4 5 private int ticketNums = 10; 6 @Override 7 public void run() { 8 while (true){ 9 if (ticketNums <= 0) break;10 //线程sleep方法模拟延时11 try {12 Thread.sleep(200);13 } catch (InterruptedException e) {14 e.printStackTrace();15 }16 //Thread.currentThread().getName() 获取当前线程的名字17 System.out.println(Thread.currentThread().getName()+" got "+ticketNums--);18 }19 }20 21 public static void main(String[] args) {22 Demo04 demo04 = new Demo04();23 24 new Thread(demo04,"user1").start();25 new Thread(demo04,"user2").start();26 new Thread(demo04,"user3").start();27 }28 }
同步方法
synchronized关键字包括:synchronized方法和synchronized块
synchronized方法
synchronized方法控制“对象”的访问,每个对象对应一把锁,每个synchronized方法都必须获得调用该方法的对象的锁才能执行,否则线程会阻塞;方法一旦执行就独占该锁,直到该方法返回才释放锁,后面被阻塞的线程才能获得这个锁,继续执行。
synchronized块
Obj称为同步监视器,推荐使用共享资源作为同步监视器
1 //synchronized同步方法,锁的是this,即类本身2 private synchronized void name(){}3 //synchronized块可以自定义锁的对象,锁的对象是变化的量(增删改的对象)4 synchronized (任何对象){}
JUC安全类型的集合:CopyOnWriteArrayList
锁
ReentranLock类(可重入锁)实现了Lock,拥有和synchronized相同的并发性和内存语义,可以显式加锁.lock()、释放锁.unlock()
1 //测试lock锁 2 public class TestLock { 3 4 public static void main(String[] args) { 5 TestLock2 testLock2 = new TestLock2(); 6 7 new Thread(testLock2).start(); 8 new Thread(testLock2).start(); 9 new Thread(testLock2).start();10 }11 12 }13 14 class TestLock2 implements Runnable{15 16 int ticketNums = 10;17 18 //定义lock锁19 private ReentrantLock lock = new ReentrantLock();20 21 @Override22 public void run() {23 while (true){24 25 try {26 27 //加锁28 lock.lock();29 30 if (ticketNums > 0){31 try {32 Thread.sleep(1000);33 } catch (InterruptedException e) {34 e.printStackTrace();35 }36 System.out.println(ticketNums--);37 }else {38 break;39 }40 } catch (Exception e) {41 e.printStackTrace();42 } finally {43 44 //解锁45 lock.unlock();46 //解锁一般放在finally块中47 }48 }49 }50 }
synchronized和Lock的对比
Lock是显式锁,需要手动开关;synchronized是隐式锁,离开了作用域自动释放
Lock只有代码块锁,synchronized有代码块锁和方法锁
Lock锁性能更好,并具有更好的扩展性
优先顺序:Lock>同步代码块>同步方法
线程通信
生产者消费者问题
Java中几个解决线程通信问题的方法(只能在同步方法或同步代码块中使用,否则抛出异常)
方法名 作用 wait() 线程等待,会释放锁 wait(long timeout) 指定等待的毫秒数 notify() 唤醒一个处于等待状态的线程 notifyAll() 唤醒同一个对象上所有调用wait方法的线程,按优先级调度
管程法
1 //管程法缓冲区解决生产者消费者问题 2 public class TestPC { 3 public static void main(String[] args) { 4 SynContainer synContainer = new SynContainer(); 5 new Productor(synContainer).start(); 6 new Consumer(synContainer).start(); 7 } 8 } 9 10 class Productor extends Thread{11 SynContainer container;12 13 public Productor(SynContainer container){14 this.container = container;15 }16 //生产17 @Override18 public void run() {19 for (int i = 0; i < 100; i++) {20 System.out.println(i+" producted");21 container.push(new Product(i));22 } 23 }24 }25 26 class Consumer extends Thread{27 SynContainer container;28 29 public Consumer(SynContainer container){30 this.container = container;31 }32 33 //消费34 @Override35 public void run() {36 for (int i = 0; i < 100; i++) {37 System.out.println(container.pop().id+" consumed");38 }39 }40 }41 42 class Product {43 int id;44 45 public Product(int id) {46 this.id = id;47 }48 49 }50 //缓冲区51 class SynContainer{52 //容器大小53 Product[] products = new Product[10];54 //容器计数器55 int count = 0;56 //生产者放入产品57 public synchronized void push(Product product) {58 //如果容器满,等待59 if (count == 10) {60 //生产者等待61 try {62 this.wait();63 } catch (InterruptedException e) {64 e.printStackTrace();65 }66 }67 //如果容器未满,放入产品68 products[count] = product;69 count++;70 //通知消费者消费71 this.notifyAll();72 }73 74 //消费者消费产品75 public synchronized Product pop() {76 //判断能否消费77 if (count == 0) {78 //消费者等待79 try {80 this.wait();81 } catch (InterruptedException e) {82 // TODO Auto-generated catch block83 e.printStackTrace();84 }85 }86 //如果可以消费87 count--;88 Product product = products[count];89 //通知生产者生产90 this.notifyAll();91 92 return product;93 }94 }
标志位法
1 //信号灯法,通过标志位解决 2 public class TestPC2 { 3 public static void main(String[] args) { 4 TV tv = new TV(); 5 new Player(tv).start(); 6 new Audience(tv).start(); 7 } 8 } 9 10 //生产者---演员11 class Player extends Thread{12 TV tv;13 public Player(TV tv){14 this.tv = tv;15 }16 @Override17 public void run() {18 for (int i = 0; i < 20; i++) {19 this.tv.play("program"+i);20 }21 }22 }23 //消费者---观众24 class Audience extends Thread{25 TV tv;26 public Audience(TV tv){27 this.tv = tv;28 }29 @Override30 public void run() {31 for (int i = 0; i < 20; i++) {32 tv.watch();33 }34 }35 }36 //产品---节目37 class TV{38 String voice;39 //标志位flag40 boolean flag = true;41 42 //表演43 public synchronized void play(String voice){44 if (!flag){45 try {46 this.wait();47 } catch (InterruptedException e) {48 e.printStackTrace();49 }50 }51 System.out.println("演员表演了"+voice);52 //通知观众公关看53 this.notifyAll();54 this.voice = voice;55 this.flag = !this.flag;56 }57 //观看58 public synchronized void watch(){59 if (flag) {60 try {61 this.wait();62 } catch (InterruptedException e) {63 e.printStackTrace();64 }65 }66 System.out.println("观众观看了"+voice);67 //通知演员表演68 this.notifyAll();69 this.flag = !this.flag;70 }71 }
线程池
提前创建多个线程,放入线程池,避免频繁创建和销毁,实现重复利用。
使用线程池的优点:
提高响应速度
降低资源消耗
便于线程管理
使用线程池:
ExecutorService:线程池接口,通过Executors工具类创建并返回不同类型的线程池并执行Runnable
也可以通过submit(Callable)执行,这种方法有返回值
1 //线程池 2 public class TestPool { 3 public static void main(String[] args) { 4 //创建线程池 5 //newFixedThreadPool(线程池大小) 6 ExecutorService service = Executors.newFixedThreadPool(10); 7 8 //启动线程 9 service.execute(new MyThread());10 service.execute(new MyThread());11 service.execute(new MyThread());12 service.execute(new MyThread());13 14 //关闭线程池15 service.shutdown();16 }17 }18 19 class MyThread implements Runnable{20 @Override21 public void run() {22 System.out.println(Thread.currentThread().getName());23 }24 }25