进程与线程 进程是执行程序的一次执行过程,它是一个动态的概念,是系统资源分配的单位
通常在一个进程中可以包含若干个线程,当然一个进程中至少有一个线程,不然没有存在的一次,线程是CPU调度和执行的单位
注:很多多线程是模拟出来的,真正的多线程是指有多个cpu,即多核,如服务器,如果是模拟出来的多线程,即在一个cpu的情况下,在同一个时间点,cpu只能执行一个代码,因为切换的很快,所以就有同时执行的错觉。
多线程其实就是让不同的线程去操作同一个对象
线程创建 Thread class 继承Thread类(重点) Runnable接口(重点) Callable实现Callable接口(了解) Thread类本身实现了Runnable接口
直接调用run方法和start方法的区别:
直接调用run方法相当于是调用了一个普通的方法,而调用start方法才相当于是开启了一个线程。当我们在main线程与分线程分别执行2000次输出语句,那么就会发现两个输出语句混杂在一起,也就是多线程。
继承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 package com.hty.demo;public class Demo1 extends Thread { public static void main (String[] args) { Demo1 demo1 = new Demo1 (); demo1.start(); for (int i=0 ;i<2000 ;++i){ System.out.println("这是main线程---" +i); } } @Override public void run () { for (int i=0 ;i<2000 ;++i){ System.out.println("这是分线程---" +i); } } }
注:线程开启后并不一定立即执行,而是由CPU进行调度
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 package com.hty.demo;import org.apache.commons.io.FileUtils;import java.io.File;import java.io.IOException;import java.net.URL;public class Demo2 extends Thread { private String url; private String name; public Demo2 (String url,String name) { this .url = url; this .name = name; } @Override public void run () { WebDownLoader wdl = new WebDownLoader (); wdl.downloader(url,name); System.out.println("下载" +name+"成功" ); } public static void main (String[] args) { Demo2 t1 = new Demo2 ("https://cdn.acwing.com/media/user/profile/photo/1_lg_844c66b332.jpg" ,"pic1.jpg" ); Demo2 t2 = new Demo2 ("https://cdn.acwing.com/media/user/profile/photo/1_lg_844c66b332.jpg" ,"pic2.jpg" ); Demo2 t3 = new Demo2 ("https://cdn.acwing.com/media/user/profile/photo/1_lg_844c66b332.jpg" ,"pic3.jpg" ); t1.start(); t2.start(); t3.start(); } } class WebDownLoader { public void downloader (String url,String name) { try { FileUtils.copyURLToFile(new URL (url),new File (name)); } catch (IOException e) { e.printStackTrace(); System.out.println("IO异常,downloader方法出现问题 " ); } } }
最后的结果是
1 2 3 下载pic3.jpg成功 下载pic1.jpg成功 下载pic2.jpg成功
我们发现并不是按照我们预想的那样,首先下载pic1,之后是pic2,pic3,每次下载完成后的结果都不尽相同,说明了线程的执行顺序与程序的顺序无关,是由CPU进行调度的
实现Runnable接口方法 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 package com.hty.demo;public class Demo3 implements Runnable { public static void main (String[] args) { Demo3 demo3 = new Demo3 (); new Thread (demo3).start(); for (int i=0 ;i<2000 ;++i){ System.out.println("这是main线程---" +i); } } @Override public void run () { for (int i=0 ;i<2000 ;++i){ System.out.println("这是分线程---" +i); } } }
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 package com.hty.demo;import org.apache.commons.io.FileUtils;import java.io.File;import java.io.IOException;import java.net.URL;public class Demo4 implements Runnable { private String url; private String name; public Demo4 (String url,String name) { this .url = url; this .name = name; } @Override public void run () { WebDownLoader wdl = new WebDownLoader (); wdl.downloader(url,name); System.out.println("下载" +name+"成功" ); } public static void main (String[] args) { Demo2 t1 = new Demo2 ("https://cdn.acwing.com/media/user/profile/photo/1_lg_844c66b332.jpg" ,"pic1.jpg" ); Demo2 t2 = new Demo2 ("https://cdn.acwing.com/media/user/profile/photo/1_lg_844c66b332.jpg" ,"pic2.jpg" ); Demo2 t3 = new Demo2 ("https://cdn.acwing.com/media/user/profile/photo/1_lg_844c66b332.jpg" ,"pic3.jpg" ); new Thread (t1).start(); new Thread (t2).start(); new Thread (t3).start(); } } class WebDownLoader1 { public void downloader (String url,String name) { try { FileUtils.copyURLToFile(new URL (url),new File (name)); } catch (IOException e) { e.printStackTrace(); System.out.println("IO异常,downloader方法出现问题 " ); } } }
我们更推荐使用的是实现Runnable接口的方法来实现多线程,避免了java的单继承局限性
使用多个线程操作同一个对象 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 package com.hty.demo;public class Demo5 implements Runnable { private int ticketNums = 10 ; public static void main (String[] args) { Demo5 t1 = new Demo5 (); new Thread (t1).start(); new Thread (t1).start(); new Thread (t1).start(); } @Override public void run () { while (true ){ if (ticketNums == 0 ) break ; System.out.println(Thread.currentThread().getName()+"-->购买了第" +ticketNums--+"张票" ); } } }
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 package com.hty.demo;public class Demo6 implements Runnable { private static String winner; @Override public void run () { for (int i = 0 ; i <= 100 ; i++) { if (Thread.currentThread().getName().equals("兔子" ) && i%10 == 0 ){ try { Thread.sleep(10 ); } catch (InterruptedException e) { e.printStackTrace(); } } boolean flag = gameOver(i); if (flag){ break ; } System.out.println(Thread.currentThread().getName()+"跑了" +i+"步" ); } } private boolean gameOver (int steps) { if (winner != null ){ return true ; }else { if (steps >= 100 ){ winner = Thread.currentThread().getName(); System.out.println("winner is " +winner); return true ; } } return false ; } public static void main (String[] args) { Demo6 demo6 = new Demo6 (); new Thread (demo6,"兔子" ).start(); new Thread (demo6,"乌龟" ).start(); } }
静态代理 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 package com.hty.demo;import java.util.Map;public class Demo7 { public static void main (String[] args) { hunqinggongsi h = new hunqinggongsi (new You ()); h.marry(); } } interface Marry { void marry () ; } class You implements Marry { @Override public void marry () { System.out.println("结婚" ); } } class hunqinggongsi implements Marry { private Marry target; public hunqinggongsi (Marry target) { this .target = target; } @Override public void marry () { before(); this .target.marry(); after(); } private void before () { System.out.println("结婚前,布置现场" ); } private void after () { System.out.println("结婚后,收尾款" ); } }
Lambda表达式 主要作用是:避免匿名内部类定义过多
可以让代码看起来很简洁
去掉一堆没有意义的代码,只留下核心的逻辑
其实质属于函数式编程的概念
(params)->experssion[表达式]
(params)->statement[语句]
(params)->{statement}
1 2 3 a->System.out.println("i like lambda"+a); new Thread(()->System.out.println("多次线程学习....")).start();
线程状态 创建状态:new出来的线程就处于创建状态
就绪状态:调用start()方法之后就处于就绪状态
运行状态:当线程获取到了CPU资源的时候就进入了运行状态
1 2 3 4 5 6 7 setPriority(int newPriority); static void sleep (long millis) ; void join () ;static void yield () ;void interrupt () ;boolean isAlive () ;
线程停止 不推荐使用JDK提供的stop()、destroy()方法[已废弃]
推荐线程自己停下来
建议使用一个标志位进行终止变量当flag=false,则终止线程运行
如果强行终止线程可能会导致数据丢失
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 package com.hty.demo;public class Demo8 implements Runnable { private boolean flag = true ; public static void main (String[] args) { Demo8 demo8 = new Demo8 (); new Thread (demo8).start(); for (int i = 0 ; i < 1000 ; i++) { System.out.println("main" +i); if (i == 900 ){ demo8.Stop(); System.out.println("分线程停止了" ); } } } @Override public void run () { int i=0 ; while (flag){ System.out.println("线程运行了" + (i++) +"次" ); } } public void Stop () { flag = !flag; } }
线程休眠sleep sleep(时间)指定当前线程阻塞的毫秒数
sleep存在异常InterruptedException
sleep时间达到后线程进入就绪状态
sleep可以模拟网络延时,倒计时等
每个对象都有一个锁,sleep不会释放锁
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 package com.hty.demo; /* * 使用多线程的sleep函数模拟倒计时 * */ public class Demo10 { public static void main(String[] args) throws InterruptedException { Demo10.tenDown(); } public static void tenDown() throws InterruptedException { int num=10; while(true){ Thread.sleep(1000); System.out.println(num--); if(num<=0) break; } } }
线程礼让 yield 礼让线程,让线程正在执行的线程暂停,但不阻塞
将线程从运行状态转为就绪状态
让CPU重新调度,礼让不一定成功
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 package com.hty.demo;public class Demo11 { public static void main (String[] args) { MyYield my1 = new MyYield (); new Thread (my1,"1" ).start(); new Thread (my1,"2" ).start(); } } class MyYield implements Runnable { @Override public void run () { System.out.println(Thread.currentThread().getName()+"start" ); Thread.yield(); System.out.println(Thread.currentThread().getName()+"end" ); } }
线程强制执行-Join 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 package com.hty.demo;public class Demo12 implements Runnable { @Override public void run () { for (int i = 0 ; i < 100 ; i++) { System.out.println("线程vip" +i); } } public static void main (String[] args) { Demo12 d = new Demo12 (); Thread t = new Thread (d); t.start(); for (int i = 0 ; i < 100 ; i++) { System.out.println("主线程" +i); if (i == 50 ){ try { t.join(); } catch (InterruptedException e) { e.printStackTrace(); } } } } }
观测线程状态 线程的各种状态表示 Thread.State类中的静态不可修改的常量
NEW:尚未启动的线程处于此状态。
RUNNABLE:在Java虚拟机中执行的线程处于此状态。
BLOCKED:被阻塞等待监视器锁定的线程处于此状态。
WAITING:正在等待另一个线程执行特定动作的线程处于此状态。
TIMED_WAITING:正在等待另一个线程执行动作达到指定等待时间的线程处于此状态。
TERMINATED:已退出的线程处于此状态。
一个线程可以在给定时间点处于一个状态,这些状态是不反应任何操作系统线程状态的虚拟机状态
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 package com.hty.demo; /* * 监测线程的状态 * */ public class Demo13 { public static void main(String[] args) { Thread thread = new Thread(()-> { for (int i = 0; i < 5; i++) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("-----------"); }); //观察线程状态 Thread.State state = thread.getState(); System.out.println(state);//NEW 因为是刚刚创建出来并没有启动 //启动线程 thread.start(); state = thread.getState(); System.out.println(state);//RUNNABLE 处于运行状态 while(state != Thread.State.TERMINATED){ state = thread.getState(); System.out.println(state); } } }
线程的优先级 Java提供一个线程调度器来监控程序中启动后进入就绪状态的所有线程,线程调度器按照优先级决定应该调度哪个线程来执行
线程的优先级用数字表示 范围从1-10
static int MAX_PRIORITY 线程可以拥有的最大优先级。 static int MIN_PRIORITY 线程可以拥有的最小优先级。 static int NORM_PRIORITY 分配给线程的默认优先级。
使用一下方式改变或者获取优先级
getPriority.setPriority(int xxx)
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 package com.hty.demo; /* * 测试线程的优先级 * */ public class Demo14 { public static void main(String[] args) { //主线程的优先级 System.out.println(Thread.currentThread().getName()+"-->"+Thread.currentThread().getPriority()); MyPriority mp = new MyPriority(); Thread thread1 = new Thread(mp); Thread thread2 = new Thread(mp); Thread thread3 = new Thread(mp); Thread thread4 = new Thread(mp); Thread thread5 = new Thread(mp); Thread thread6 = new Thread(mp); //首先设置优先级 thread1.start(); thread2.setPriority(1); thread2.start(); thread3.setPriority(2); thread3.start(); thread4.setPriority(3); thread4.start(); thread5.setPriority(4); thread5.start(); thread6.setPriority(5); thread6.start(); } } class MyPriority implements Runnable{ @Override public void run() { System.out.println(Thread.currentThread().getName()+"-->"+Thread.currentThread().getPriority()); } }
注意:并不是t1线程的优先级高于t2就必定先执行,这只是一个参考,最主要还是看CPU的调度
优先级低只是意味着获得调度的概率低,并不是优先级低就不会被调用了
守护线程 daemon 线程分为用户线程和守护线程
虚拟机必须确保用户线程执行完毕
虚拟机不用等待守护线程执行完毕
如:后台记录操作日志,监控内存,垃圾回收等待
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 package com.hty.demo; import java.util.regex.Pattern; /* * 守护线程 * 上帝守护人民 * */ public class Demo15 { public static void main(String[] args) { God god = new God(); You1 you = new You1(); Thread thread = new Thread(god); thread.setDaemon(true);//默认是false 正常的线程都是用户线程 thread.start();//上帝守护线程启动 new Thread(you).start();//用户线程启动 } } //上帝 class God implements Runnable{ @Override public void run() { while(true) { System.out.println("上帝保佑"); } } } //人民 class You1 implements Runnable{ @Override public void run() { for (int i = 0; i < 36500; i++) { System.out.println("已经活了"+i+"天"); } System.out.println("死亡"); } }
线程同步 线程操作同一个资源
并发 同一个对象被多个线程同时操作
例:抢火车票,两个银行同时取钱
处理多线程问题时,多个线程访问同一个对象,并且某些线程还想修改这个对象,这时候我们就需要线程同步,线程同步就是一种等待机制,多个需要同时访问此对象的线程进入这个对象的等待池形成队列,等待前面线程使用完毕,下一个线程再使用
队列和锁 线程同步的形成条件:队列+锁
同步代码块的锁就是括号中的Obj对象 同步方法的锁就是当前对象 this 静态的同步方法锁对象就是该类的字节码文件 由于统一进程的多个线程共享同一块存储空间,在带来方便的同时,也带来了访问冲突问题,为了保证数据在方法中被访问时的正确性,在访问时加入了锁机制,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 package com.hty.demo;public class Demo16 { public static void main (String[] args) { BuyTicket bt = new BuyTicket (); Thread t1 = new Thread (bt,"小明" ); Thread t2 = new Thread (bt,"小红" ); t1.start(); t2.start(); } } class BuyTicket implements Runnable { private Integer ticketcount = 10 ; private boolean flag = true ; @Override public void run () { while (flag){ buy(); } } private void buy () { if (ticketcount <= 0 ) { flag =false ; return ; } try { Thread.sleep(100 ); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"买了一张票,现在还有" +(--ticketcount)+"张票" ); } }
由于我们可以通过private关键字来保证数据对象只能被方法访问,所以我们只需要针对方法提出一套机制,这套机制就是synchronized关键字,他包括两种用法:synchronized方法和synchronized块
同步方法:public synchronized void method(int args){}
synchronized方法控制对“对象”的访问,每个对象对应一把锁,每个synchronized方法都必须获得调用该方法的对象的锁才能执行,否则线程会阻塞,方法一旦执行,就独占该锁,知道该方法返回才释放锁,后面被阻塞的线程才能获得这个锁,继续执行
缺陷:若将一个打的方法声明为synchronized将会影响效率
一般是将执行修改功能的代码块加上synchronized
同步块 synchronized(Obj){}
Obj称之为同步监视器
Obj可以是任何对象,但是推荐使用共享资源作为同步监视器
同步方法中无需指定同步监视器,因为同步方法的同步监视器就是this,就是这个对象本身,或者是class[反射中讲解]
同步监视器的执行过程
1 2 3 4 5 1. 第一个线程访问,锁定同步监视器,执行其中代码2. 第二个线程访问,发现同步监视器被锁定,无法访问3. 第一个线程访问完毕,解锁同步监视器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 33 34 35 36 37 38 39 40 41 package com.hty.demo;public class Demo16 { public static void main (String[] args) { BuyTicket bt = new BuyTicket (); Thread t1 = new Thread (bt,"小明" ); Thread t2 = new Thread (bt,"小红" ); t1.start(); t2.start(); } } class BuyTicket implements Runnable { private Integer ticketcount = 10 ; private boolean flag = true ; @Override public void run () { while (flag){ buy(); } } private synchronized void buy () { if (ticketcount <= 0 ) { flag =false ; return ; } try { Thread.sleep(100 ); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"买了一张票,现在还有" +(--ticketcount)+"张票" ); } }
死锁 多个线程各自占有一些共享资源,并且相互等待其他线程占有的资源才能运行,而导致两个或者多个线程都在等待对方释放资源,都停止执行的情形,某一个同步块同时拥有两个以上对象锁时,就可能发生死锁的问题
线程1拿着对象1的锁,线程2拿着对象2的锁,线程1需要拿到对象2的锁才能放下对象1的锁,但是线程2必须拿到对象1的锁才能放下对象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 package com.hty.demo; /* * 死锁程序演示 * 死锁:多个线程互相拿着对方需要的资源,然后形成僵持 * */ public class Demo18 { public static void main(String[] args) { Makeup g1 = new Makeup(0,"小红"); Makeup g2 = new Makeup(1,"小明"); g1.start(); g2.start(); } } class Lipstick{ } class Mirror{ } class Makeup extends Thread{ //需要的资源只有一份 使用static来标记 static Lipstick l = new Lipstick(); static Mirror m = new Mirror(); int choice;//选择 String girlName;//使用化妆品的人 public Makeup(int choice,String girlName){ this.choice = choice; this.girlName = girlName; } @Override public void run() { makeup(); } //化妆 private void makeup(){ //互相持有对方的锁 if(choice == 0){ //获得口红的锁 synchronized (l){ System.out.println(this.girlName+"获得口红的锁"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } //拿到镜子的锁 // synchronized (m){ // System.out.println(this.girlName+"获得镜子的锁"); // } //此处进行synchronized的嵌套就行成了死锁 解决方法就是将嵌套的synchronized放在外面 } synchronized (m){ System.out.println(this.girlName+"获得镜子的锁"); } }else{ //获得口红的锁 synchronized (m){ System.out.println(this.girlName+"获得镜子的锁"); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } //拿到镜子的锁 // synchronized (l){ // System.out.println(this.girlName+"获得口红 的锁"); // } } synchronized (l){ System.out.println(this.girlName+"获得口红的锁"); } } } }
线程协作——生产者消费者模式 应用场景:
假设仓库中只能存放一件产品,生产者将生产出来的产品放入仓库,消费者将仓库中产品取走消费
如果仓库中没有产品,则生产者将产品放入仓库,否则停止生产并等待,直到仓库中的产品被消费者取走为止
如果仓库中放有产品,则消费者可以将产品取走消费,否则停止消费并等待,知道仓库中再次放入产品为止
线程通信-分析 这是一个线程同步问题,生产者和消费者共享同一个资源,并且生产者和消费者之间相互依赖,互为条件
对于生产者,没有生产产品之前,要通知消费者等待,而生产了产品之后,有需要马上通知消费者消费
对于消费者,在消费 之后,要通知生产者已经结束消费,需要生产新的产品以供消费
在生产者消费者问题中,仅有synchronized是不够的
synchronized可阻止并发更新同一个共享资源,实现了同步
synchronized不能用来实现不同线程之间的消息传递(通信)
Java提供了几个方法解决线程之间的通信问题
wait() 表示线程一直等待,直到其他线程通知,与sleep不同,会释放锁
wait(long timeout) 指定等待的毫秒数
notify() 唤醒一个处于等待状态的线程
notifyAll() 唤醒同一个对象上所有调用wait()方法的线程,优先级高的线程优先调度
注: 均是Oject类的方法,都只能在同步方法或者同步代码块中使用,否则会抛出异常
解决方法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 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 package com.hty.demo;public class Demo19 { public static void main (String[] args) { huanchongqu h = new huanchongqu (); new shengchanzhe (h).start(); new xiaofeizhe (h).start(); } } class shengchanzhe extends Thread { huanchongqu hcq; public shengchanzhe (huanchongqu hcq) { this .hcq = hcq; } @Override public void run () { for (int i = 1 ; i < 101 ; i++) { hcq.push(new chanpin (i)); System.out.println("生产了" +i+"个产品" ); } } } class xiaofeizhe extends Thread { huanchongqu hcq; public xiaofeizhe (huanchongqu hcq) { this .hcq = hcq; } @Override public void run () { for (int i = 1 ; i < 101 ; i++) { System.out.println("消费了" +hcq.use().id); } } } class chanpin { int id; public chanpin (int id) { this .id = id; } } class huanchongqu { chanpin[] chanpins = new chanpin [10 ]; int count=0 ; public synchronized void push (chanpin c) { if (count == chanpins.length){ try { this .wait(); } catch (InterruptedException e) { e.printStackTrace(); } } chanpins[count++] = c; this .notify(); } public synchronized chanpin use () { if (count == 0 ){ try { this .wait(); } catch (InterruptedException e) { e.printStackTrace(); } } count--; chanpin chanpin = chanpins[count]; this .notify(); return chanpin; } }
解决方法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 package com.hty.demo;import java.util.regex.Pattern;public class Demo20 { public static void main (String[] args) { TV tv = new TV (); new Player (tv).start(); new Watcher (tv).start(); } } class Player extends Thread { TV tv; public Player (TV tv) { this .tv = tv; } @Override public void run () { for (int i = 0 ; i < 20 ; i++) { if (i%2 ==0 ){ this .tv.play("快乐大本营" ); }else { this .tv.play("抖音" ); } } } } class Watcher extends Thread { TV tv; public Watcher (TV tv) { this .tv = tv; } @Override public void run () { for (int i = 0 ; i < 20 ; i++) { tv.watch(); } } } class TV { String voice; boolean flag = true ; public synchronized void play (String voice) { if (!flag) { try { this .wait(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("演员表演了:" +voice); this .notifyAll(); this .voice = voice; flag = false ; } public synchronized void watch () { if (flag){ try { this .wait(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("观看了" +voice); this .notifyAll(); flag = !this .flag; } }
线程池 背景:经常创建和销毁、使用量特别大的资源,比如并发情况下的线程,对性能影响很大
思路:提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中。可以避免频繁创建销毁、实现重复利用。类似生活着的公共交通工具
好处:
提高响应速度(减少了创建新线程的时间) 降低资源消耗(重复利用线程池中线程,不需要每次都创建) 便于线程管理 corePoolSize 核心池的大小 maximumPoolSize 最大线程数 keepAliveTime 线程没有任务时,最多保持多长时间后会终止 0; i++) { tv.watch(); } } } //产品——-节目 class TV{ //演员表演 观众等待 T //观众观看 演员等待 F String voice;//表演的节目 boolean flag = true;
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 public synchronized void play (String voice) { if (!flag) { try { this .wait(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("演员表演了:" +voice); this .notifyAll(); this .voice = voice; flag = false ; } public synchronized void watch () { if (flag){ try { this .wait(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("观看了" +voice); this .notifyAll(); flag = !this .flag; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 ## 线程池 背景:经常创建和销毁、使用量特别大的资源,比如并发情况下的线程,对性能影响很大 思路:提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中。可以避免频繁创建销毁、实现重复利用。类似生活着的公共交通工具 好处: * 提高响应速度(减少了创建新线程的时间) * 降低资源消耗(重复利用线程池中线程,不需要每次都创建) * 便于线程管理 * corePoolSize 核心池的大小 * maximumPoolSize 最大线程数 * keepAliveTime 线程没有任务时,最多保持多长时间后会终止