Java多线程
-
三种创建线程的方式:
-
继承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 @Override 12 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 target 20 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 @Override 15 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 target 24 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 @Override 23 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 @Override 37 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 @Override 22 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 @Override 32 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 @Override 54 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 may 11 * 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 @Override 11 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 @Override 12 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方法,线程处于创建态NEW 17 18 thread.start(); //调用start后,进程进入就绪态,准备运行 19 state = thread.getState(); 20 System.out.println(state); //此时线程为运行态RUNNABLE 21 22 while (state != Thread.State.TERMINATED){ //只要线程不终止TERMINATED,就一直输出状态 23 state = thread.getState(); 24 System.out.println(state); //5s内线程处于就绪等待状态TIMED_WAITING 25 } 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);//最大优先级10 23 t4.start(); 24 t5.setPriority(Thread.MIN_PRIORITY);//最小优先级1 25 t5.start(); 26 t6.setPriority(Thread.NORM_PRIORITY);//中等优先级5 27 t6.start(); 28 } 29 } 30 31 class MyPriority implements Runnable{ 32 @Override 33 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 @Override 17 public void run() { 18 while (true){ 19 System.out.println("God bless you"); 20 } 21 } 22 } 23 class Person implements Runnable{ 24 @Override 25 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 @Override 22 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锁性能更好,并具有更好的扩展性
-
优先顺序: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 @Override 18 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 @Override 35 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 block 83 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 @Override 17 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 @Override 30 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 //标志位flag 40 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 @Override 21 public void run() { 22 System.out.println(Thread.currentThread().getName()); 23 } 24 } 25