多线程的创建和使用
多线程
点击标题旁可查看目录
一.基本概念
1.线程与进程相似,但线程是一个比进程更小的执行单位。一个进程在其执行的过程中可以产生多个线程。与进程不同的是同类的多个线程共享同一块内存空间和一组系统资源,所以系统在产生一个线程,或是在各个线程之间作切换工作时,负担要比进程小得多,也正因为如此,线程也被称为轻量级进程。
2.程序是含有指令和数据的文件,被存储在磁盘或其他的数据存储设备中,也就是说程序是静态的代码。
3.进程是程序的一次执行过程,是系统运行程序的基本单位,因此进程是动态的。系统运行一个程序即是一个进程从创建,运行到消亡的过程。简单来说,一个进程就是一个执行中的程序,它在计算机中一个指令接着一个指令地执行着,同时,每个进程还占有某些系统资源如CPU时间,内存空间,文件,文件,输入输出设备的使用权等等。换句话说,当程序在执行时,将会被操作系统载入内存中。
线程状态
二.线程实现
1.线程创建
1.1继承Thread类(重要)
Thread类实现了Runnable接口,在Thread类中,有一些比较关键的属性,比如name是表示Thread的名字,可以通过Thread类的构造器中的参数来指定线程名字。
自定义线程类继承Thread
类
重写run()
方法,编写线程执行体
创建线程对象,调用start()
方法启动线程
实现
/** 创建线程方法一: 1.集成Thread类 2.重写run方法 3.调用start开启线程 线程开启不一定马上执行由cpu调度分配 **/ public class TestThread extends Thread{ @Override public void run() { //run方法线程体 for (int i = 0; i < 20; i++) { System.out.println("我在看代码..."+i); } } //main 主方法 public static void main(String[] args) { //创建一个线程对象 TestThread testThread = new TestThread(); //调用start方法开启线程 testThread.start(); for (int i = 0; i < 100; i++) { System.out.println("我在学习多线程..."+i); } } }
1.2实现Runnable接口(很重要)和出现的并发问题
推荐使用Runnable对象,因为Java单继承的局限性
自定义线程类实现Runnable
接口
实现run()
方法,编写线程执行体
创建线程对象,调用start()
方法启动对象
实现1:火车票
//多个线程同时操作同一个对象 //买火车票的例子 //黄牛-->拿到了第9票 //小王-->拿到了第10票 //老师-->拿到了第9票 //发现问题,多个线程操作同一个资源的情况下,线程不安全,数据紊乱 public class TestThread04 implements Runnable{ private int ticketNums = 10; @Override public void run() { while(true){ if(ticketNums<=0){ break; } System.out.println(Thread.currentThread().getName()+"-->拿到了第"+ticketNums--+"票"); } } public static void main(String[] args) { TestThread04 ticket = new TestThread04(); new Thread(ticket,"小王").start(); new Thread(ticket,"老师").start(); new Thread(ticket,"黄牛").start(); } }
实现2:网图下载
import org.apache.commons.io.FileUtils; import java.io.File; import java.io.IOException; import java.net.URL; //练习Runnable下载图片 public class TestThread02 implements Runnable{ private String url; private String name; public TestThread02(String url,String name){ this.url = url; this.name = name; } @Override public void run() { WebDownLoader webDownLoader = new WebDownLoader(); webDownLoader.downloader(url,name); System.out.println("下载了文件名为:"+name); } public static void main(String[] args) { TestThread02 t1 = new TestThread02("https://cn.bing.com/images/search?view=detailV2&ccid=U20mfsGg&id=3C25879C970B2A19A70A37C6405B472C3020BB1F&thid=OIP.U20mfsGgNFk5PK7Im_-OeAHaGB&mediaurl=https%3a%2f%2fts1.cn.mm.bing.net%2fth%2fid%2fR-C.536d267ec1a03459393caec89bff8e78%3frik%3dH7sgMCxHW0DGNw%26riu%3dhttp%253a%252f%252fimg.pconline.com.cn%252fimages%252fupload%252fupc%252ftx%252fphotoblog%252f1911%252f12%252fc1%252f178226108_1573517760033.jpg%26ehk%3d1yebtJchjzZR8q10NfiY6u520bFl7iebWFwon46LYPA%253d%26risl%3d%26pid%3dImgRaw%26r%3d0&exph=2361&expw=2901&q=%e9%b8%9f&simid=608006763565768368&FORM=IRPRST&ck=EA2E1D259E9C0AE87ECC6541E02DB1CA&selectedIndex=0","1.jpg"); TestThread02 t2 = new TestThread02("https://cn.bing.com/images/search?view=detailV2&ccid=dz%2f%2b5m9N&id=CE1825DCFCD4D67A5C77E55BF462213EAD433904&thid=OIP.dz_-5m9NKreH7s1qhcg6dAHaEK&mediaurl=https%3a%2f%2fpic.2amok.com%2f20200608%2fc72aa747c455968fdc2056277b20c83aoutput_00002.jpg&exph=2160&expw=3840&q=%e9%b8%9f&simid=607996455668821613&FORM=IRPRST&ck=E898107272DC6BCA8997246EAE3D7E90&selectedIndex=34","2.jpg"); TestThread02 t3 = new TestThread02("https://cn.bing.com/images/search?view=detailV2&ccid=w6HXivfB&id=940D7D65A9B5A53CB805DFC8563FF9276A94877C&thid=OIP.w6HXivfByykh5zpNTrXFlwHaEj&mediaurl=https%3a%2f%2fts1.cn.mm.bing.net%2fth%2fid%2fR-C.c3a1d78af7c1cb2921e73a4d4eb5c597%3frik%3dfIeUaif5P1bI3w%26riu%3dhttp%253a%252f%252fpic.baike.soso.com%252fp%252f20140624%252f20140624162352-1667529285.jpg%26ehk%3dBp%252bQEnz4b5CE00uF1uUlPMUjk8qlwsx%252fxrz2SigCDBQ%253d%26risl%3d%26pid%3dImgRaw%26r%3d0&exph=640&expw=1042&q=%e9%b8%9f&simid=608024695053709926&FORM=IRPRST&ck=D43356B9157125E88F69FA6C989FE913&selectedIndex=73","3.jpg"); new Thread(t1).start(); new Thread(t2).start(); new Thread(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异常"); } } }
实现3:龟兔赛跑
操作的是公共方法run,多线程允许各自的
//模拟龟兔赛跑 public class Race 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(1); } 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; }{ if(steps>=100){ winner = Thread.currentThread().getName(); System.out.println("winner is"+winner); return true; } } return false; } public static void main(String[] args) { Race race = new Race(); new Thread(race,"兔子").start(); new Thread(race,"乌龟").start(); } }
1.3实现Callable接口(了解)
实现Callable接口,需要返回值类型
重写call方法,需要抛出异常
创建目标对象
创建执行服务:ExecutorService ser = Executors.newFixedThreadPool(1);
提交执行:Future result1 = ser.submit(11);
获取结果:boolean r1 = result1.get()
关闭服务:ser.shutdownNow();
/** * 实现Callable接口 */ public class Demo6_CreateCallable implements Callable<Boolean> { private String url;//网络图片地址 private String name;//报错扥文件名 //有参构造 public Demo6_CreateCallable(String url, String name) { this.url = url; this.name = name; } //下载图片线程的执行体 public Boolean call() throws Exception { WebDownloader webDownloader = new WebDownloader(); webDownloader.downloader(url, name); System.out.println("下载了文件名为:" + name); return true; } public static void main(String[] args) throws ExecutionException, InterruptedException { Demo6_CreateCallable c = new Demo6_CreateCallable("https://img-home.csdnimg.cn/images/20201124032511.png", "1.png"); Demo6_CreateCallable c1 = new Demo6_CreateCallable("https://img-home.csdnimg.cn/images/20201124032511.png", "2.png"); Demo6_CreateCallable c2 = new Demo6_CreateCallable("https://img-home.csdnimg.cn/images/20201124032511.png", "3.png"); //创建执行服务 ExecutorService ser = Executors.newFixedThreadPool(3); //提交执行 Future<Boolean> r = ser.submit(c); Future<Boolean> r1 = ser.submit(c1); Future<Boolean> r2 = ser.submit(c2); //获取结果 boolean res = r.get(); boolean res1 = r1.get(); boolean res2 = r2.get(); //关闭服务 ser.shutdownNow(); } } //class WebDownloader在前面下载图片已经定义了,这里就不用再次写,直接使用就好
1.4Thread和Runnable的区别
7.currentThread()下面四个方法需要此方法调用
用来获取当前线程
举例
Thread.currentThread().getName()
8.getId
用来得到线程ID
9.getName和setName
用来得到或者设置线程名称。
10.getPriority和setPriority
用来获取和设置线程优先级。
11.setDaemon和isDaemon
用来设置线程是否成为守护线程和判断线程是否是守护线程。
守护线程和用户线程的区别在于:守护线程依赖于创建它的线程,而用户线程则不依赖。举个简单的例子:如果在main线程中创建了一个守护线程,当main方法运行完毕之后,守护线程也会随着消亡。而用户线程则不会,用户线程会一直运行直到其运行完毕。在JVM中,像垃圾收集器线程就是守护线程。
2.静态代理
静态代理模式总结:
真实对象和代理对象都要实现同一个接口
代理对象要代理真实角色
好处
代理对象可以做很多真实对象做不了的事情
真实对象专注做自己的事
实现:结婚案例
//静态代理模式: //真实对象和代理对象都要实现同一个接口 //代理对象要代理真实角色 //好处: // 代理对象可以做很多真实对象做不了的事情 // 真实对象专注做自己的事情 public class StaticProxy { public static void main(String[] args) { // WeddingCompany weddingCompany = new WeddingCompany(new You()); // weddingCompany.HappyMarry(); //简化 new WeddingCompany(new You()).HappyMarry(); //非常类似 Thread的底层同样是静态代理 //new Thread( ()-> System.out.println("999")).start(); } } interface Marry{ void HappyMarry(); } //真实角色,你去结婚 class You implements Marry{ @Override public void HappyMarry() { System.out.println("结婚了,超开心"); } } //代理角色,帮助你去结婚 class WeddingCompany implements Marry{ private You you; public WeddingCompany(You you){ this.you = you; } @Override public void HappyMarry() { before(); this.you.HappyMarry(); after(); } private void after() { System.out.println("结婚之后,收尾款"); } private void before() { System.out.println("结婚之前,布置现场"); } }
优化:使用线程,Lamda表达式
/** * 线程中的代理模式 */ public class Demo8_StaticProxy { public static void main(String[] args) { //() -->代之run()方法 因为函数式接口 new Thread(()-> System.out.println("我爱你")).start(); new WeddingCompany(new You()).happyMarry(); } } //WeddingCompany...上一个文件定义过了这里就直接使用了
3.Lamda表达式
1.Lamda使用演化
!!!什么是函数式接口 : 一个接口只有一个抽象方法
//演化lamda的使用 不带参数 //避免匿名内部类定义过多 //简化代码 public class TestLamda { //3.静态内部内 static class Like2 implements ILike{ @Override public void lamda() { System.out.println("I like lamda2"); } } public static void main(String[] args) { ILike like = new Like(); like.lamda(); like = new Like2(); like.lamda(); //4.局部内部类 class Like3 implements ILike{ @Override public void lamda() { System.out.println("I like lamda3"); } } like = new Like3(); like.lamda(); //5.匿名内部类 like = new ILike() { @Override public void lamda() { System.out.println("I like lamda4"); } }; like.lamda(); //6.用lamda简化 like = () -> { System.out.println("I like lamda5"); }; like.lamda(); } } //1.定义一个函数式接口 一个接口只有一个抽象方法 interface ILike{ void lamda(); } //2.实现类 外部类 class Like implements ILike{ @Override public void lamda() { System.out.println("I like lamda"); } }
2.Lamda使用简化
//带有参数的 //lamda //lamda表达式只能有一行代码的情况下才能简化为一行,如果有多行,必须用代码块包裹 //love = (a,b,c) -> System.out.println("I Love you-->"+a); //前提是接口为函数式接口 //多个参数也可以去掉参数类型,要去掉都要去掉必须一致,必须要加上括号 //如果只要一个可以去掉括号 //love = a -> System.out.println("I Love you-->"+a); public class TestLamda2 { public static void main(String[] args) { ILove love = null; //lamda 简化 去掉int 参数类型 (主要使用) love = (a,b,c) ->{ System.out.println("I Love you-->"+a); System.out.println("I Love you-->"+b); }; love.love(521,522,523); } } interface ILove{ void love(int a,int b,int c); }
三.线程状态
1.线程停止(使用标志位)
//测试线程停止 //1.建议线程正常停止---->利用次数,不建议死循环 //2.建议使用标志位--->设置一个标志位 //3.不要使用stop或者destroy等过时或者jdk不建议使用的方法 public class TestStop implements Runnable{ //设置一个标志位 private boolean flag = true; @Override public void run() { int i=0; while (flag){ System.out.println("run....thread"+i++); } } //2.设置一个公开的方法停止线程,转换标志位 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("线程该停止了"); } } } }
2.线程休眠(sleep)
2.1模拟网络延迟
//模拟网络延迟 public class TestSleep implements Runnable{ private int ticketNums = 10; @Override public void run() { while(true){ if(ticketNums<=0){ break; } //模拟延时 try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"-->拿到了第"+ticketNums--+"票"); } } public static void main(String[] args) { TestSleep ticket = new TestSleep(); new Thread(ticket,"小王").start(); new Thread(ticket,"老师").start(); new Thread(ticket,"黄牛").start(); } }
2.2模拟倒计时
//模拟倒计时 public class TestSleep2 { public static void main(String[] args) throws InterruptedException { tenDown(); } public static void tenDown() throws InterruptedException { int num = 10; while (true){ Thread.sleep(1000); System.out.println(num--); if(num<=0){ break; } } } }
2.3不断获取系统当前时间
//打印系统当前时间 public class TestSleep3 { public static void main(String[] args) { //获取当前时间 Date startTime = new Date(System.currentTimeMillis()); while (true){ try { Thread.sleep(1000); System.out.println(new SimpleDateFormat("HH:mm:ss").format(startTime)); startTime = new Date(System.currentTimeMillis()); } catch (InterruptedException e) { e.printStackTrace(); } } } }
3.线程礼让(Yield)
案例:
/** * 测试礼让线程 * 礼让不一定成功,看cpu心情 */ public class Demo19_YieldThread { public static void main(String[] args) { MyYeild myYeild = new MyYeild(); new Thread(myYeild, "a").start(); new Thread(myYeild, "b").start(); } } class MyYeild implements Runnable { @Override public void run() { System.out.println(Thread.currentThread().getName() + "线程开始执行"); Thread.yield();//礼让 System.out.println(Thread.currentThread().getName() + "线程停止执行"); } }
4.线程插队(join少用)
实现:
/** * 测试join阻塞 * 理解为插队 直接join后其他线程 少用 */ public class Demo20_JoinThread implements Runnable { @Override public void run() { for (int i = 0; i < 500; i++) { System.out.println("线程vip" + i); } } public static void main(String[] args) throws InterruptedException { //启动我们的线程 Demo20_JoinThread joinThread = new Demo20_JoinThread(); Thread thread = new Thread(joinThread); thread.start(); //主线程 for (int i = 0; i < 500; i++) { if (i == 200) { thread.join();//插队 } System.out.println("main" + i); } } }
5.观测线程状态
实现
/** * 观察测试线程状态 */ public class Demo21_ThreadState { 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); //观察启动后 thread.start(); state = thread.getState(); System.out.println(state);//Run while (state != Thread.State.TERMINATED) {//只要现成不终止,就一直输出状态 Thread.sleep(100); state = thread.getState();//更新线程状态 System.out.println(state); } //死亡后的线程不能再启动了,启动会报异常 //thread.start(); } }
6.线程优先级
实现:
/** * 线程优先级 */ public class Demo22_ThreadPriority{ public static void main(String[] args) { //主线程默认优先级 System.out.println(Thread.currentThread().getName()+"-->"+Thread.currentThread().getPriority()); MyPriority myPriority = new MyPriority(); Thread thread1 = new Thread(myPriority); Thread thread2 = new Thread(myPriority); Thread thread3 = new Thread(myPriority); Thread thread4 = new Thread(myPriority); Thread thread5 = new Thread(myPriority); //先设置优先级,再启动 thread1.start(); thread2.setPriority(1); thread2.start(); thread3.setPriority(4); thread3.start(); thread4.setPriority(Thread.MAX_PRIORITY);//MAX_PRIORITY=10 thread4.start(); thread5.setPriority(8); thread5.start(); } } class MyPriority implements Runnable{ @Override public void run() { System.out.println(Thread.currentThread().getName()+"-->"+Thread.currentThread().getPriority()); } }
7.守护线程
/** * 守护线程 * *jvm不用等待守护线程执行完成 *用户线程执行完毕 jvm结束 守护线程随之结束 */ public class TestDaemon { public static void main(String[] args) { God god = new God(); You you = new You(); Thread thread = new Thread(god); //默认false表示是用户线程,正常的线程都是用户线程... thread.setDaemon(true); //上帝守护线程启动 thread.start(); //你 用户线程启动 new Thread(you).start(); } } //上帝 class God implements Runnable{ @Override public void run() { while (true){ System.out.println("上帝保佑着你"); } } } //你 class You implements Runnable{ @Override public void run() { for (int i = 0; i < 36500; i++) { System.out.println("你一生都开心的活着"); } System.out.println("====goodbye!world===="); } }
四.线程同步
1.介绍
**多个线程操作同一个资源 **
2.不安全的线程案例
案例1:
//不安全的买票 public class UnsafeBuyTicket { public static void main(String[] args) { BuyTicket ticket = new BuyTicket(); new Thread(ticket,"我").start(); new Thread(ticket,"你").start(); new Thread(ticket,"黄牛").start(); } } class BuyTicket implements Runnable{ //票 private int TicketNums = 10; //标志位 boolean flag = true; @Override public void run() { //买票 while (flag){ //外部方式实现线程中断 安全 try { buy(); } catch (InterruptedException e) { e.printStackTrace(); } } } public void buy() throws InterruptedException { if(TicketNums<=0){ flag =false; return ; } Thread.sleep(200); System.out.println(Thread.currentThread().getName()+"拿到了"+TicketNums--); } } 我拿到了10 你拿到了8 黄牛拿到了9 你拿到了6 我拿到了5 黄牛拿到了7 我拿到了4 黄牛拿到了3 你拿到了4 我拿到了2 黄牛拿到了2 你拿到了2 我拿到了1 黄牛拿到了1 你拿到了0
案例2:
/** * 不安全的银行取钱 */ public class UnsafeBank { public static void main(String[] args) { Account account = new Account(100, "结婚基金"); Drawing you = new Drawing(account, 50, "你"); Drawing girlfriend = new Drawing(account, 100, "她"); you.start(); girlfriend.start(); } } //账户 class Account { int money;//余额 String cardName;//卡名 public Account(int money, String cardName) { this.money = money; this.cardName = cardName; } } //银行:模拟取款 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; } //取钱 @Override public void run() { //判断是否有钱 if (account.money - drawingMoney < 0) { System.out.println(Thread.currentThread().getName() + "余额不足,不能进行取钱"); return; } try { Thread.sleep(1000);//放大问题的发生性 } catch (InterruptedException e) { e.printStackTrace(); } //卡内金额 = 余额-你的钱 account.money = account.money - drawingMoney; //你手里的钱 nowMoney = nowMoney + drawingMoney; System.out.println(account.cardName + "余额为:" + account.money); //this.getName()==Thread.currentThread().getName() System.out.println(this.getName() + "手里的钱:" + nowMoney); } }
案例3:
//线程不安全的集合 public class UnsafeList { public static void main(String[] args) { List<String> list = new ArrayList<String>(); for (int i = 0; i < 1000; i++) { new Thread(()->{ list.add(Thread.currentThread().getName()); }).start(); } System.out.println(list.size()); } }
3.线程同步方法
同步方法,锁的是this
实现:
//安全买票 public class SafeBuyTicket { public static void main(String[] args) { BuyTicket1 buyTicket = new BuyTicket1(); new Thread(buyTicket, "张三").start(); new Thread(buyTicket, "李四").start(); new Thread(buyTicket, "王五").start(); } } class BuyTicket1 implements Runnable { //票 private int ticketNums = 10; boolean flag = true; @Override public void run() { //买票 while (flag) { try { buy(); } catch (Exception e) { e.printStackTrace(); } } } //synchronized 同步方法,锁的是this private synchronized void buy() { //判断是否有票 if (ticketNums <= 0) { flag = false; return; } //延迟 try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } //买票 System.out.println(Thread.currentThread().getName() + "拿到" + ticketNums--); } }
4.线程同步块
锁的对象就是变量的量,需要增删改查的对象
实现1:
/** * 安全的取钱 同步块 */ public class SafeBank { public static void main(String[] args) { Account1 account = new Account1(100, "结婚基金"); Drawing1 you = new Drawing1(account, 50, "展堂"); Drawing1 girlfriend = new Drawing1(account, 100, "sad"); you.start(); girlfriend.start(); } } //账户 class Account1 { int money;//余额 String cardName;//卡名 public Account1(int money, String cardName) { this.money = money; this.cardName = cardName; } } //银行:模拟取款 class Drawing1 extends Thread { Account1 account;//账户 int drawingMoney;//取金额 int nowMoney;//你手里的钱 public Drawing1(Account1 account, int drawingMoney, String name) { super(name); this.account = account; this.drawingMoney = drawingMoney; } //取钱 @Override public void run() { //锁的对象就是变量的量,需要增删改查的对象 synchronized (account) { //判断是否有钱 if (account.money - drawingMoney < 0) { System.out.println(Thread.currentThread().getName() + "余额不足,不能进行取钱"); return; } try { Thread.sleep(1000);//放大问题的发生性 } catch (InterruptedException e) { e.printStackTrace(); } //卡内金额 = 余额-你的钱 account.money = account.money - drawingMoney; //你手里的钱 nowMoney = nowMoney + drawingMoney; System.out.println(account.cardName + "余额为:" + account.money); //this.getName()==Thread.currentThread().getName() System.out.println(this.getName() + "手里的钱:" + nowMoney); } }
实现2:
//线程安全的集合 同步块 public class SafeList { public static void main(String[] args) { List<String> list = new ArrayList<String>(); for (int i = 0; i < 1000; i++) { new Thread(() -> { synchronized (list) { list.add(Thread.currentThread().getName()); } }).start(); } try { Thread.sleep(300); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(list.size()); } }
5.CopyOnWriteArrayList(JUC的安全list集合)
//测试JUC安全类型的集合 public class Demo30_ThreadJuc { public static void main(String[] args) { CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<String>(); for (int i = 0; i < 10000; i++) { new Thread(() -> { list.add(Thread.currentThread().getName()); }).start(); } try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(list.size()); } }
六.死锁
案例:
/** * 死锁:多个线程互相抱着对方需要的资源,然后形成僵持 * 解决:一个锁只锁一个对象 */ class Demo31_DeadLock { public static void main(String[] args) { Makeup makeup = new Makeup(0, "灰姑娘"); Makeup makeup1 = new Makeup(1, "白雪公主"); makeup.start(); makeup1.start(); } } //口红 class Lipstick { } //镜子 class Mirror { } class Makeup extends Thread { //需要的资源只有一份,用static保证只有一份 static Lipstick lipstick = new Lipstick(); static Mirror mirror = new Mirror(); int choice;//选择 String girlName;//使用化妆品的人 public Makeup(int choice, String girlName) { this.choice = choice; this.girlName = girlName; } @Override public void run() { //化妆 try { makeup(); } catch (InterruptedException e) { e.printStackTrace(); } } private void makeup() throws InterruptedException { if (choice == 0) { synchronized (lipstick) {//获得口红的锁 System.out.println(this.girlName + "获得口红的锁"); Thread.sleep(1000); synchronized (mirror) {//一秒钟后想获得镜子 System.out.println(this.girlName + "获得镜子的锁"); } } } else { synchronized (mirror) {//获得口红镜子 System.out.println(this.girlName + "获得镜子的锁"); Thread.sleep(2000); synchronized (lipstick) {//二秒钟后想获得的锁 System.out.println(this.girlName + "获得口红的锁"); } } } } }
解决方法:
/** * 死锁:多个线程互相抱着对方需要的资源,然后形成僵持 * 解决:一个锁只锁一个对象 */ private void makeup() throws InterruptedException { if (choice == 0) { synchronized (lipstick) {//获得口红的锁 System.out.println(this.girlName + "获得口红的锁"); Thread.sleep(1000); } synchronized (mirror) {//一秒钟后想获得镜子 System.out.println(this.girlName + "获得镜子的锁"); } } else { synchronized (mirror) {//获得口红镜子 System.out.println(this.girlName + "获得镜子的锁"); Thread.sleep(2000); } synchronized (lipstick) {//二秒钟后想获得的锁 System.out.println(this.girlName + "获得口红的锁"); } } }
七.Lock锁
如果线程中没有异常就不需要try Finally
案例:
//测试 可重入锁 lock public class TestLock { public static void main(String[] args) { Testlock2 lock = new Testlock2(); new Thread(lock).start(); new Thread(lock).start(); new Thread(lock).start(); } } class Testlock2 implements Runnable{ int ticketNums=10; //定义 可重入锁lock private final ReentrantLock lock = new ReentrantLock(); @Override public void run() { while (true){ try { lock.lock(); //加锁 if(ticketNums<=0){ break; }else { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(ticketNums--); } } finally { //解锁 lock.unlock(); } } } }
八.synchroized与Lock对比
五.线程通信
1.管程法: 利用缓冲区解决
/** * 测试生产者消费者模型 方法一 利用缓冲区解决:管程法 * * 生产者 消费者 产品 缓冲区 */ 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; } //生产 @Override public void run() { for (int i = 1; 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; } //消费 @Override public void run() { for (int i = 1; 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的话,假如消费者1消费了最后一个,这是index变成0此时释放锁被消费者2拿到而不是生产者拿到, 这时消费者的wait是在if里所以它就直接去消费index-1下标越界,如果是while就会再去判断一下index得值是不是变成0了*/ while (count == chickens.length){ //通知消费者消费,生产等待 try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } //如果容器没有满。我们就需要丢人产品 chickens[count] = chicken; count++; //可以通知消费者消费了 this.notifyAll(); } //消费者消费产品 public synchronized Chicken pop(){ //判断能否消费 if(count<=0){ //等待生产者生产,消费者等待 try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } //如果可以消费 count--; Chicken chicken = chickens[count]; //吃完了,痛着生产者生产 this.notifyAll(); return chicken; } }
2.信号灯法: 标志位解决
/** * 并发协作模型“生产者消费者问题2”: 信号灯法: 标志位解决 */ 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; } @Override public void run() { for (int i = 0; i < 20; i++) { if(i%2==0){ tv.play("快乐大本营"); }else { 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; //演员在表演,观众在等待为T //观众在观看,演员在等待为F boolean flag = true; //表演 public synchronized void play(String voice){ if(!flag){ try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("演员在表演"+voice); //通知观众观看 this.voice=voice; //传值进来后,需要更新类中的voice的值,不然watch接收不到 notifyAll(); flag = !flag; } //观众 public synchronized void watch(){ if(flag){ try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("观众在观看"+voice); //通知演员表演 notifyAll(); flag = !flag; } }
3.线程池
实现:
/** *线程池 */ public class TestPool { public static void main(String[] args) { //1.创建服务,创建线程池 //newFixedThreadPool 参数为 池子大小 ExecutorService service = Executors.newFixedThreadPool(10); service.execute(new MyThread()); service.execute(new MyThread()); service.execute(new MyThread()); //关闭连接 service.shutdown(); } } class MyThread implements Runnable{ @Override public void run() { System.out.println(Thread.currentThread().getName()); } }
六.总结三种线程方法的创建和使用
//回顾总结线程的创建 public class ThreadNew { public static void main(String[] args) { new MyThread1().start(); new Thread(new MyThread2()).start(); FutureTask<Integer> futureTask = new FutureTask<Integer>(new MyThread3()); new Thread(futureTask).start(); try { Integer integer = futureTask.get(); System.out.println(integer); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } } //1.继承Thread类 class MyThread1 extends Thread{ @Override public void run() { System.out.println("MyThread1"); } } //2.实现Runnable接口 class MyThread2 implements Runnable{ @Override public void run() { System.out.println("MyThread2"); } } //3.创建callable接口 class MyThread3 implements Callable<Integer>{ @Override public Integer call() throws Exception { System.out.println("MyThread3"); return 100; } }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异
· 三行代码完成国际化适配,妙~啊~