1、多线程的简介
1.1主要概念和三种创建方式
主要概念
-
任务、多任务
-
多线程(multithreading)
多线程(多条道路),是指从软件或者硬件上实现多个线程并发执行的技术。具有多线程能力的计算机因有硬件支持而能够在同一时间执行多于一个线程,进而提升整体处理性能。
-
普通方法调用和多线程
-
进程(process) 线程(Thread)
- 程序:程序并不能单独执行,只有将程序加载到内存中
- 进程(动态执行程序):进程是系统进行资源分配和调度的一个独立单位,每个进程都有自己单独的地址空间
- 线程(执行程序的真正路径):进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程只是一个进程中的不同执行路径
-
调度器(CPU)
三种创建方式
1.2Tread类创建
1.2.1继承Tread类
代码实现过程
- 代码实现案列(简单输出顺序)
package multithreading1;
//创建线程方式一:继承Thread类,重写run()方法,调用start开启线程//总结:线程开启不一定立即执行(由CPU调度)public class TestThread1 extends Thread{//继承、重写run方法@Overridepublic void run() {//run方法线程体for (int i = 0; i < 20; i++) {System.out.println("我在看代码:"+i);}}//main线程,主线程public static void main(String[] args) {//创建一个线程对象TestThread1 testThread1 = new TestThread1();//调用start方法开启线程testThread1.start();//start代码是(CPU调度执行),run先调用run方法for (int i = 0; i < 20; i++) {System.out.println("我在学习多线程:"+i);}}
}
1.2.3多线程下载网络图片
理论过程
具体实现
package multithreading1;
import org.apache.commons.io.FileUtils;//需要导入commons-io-2.6.jar包import java.io.File;
import java.io.IOException;
import java.net.URL;//练习Thread,实现多线程同步下载图片
public class TestThread2 extends Thread {private String url;//网络图片地址private String name;//保存的文件名//构造器写入参数public TestThread2(String url, String name) {this.name = name;this.url = url;}//下载图片线程的执行体@Overridepublic void run() {WebDownloader webDownloader = new WebDownloader();webDownloader.downloader(url,name);System.out.println("下载了文件名为:"+name);}public static void main(String[] args) {//需要的图片路径TestThread2 testThread1 = new TestThread2("https://img0.baidu.com/it/u=3246072535,1598942673&fm=253&fmt=auto&app=138&f=JPEG?w=800&h=800","1.jpg");TestThread2 testThread2 = new TestThread2("https://img0.baidu.com/it/u=3041099101,3668300079&fm=253&app=138&f=JPEG?w=800&h=1067","2.jpg");TestThread2 testThread3 = new TestThread2("https://img1.baidu.com/it/u=898713984,3542374459&fm=253&fmt=auto&app=138&f=JPEG?w=1422&h=800","3.jpg");//下载不是此顺序(CPU调度)testThread1.start();testThread2.start();testThread3.start();}
}
//下载器
class WebDownloader{//下载方法public void downloader(String url,String name){try {FileUtils.copyURLToFile(new URL(url),new File(name));//写好的工具类(点进去看):文件工具类,拷贝图片(url地址)到文件} catch (IOException e) {e.printStackTrace();System.out.println("IO异常,downloader方法出现问题");//改写后,更好定位问题所在}}
}
1.3Runnable接口创建
理论过程
代码线程
package multithreading1;
//方式2:Runnable接口创建——实现runnable接口,执行线程需要丢入runnable接口实现类对象,调用start方法
public class TestThread3 implements Runnable {@Overridepublic void run() {//run方法线程体for (int i = 0; i < 200; i++) {System.out.println("1代码:"+i);}}public static void main(String[] args) {//创建runnable接口的实现对象TestThread3 testThread3 = new TestThread3();//创建线程对象,通过线程对象来开启我们的线程(代理)Thread thread = new Thread(testThread3);//通过它调用start方法thread.start();//一样效果的写法// new Thread(testThread3).start();for (int i = 0; i < 300; i++) {System.out.println("2多线程:"+i);}}
}
1.4初识并发问题(多个线程操作同一个对象)
买火车票的列子——多个线程操作同一个资源的情况下,线程不安全,会出现数据紊乱
package multithreading1;
//多个线程操作同一个对象//买火车票的列子//发现问题:多个线程操作同一个资源的情况下,线程不安全,会出现数据紊乱
public class TestThread4 implements Runnable{//票数private int ticketNums = 10;@Overridepublic void run() {while(true){//死循环if(ticketNums<=0){break;}//模拟延时(需要延时)try {Thread.sleep(200);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println(Thread.currentThread().getName()+"---拿到了第"+ticketNums--+"票");//ticketNums--每拿一张票,就减少一张票}}public static void main(String[] args) {TestThread4 ticket = new TestThread4();new Thread(ticket,"小1").start();new Thread(ticket,"小2").start();new Thread(ticket,"小3").start();}
}
1.5巩固多线程(龟兔赛跑案例——没有实现)
没有Race的包——实现不了
package multithreading1;
//模拟龟兔赛跑
public class TestThread5 implements Runnable{//胜利者private static String winner;//static 保证只有一个胜利者@Overridepublic void run() {for (int i = 0; i < 100; i++) {//判断比赛是否结束boolean flag = gameOver(i);//如果比赛结束了,就停止程序if(flag){break;}System.out.println(Thread.currentThread().getName()+"---跑了"+i+"步");}}//判断是否完成了比赛private boolean gameOver(int steps){//判断是否有胜利者if(winner!=null){//已经存在胜利者了return true;}{if(steps>=100){winner =Thread.currentThread().getName();System.out.println("winner is"+winner);return true;}}return false;}
//版本没有Racepublic static void main(String[] args) {Race race = new Race();new Thread(race,"兔子").start();new Thread(race,"乌龟").start();}
}
1.6Callable接口创建(不怎么用——没学)
1.7线程底库的实现原理(静态代理模式)
代理对象可以做很多代理对象做不了的事情,真实对象就可以专注做自己
真实对象和代理对象都要实现同一个接口(interface接口)
代理对象要代理真实角色
package multithreading1;
//静态代理模式/* 总结
代理对象可以做很多代理对象做不了的事情,真实对象就可以专注做自己
真实对象和代理对象都要实现同一个接口(interface接口)
代理对象要代理真实角色*/
public class StaticProxy {public static void main(String[] args) {You you = new You();//你要结婚new Thread(()-> System.out.println("我爱你")).start();//一个多线程非常简介的表达式new WeddingCompany(new You()).HappyMarry();//与多线程对比}
}
interface Marry{//interface接口void HappyMarry();
}
//真实角色
class You implements Marry{@Overridepublic void HappyMarry() {System.out.println("小胖要结婚啦");}
}
//代理角色,帮助你结婚
class WeddingCompany implements Marry{private Marry target;//代理的--》真实目标角色public WeddingCompany(Marry target){this.target = target;}@Overridepublic void HappyMarry() {before();this.target.HappyMarry();//这就是真实对象after();}private void after() {System.out.println("结婚之后,收尾款");}private void before() {System.out.println("结婚之前,布置现场");}}
2、Lambda表达式(简化程序)(接口方法调用的五种实现)
一个接口里一个方法为函数式接口,才能用Lambda表达式创建接口的对象
线程代码体
package multithreading2;
//观察测试线程状态
public class TestStateThread thread = new Thread(()->{ //线程代码体for (int i = 0; i < 5; i++) {try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}System.out.println("/////////");});
接口方法调用的五种实现
package multithreading1;
/*
推到Lambda表达式*/
public class TestLambda1 {//3.静态内部类static class Like2 implements ILike {@Overridepublic void Lambda() {System.out.println("You like Lambda2");}}public static void main(String[] args) {ILike like = new Like();like.Lambda();like = new Like2();like.Lambda();//4.局部内部类class Like3 implements ILike {@Overridepublic void Lambda() {System.out.println("He like lambda3");}}like = new Like3();like.Lambda();//5.匿名内部类,没有类的名称,必须借用接口或者父类like = new ILike(){@Overridepublic void Lambda() {System.out.println("She like lambda4");}};like.Lambda();//6.用lambda简化like = ()->{System.out.println("It like lambda5");};like.Lambda();}
}//1.定义一个函数式接口
interface ILike{void Lambda();
}//2.实现类
class Like implements ILike{@Overridepublic void Lambda() {System.out.println("I like lambda");}
}
lambda的详细使用介绍
package multithreading1;public class TestLambda2 {public static void main(String[] args) {ILove love = null;/*//1.lambda表达式简化ILove love = (int a)->{System.out.println("I love xiaopang-->"+a);};love.love(520);*///简化1.参数类型love = (a)->{System.out.println("You love xiaopang-->"+a);};love.love(521);//简化2.简化括号love = a -> {System.out.println("He love xiaopang-->"+a);};love.love(1314);//简化3.去掉花括号love = a -> System.out.println("She love xiappang-->"+a);love.love(5201314);/*总结1.(针对简化3)lambda表达式只能有一行代码的情况下才能简化成一行,如果有多行就用代码块包裹{}2.前提接口必须是函数式接口(只有一个方法)3.多个参数也可以去掉参数类型,要去掉就都去掉,必须加上括号*/}}
interface ILove{void love(int a);
}
3、线程状态
线程实现流程
线程常用方法
3.1线程停止
停止线程
package multithreading2;
//测试停止线程
/* 建议
1.建议线程正常停止— —利用次数,不建议死循环
2.建议使用标志位— —设置一个标志位
3.不要使用stop或者destroy等过时或者JDK不建议使用的方法*/
public class TestStop implements Runnable {//1.设置一个标志位private boolean flag = true;@Overridepublic void run() {int i = 0;while(flag){//设置循环标志System.out.println("run.........thread"+i++);}}//2.设置一个公开的方法停止线程,转换标志位(自己写的stop方法)public void stop(){this.flag = false;}public static void main(String[] args) {TestStop testStop = new TestStop();new Thread(testStop).start();for (int i = 0; i < 1000; i++) {System.out.println("main"+i);if(i==900){//调用stop方法切换标志位,让线程停止testStop.stop();System.out.println("线程该停止了");}}}
}
3.2线程休眠_sleep(需要抛出和捕获异常)
线程休眠
1000毫秒 = 1秒
模拟网络延时:放大网络问题的发生性
package multithreading2;import multithreading1.TestThread4;//模拟网络延时:放大网络问题的发生性
public class TestSleep implements Runnable{//票数private int ticketNums = 10;@Overridepublic void run() {while(true){//死循环if(ticketNums<=0){break;}//模拟延时(需要延时)try {Thread.sleep(200);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println(Thread.currentThread().getName()+"---拿到了第"+ticketNums--+"票");//ticketNums--每拿一张票,就减少一张票}}public static void main(String[] args) {TestSleep ticket = new TestSleep();new Thread(ticket,"小1").start();new Thread(ticket,"小2").start();new Thread(ticket,"小3").start();}
}
模拟倒计时
package multithreading2;import com.sun.xml.internal.ws.api.model.wsdl.WSDLOutput;import java.text.SimpleDateFormat;
import java.util.Date;//模拟倒计时
public class TestSleep2 {public static void main(String[] args) {/*//调用倒计时try {tenDown();} catch (InterruptedException e) {e.printStackTrace();}*///打印当前系统时间Date startTime= new Date(System.currentTimeMillis());//获取系统当前时间while(true){try {Thread.sleep(1000);System.out.println(new SimpleDateFormat("HH:mm:ss").format(startTime));//SimpleDateFormat格式化模式时间startTime = new Date(System.currentTimeMillis());//更新当前时间} catch (InterruptedException e) {e.printStackTrace();}}}//模拟倒计时public static void tenDown() throws InterruptedException {int num = 10;while(true){Thread.sleep(1000);System.out.println(num--);if(num<=0){break;}}}
}
3.3线程礼让_Yield(奇葩)
线程礼让
package multithreading2;//测试线程礼让
//礼让不一定成功,看CPU心情
public class TestYield {public static void main(String[] args) {MyYield myYield = new MyYield();new Thread(myYield,"a").start();new Thread(myYield,"b").start();}
}class MyYield implements Runnable{@Overridepublic void run() {System.out.println(Thread.currentThread().getName()+"线程开始执行");Thread.yield();//礼让System.out.println(Thread.currentThread().getName()+"线程停止执行");}
}
3.4线程强制执行_Join(存在并发执行体现不了优先)
线程强制执行
此线程执行完,其他线程才能执行
package multithreading2;//测试Join,相当于插队(这个代码不行,存在并发执行体现不了优先)
public class TestJoin implements Runnable {@Overridepublic void run() {for (int i = 0; i < 100; i++) {System.out.println("线程VIP "+i);}}public static void main(String[] args) throws InterruptedException {//启动我们的线程TestJoin testJoin = new TestJoin();Thread thread = new Thread(testJoin );thread.start();//主线程for (int i = 0; i < 300; i++) {if(i==200){thread.join();//插队}System.out.println("main "+i);}}
}
3.5观测线程状态
线程有五个状态
观察测试线程状态
package multithreading2;//观察测试线程状态
public class TestState {public static void main(String[] args) throws InterruptedException {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);//RUN(执行)//判断 执行状态while(state != Thread.State.TERMINATED){//只要线程不终止(Thread.State.TERMINATED),就一直输出状态thread.sleep(100);state = thread.getState(); // 更新线程状态System.out.println(state); // 输出状态}// thread.start();//死亡或者停止之后的线程就不能再次启动(线程只能启动一次)}
}
3.6线程的优先级_Priority(可能)
大多数时一般优先级高的先执行(有可能实现不了)
package multithreading2;//测试线程的优先级
public class TestPriority {public static void main(String[] args) {//主线程的默认优先级System.out.println(Thread.currentThread().getName()+"--->"+Thread.currentThread().getPriority());MyPriority myPriority = new MyPriority();Thread t1 = new Thread(myPriority);Thread t2 = new Thread(myPriority);Thread t3 = new Thread(myPriority);Thread t4 = new Thread(myPriority);Thread t5 = new Thread(myPriority);Thread t6 = new Thread(myPriority);//先设置优先级,再启动t1.start();t2.setPriority(1);t2.start();t3.setPriority(4);t3.start();t4.setPriority(Thread.MAX_PRIORITY);//最大为10t4.start();
/* 报错t5.setPriority(-1);t5.start();t6.setPriority(11);t6.start();*/}
}class MyPriority implements Runnable{@Overridepublic void run() {System.out.println(Thread.currentThread().getName()+"--->"+Thread.currentThread().getPriority());}
}
3.7守护(daemon)线程
-
线程分为用户线程和守护线程
虚拟机 必须确保用户线程执行完毕,不用等待守护线程执行完毕
你结束,守护线程也就随着结束了
package multithreading2;//测试守护线程
//上帝守护你
public class TestDaemon {public static void main(String[] args) {God god = new God();You you = new You();Thread thread = new Thread(god);thread.setDaemon(true);//默认是false表示是用户线程,正常的线程都是用户线程thread.start();new Thread(you).start(); //你,用户启动线程}
}//上帝
class God implements Runnable{@Overridepublic void run() {while(true){System.out.println("上帝保佑着你");}}
}//你
class You implements Runnable{@Overridepublic void run() {for (int i = 0; i < 36500; i++) {System.out.println("你一生都开心的活着");}System.out.println("========goodbye! world!======");}
}
4、线程同步(并发问题解决)
- 并发:同一个对象被多个线程操作
- 线程同步:其实是一种等待机制,进入对象的等待池成形成队列,等前面的线程使用完毕,后面的线程再使用
- 队列和锁(synchronized):队列-排队,锁-保证门内的安全(即每一个对象都有一把锁)
会导致:提高安全,减低性能;性能倒置
4.1同步方法-锁(厕所)(synchronized)
锁 synchronized 同步方法 和 synchronized(Obj) {} 同步代码块
关键:分析需要锁的对象(有增、删、改的量)
-
在需要队列的方法前加入 synchronized 方法
synchronized方法默认锁的是this
-
非方法(比如run)使用 synchronized(Obj) {} 同步代码块
方法丢在方法里面,块丢在块里面
4.2三大不安全案列
4.2.1"不安去的买票及锁"——没改出来
package multithreading3;
//多个线程操作同一个对象//买火车票的列子import multithreading1.TestThread4;//发现问题:多个线程操作同一个资源的情况下,线程不安全,会出现数据紊乱(有负数和重复)
public class UnsafeBuyTicket implements Runnable{//票数private int ticketNums = 10;@Overridepublic void run() {while(true){//死循环if(ticketNums<=0){break;}//模拟延时(需要延时)try {Thread.sleep(200);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println(Thread.currentThread().getName()+"---拿到了第"+ticketNums--+"票");//ticketNums--每拿一张票,就减少一张票}}public static void main(String[] args) {TestThread4 ticket = new TestThread4();new Thread(ticket,"小1").start();new Thread(ticket,"小2").start();new Thread(ticket,"小3").start();}
}
package multithreading3;
//多个线程操作同一个对象//买火车票的列子import multithreading1.TestThread4;//发现问题:多个线程操作同一个资源的情况下,线程不安全,会出现数据紊乱(有负数和重复)
public class UnsafeBuyTicket implements Runnable{//票数private Integer ticketNums = 10;public static void main(String[] args) {TestThread4 ticket = new TestThread4();new Thread(ticket,"小1").start();new Thread(ticket,"小2").start();new Thread(ticket,"小3").start();}@Overridepublic void run() {synchronized (ticketNums){while(true){//死循环if(ticketNums<=1){break;}//模拟延时(需要延时)try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName()+"---拿到了第"+ticketNums--+"票");//ticketNums--每拿一张票,就减少一张票}}}
}
4.2.2不安全的取钱及锁
package multithreading3;//不安全的取钱 sleep放大问题的发生性
//两个人同时在一个账户取钱
public class UnsafeBank {public static void main(String[] args) {//账户Account account = new Account(100, "吃");Drawing you = new Drawing(account, 50, "你");Drawing he = new Drawing(account, 80, "他");you.start();he.start();}
}//账户
class Account{int money; //余额String name; //账户public Account(int money, String name) {this.money = money;this.name = name;}
}//银行:模拟取款
class Drawing extends Thread{Account account;//账户//取了多少钱int drawingMoney;//现在手里有多少钱int nowMoney;//构造方法快捷生成不了,手动写public Drawing(Account account,int drawingMoney,String name){super(name);this.account = account;this.drawingMoney = drawingMoney;}//取钱@Overridepublic void run() {//判断有没有钱if(account.money-drawingMoney<0){System.out.println(Thread.currentThread().getName()+"钱不够,取不了");return;}//模拟延时try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}//卡内余额 = 余额 - 取得钱account.money = account.money- drawingMoney;//你手里得钱nowMoney = nowMoney + drawingMoney;System.out.println(account.name+"余额为:"+account.money);// Thread.currentThread().getName() = this.getName();System.out.println(this.getName()+"手里的钱:"+nowMoney);}}
package multithreading3;//不安全的取钱 sleep放大问题的发生性
//两个人同时在一个账户取钱
public class UnsafeBank {public static void main(String[] args) {//账户Account account = new Account(100, "吃");Drawing you = new Drawing(account, 50, "你");Drawing he = new Drawing(account, 80, "他");you.start();he.start();}
}//账户
class Account{int money; //余额String name; //账户public Account(int money, String name) {this.money = money;this.name = name;}
}//银行:模拟取款
class Drawing extends Thread{Account account;//账户//取了多少钱int drawingMoney;//现在手里有多少钱int nowMoney;//构造方法快捷生成不了,手动写public Drawing(Account account,int drawingMoney,String name){super(name);this.account = account;this.drawingMoney = drawingMoney;}//取钱@Overridepublic void run() {synchronized (account){//判断有没有钱if(account.money-drawingMoney<0){System.out.println(Thread.currentThread().getName()+"钱不够,取不了");return;}//模拟延时try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}//卡内余额 = 余额 - 取得钱account.money = account.money- drawingMoney;//你手里得钱nowMoney = nowMoney + drawingMoney;System.out.println(account.name+"余额为:"+account.money);// Thread.currentThread().getName() = this.getName();System.out.println(this.getName()+"手里的钱:"+nowMoney);}}}
4.2.3线程不安全的集合及锁
package multithreading3;import java.util.ArrayList;//线程不安全的集合--输出结果难懂
public class UnsafeList {public static void main(String[] args) {ArrayList<String> list = new ArrayList<>();for (int i = 0; i < 10000; i++) {new Thread(()->{ //线程代码块list.add(Thread.currentThread().getName());}).start();}try {Thread.sleep(3000);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println(list.size());}
}
package multithreading3;import java.util.ArrayList;//线程不安全的集合--输出结果难懂
public class UnsafeList {public static void main(String[] args) {ArrayList<String> list = new ArrayList<>();for (int i = 0; i < 10000; i++) {new Thread(()->{ //线程代码块synchronized (list){list.add(Thread.currentThread().getName());}}).start();}try {Thread.sleep(3000);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println(list.size());}
}
4.3补充:JUC(java并发包) 安全类型的集合CopyOnWriteArrayList
package multithreading3;import java.util.ArrayList;
import java.util.concurrent.CopyOnWriteArrayList;//测试JUC(java并发包) 安全类型的集合
public class TestJUC {public static void main(String[] args) {CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();//CopyOnWriteArrayList安全数列组for (int i = 0; i < 10000; i++) {new Thread(()->{ //线程代码块list.add(Thread.currentThread().getName());}).start();}try {Thread.sleep(3000);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println(list.size());}
}
4.4死锁(避免)
某一个同步代码块同时拥有"两个以上对象的锁",而导致线程都在等待对方释放资源,都停止执行的情形
package multithreading3;//死锁:多个线程相互抱着对方需要的资源,然后形成僵持
public class DeadLock {public static void main(String[] args) {Makeup g1 = new Makeup(0, "11");Makeup g2 = new Makeup(1, "22");g1.start();g2.start();}
}//口红
class Lipstick{}//镜子
class Mirror{}class Makeup extends Thread{//需要的资源只有一份,用static来保证只有一份static Lipstick lipstick = new Lipstick();//对于静态成员变量,无论类被实例化为多少个对象,该变量只有一份 即静态成员变量属于类,而普通变量属于对象static Mirror mirror = new Mirror();int choice;//选择String name;//使用化妆品的人Makeup(int choice,String name){this.choice = choice;this.name = name;}@Overridepublic void run() {//化妆try {makeup();} catch (InterruptedException e) {throw new RuntimeException(e);}}//化妆,相互持有对方的锁,就是需要拿到对方的资源private void makeup() throws InterruptedException {if(choice==0){//锁——同步synchronized (lipstick){//获得口红的锁System.out.println(this.name+"获得口红的锁");Thread.sleep(1000);synchronized (mirror){//一秒钟后想获得镜子System.out.println(this.name+"获得镜子的锁");}}}else{synchronized (mirror){//获得镜子的锁System.out.println(this.name+"获得镜子的锁");Thread.sleep(1000);synchronized (lipstick){//一秒钟后想获得镜子System.out.println(this.name+"获得镜子的锁");}}}}
}
避免方法
避免一个同步锁进程中同时获取两个资源
4.5Lock接口锁,ReentrantLock实现
手动打开关闭(更方便使用)
-
通过显示对象同步锁 Lock 来实现同步(和synchronized用法相似)
-
ReentrantLock (可重入锁)类实现Lock ,它拥有 synchronized 先相同的并发性和内存语义,在实现过程中更常用,可以显示加锁,释放锁
package multithreading3;import java.util.concurrent.locks.ReentrantLock;//测试 Lock 锁
public class TestLock {public static void main(String[] args) {TestLock2 testLock2 = new TestLock2();new Thread(testLock2).start();new Thread(testLock2).start();new Thread(testLock2).start();}
}class TestLock2 implements Runnable{int ticketNums = 10;//定义Lock锁private final ReentrantLock lock = new ReentrantLock();@Overridepublic void run() {while(true){try{lock.lock();//加锁if(ticketNums>0){try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println(ticketNums--);}else{break;}} catch (Exception e) {throw new RuntimeException(e);}finally {//解锁lock.unlock();}}}
}
5、线程协作(很难了解)
5.1生产者和消费者问题(通信问题)
所有对象都有一把锁
Java提供解决线程之间通信问题的方法
5.2解决方式
- 生产者将生产好的数据放入缓冲区,消费者再从缓冲区拿出数据
- 判断标志位(信号灯)
5.2.1方式1——管程法(wait(),notifiAll())
掌握 wait() 和 notifiAll() 方法的使用
package multithreading4;//测试生产者和消费者模型——利用缓冲区解决,管程法//生产者、消费者,产品,缓冲区
public class TestPC {public static void main(String[] args) {SynContainer container = new SynContainer();new Productor(container).start();new Consumer(container).start();}
}//生产者
class Productor extends Thread{SynContainer container;public Productor(SynContainer container){ //构造器方便我们创造对象this.container = container;}//生产@Overridepublic void run() {for (int i= 0; i <100;i++){container.push(new Chicken(i));System.out.println("生产了"+i+"只鸡");}}
}//消费者
class Consumer extends Thread{SynContainer container;public Consumer(SynContainer container){this.container = container;}//消费@Overridepublic void run() {for (int i = 0; i <100; i++) {System.out.println("消费者消费了第"+container.pop().id+"只鸡");}}
}//产品
class Chicken{int id;//产品编号public Chicken(int id) {this.id = id;}
}//缓冲区
class SynContainer{//需要一个容器大小Chicken[] chickens = new Chicken[10];//容器计数器int count =0;//生产者放入商品public synchronized void push(Chicken chicken){//如果容器满了,就需要等待消费者消费if(count==chickens.length){//通知消费者,生产等待try {this.wait();} catch (InterruptedException e) {throw new RuntimeException(e);}}//如果没有满,我们就需要丢入产品chickens[count] = chicken;count++;//可以通知消费者消费了this.notifyAll();}//消费者消费产品public synchronized Chicken pop(){//判断能否消费if(count==0){//等待生产者生产try {this.wait();} catch (InterruptedException e) {throw new RuntimeException(e);}}//如果可以消费count--;Chicken chicken = chickens[count];//吃完了,通知生产者生产this.notifyAll();return chicken;}
}
5.2.2方式2——信号灯法
package multithreading4;//测试生产者和消费者模型——利用标志位解决,信号灯法
public class TestPC2 {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;}@Overridepublic 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;}@Overridepublic 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) {throw new RuntimeException(e);}}System.out.println("演员表演了:"+voice);//通知观众观看this.notifyAll();//通知唤醒this.voice = voice;this.flag = !this.flag;}//观看public synchronized void watch(){if(flag){try {this.wait();} catch (InterruptedException e) {throw new RuntimeException(e);}}System.out.println("观看了:"+voice);//通知演员表演this.notifyAll();this.flag = !this.flag;}
}
5.3线程池(ExecutorService、Executors、execute)
- 减少了创建新线程的时间
- 重复利用线程池中的线程,不需要每次都创建
- 便于线程的管理
package multithreading4;import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;//测试线程池
public class TestPool {public static void main(String[] args) {//1.用线程池服务(ExecutorService)创建线程池大小(Executors)ExecutorService service = Executors.newFixedThreadPool(10);//使用(execute)执行service.execute(new MyThread());service.execute(new MyThread());service.execute(new MyThread());service.execute(new MyThread());//2.关闭连接service.shutdown();}
}class MyThread implements Runnable{@Overridepublic void run() {System.out.println(Thread.currentThread().getName());}
}