线程生命周期
- 创建:new一个Thread类的对象
- 就绪:调用start()方法,等待CPU调度
- 运行:系统为线程分配硬件资源后,线程按照核心业务的功能执行,但不是一直执行,需进行资源抢占,运行一段时间后一个线程就会让出当前的资源,然后重新等待调度
- 堵塞:当某一个线程对象让出了当前的资源,该线程对象进入阻塞状态(未完成代码暂不执行),除了系统的自动控制外,也可利用Thread类中的线程控制方法进行阻塞操作
- 终止:当线程方法全部执行完毕后,该线程将释放掉全部资源,并结束调用
线程命名
- Thread(Runnable target, String name):实例化线程对象,接收Runnable接口子类对象,同时设置线程名称
- setName(String name):设置线程名称
- getName(String name):取得线程名称
- cuurentThread():取得当前线程对象的方法
- 默认情况下返回的线程名为“Thread-xxx”,对象自动编号
- 调用run()的两种方式
- Thread线程对象接收Runnable子类对象实例,通过start()调用
- Runnable子类对象实例调用
- 每个JVM运行就是进程,每个JVM进程都至少启动两个线程
- main:程序的主要执行,以及启动子线程(Thread创建)
- gc:垃圾回收
- 写代码时,可将所有耗时操作利用子线程的形式进行定义,主线程完成主要功能操作,如子线程的创建、控制等
1 class MyThread implements Runnable{ 2 @Override 3 public void run() { 4 System.out.println(Thread.currentThread().getName()); 5 } 6 } 7 8 public class TestDemo { 9 public static void main(String[] args) throws Exception{ 10 MyThread mt = new MyThread(); 11 new Thread(mt,"自己的线程").start(); 12 mt.run(); 13 } 14 }
线程休眠
- sleep()
- java.lang.InterruptException:休眠时间不到,非正常结束时抛出
- 进入到run()的线程的个数可能非常多,但是所有线程一旦遇到休眠后将按照各自的时间进行休眠处理,由于线程执行速度非常块,所以从整个的执行上来讲,好像是若干个线程一起休眠,一起唤醒
- 重写run()时定义每个线程的操作,main()中创建若干个线程同时执行run(),加了sleep就是run()中的每步操作执行完都停顿一下
View Code
线程中断
- 一个线程无法自己进行中断,必须由其他线程帮助完成中断
- interrupt():线程中断
- isInterrupted():判断线程的中断状态
- 线程中断一旦产生,一定会产生一个与之匹配的InterruptedException
1 public class Sleep_Demo { 2 public static void main(String[] args) throws Exception{ 3 Thread thread = new Thread(()->{ 4 try { 5 System.out.println("["+Thread.currentThread().getName()+"]准备进入休眠状态,预计时间10秒..."); 6 Thread.sleep(10000); 7 System.out.println("["+Thread.currentThread().getName()+"]休眠状态正常结束..."); 8 } catch (InterruptedException e) { 9 System.out.println("["+Thread.currentThread().getName()+"]休眠产生异常,无法正常完成休眠..."); 10 } 11 },"休眠线程"); 12 thread.start(); 13 System.out.println("[中断状态]" + thread.isInterrupted()); 14 Thread.sleep(2000); 15 thread.interrupt(); 16 } 17 }
线程强制执行
- 正常情况下,线程间平等抢占资源
- 若某个线程非常重要,则需要强制性霸占资源,优先处理
- join():持续执行直到线程结束
1 public class Sleep_Demo { 2 public static void main(String[] args) throws Exception{ 3 Thread mainThread = Thread.currentThread(); 4 Thread joinThread = new Thread(()->{ 5 for(int x = 0 ; x < 1000 ; x ++ ){ 6 try { 7 Thread.sleep(1000); 8 if(x >= 10) { 9 mainThread.join(); // 子线程交出全部资源给主线程 10 } 11 System.out.println("["+Thread.currentThread().getName()+"]子线程执行,x = " + x); 12 }catch(InterruptedException e) { 13 e.printStackTrace(); 14 } 15 } 16 },"工作线程"); 17 joinThread.start(); // 启动子线程 18 for(int x = 0 ; x < 1000 ; x ++ ) { 19 Thread.sleep(1000); 20 System.out.println("[" + Thread.currentThread().getName() + "]线程执行,x = " + x); 21 } 22 } 23 }
线程礼让
- 运行时线程轮流进行CPU资源抢占,当一个线程抢占了资源后,可通过礼让的形式让出资源
- public static void yield():把到手的资源让给其他线程
- 实际开发中,由于会有若干个不同线程,需要为每一个线程设置一些名称标记表示其重要性(CEO、员工等)
1 public class Sleep_Demo { 2 public static void main(String[] args) throws Exception{ 3 Thread mainThread = Thread.currentThread(); 4 Thread joinThread = new Thread(()->{ 5 for(int x = 0 ; x < 1000 ; x ++ ){ 6 try { 7 if(x % 2 == 0) { 8 Thread.yield(); 9 System.out.println("[YIELD]线程礼让执行"); 10 } 11 Thread.sleep(1000); 12 System.out.println("["+Thread.currentThread().getName()+"]子线程执行,x = " + x); 13 }catch(InterruptedException e) { 14 e.printStackTrace(); 15 } 16 } 17 },"工作线程"); 18 joinThread.start(); // 启动子线程 19 for(int x = 0 ; x < 1000 ; x ++ ) { 20 Thread.sleep(1000); 21 System.out.println("[" + Thread.currentThread().getName() + "]线程执行,x = " + x); 22 } 23 } 24 }
线程优先级
- 线程优先级越高,越有可能先执行
- 优先级常量
- MAX_PRIORITY:最高优先级,数值10
- NORM_PRIORITY:中等优先级,数值5
- MIN_PRIORITY:最低优先级,数值1
- setPriority():获取线程优先级
- getPriority():修改线程优先级
- 主方法及子线程默认线程优先级为均为5
- 可在线程执行前修改优先级
- 线程的等待与唤醒
1 public class Sleep_Demo { 2 public static void main(String[] args) throws Exception{ 3 // 定义线程数组 4 Thread threads [] = new Thread[3]; 5 for(int x = 0 ; x < threads.length ; x ++ ) { 6 threads[x] = new Thread(()->{ 7 while(true) { 8 try { 9 Thread.sleep(1000); 10 System.out.println("["+Thread.currentThread().getName()+"]线程执行,线程优先级"+Thread.currentThread().getPriority()); 11 }catch(InterruptedException e){ 12 e.printStackTrace(); 13 } 14 } 15 }); 16 } 17 // 设置线程优先级 18 threads[0].setPriority(Thread.MIN_PRIORITY); 19 threads[2].setPriority(Thread.MIN_PRIORITY); 20 threads[1].setPriority(Thread.MAX_PRIORITY); 21 // 启动线程 22 for(int x = 0 ; x < threads.length; x ++) { 23 threads[x].start(); 24 } 25 } 26 }
同步与死锁
- 多线程目的:提升程序在单位时间内的处理性能
- 可能产生的问题:一个人盖房子/多个人盖房子
- 同步问题
- 问题原因:由于休眠的出现,导致了票数修改的操作延迟,其余的线程也可以通过票数判断,当休眠结束后,所有的线程都执行票数减少操作,导致数据的修改错误
- 类比:关起门上厕所,所有操作在门里完成,不受别人打扰
- 锁处理机制:在Java里通过同步代码块、同步方法实现
- 同步代码块:sychronized(锁定对象) 代码块,其中的操作在一个时间段内只能有一个线程执行,其他线程等此线程执行完后才可继续执行
- 同步方法:public sychronized 返回值 方法名称(){代码}
- 优点:保证程序的正确处理(线程安全性)
- 缺点:异步执行的速度远高于同步执行,同步代码会导致性能下降(例:银行取钱)
- 死锁问题
- 产生:同步后线程之间相互等待,A要等B释放资源后才可执行,在这之前A进入到阻塞状态
- 死锁:两个线程彼此等待对方先完成,造成了程序的停止状态
- 死锁可能随机出现,开发时不易排查,可使用工具观察
- 同步问题
- 多线程开发的两个模型
- 共同参与:考虑同步、死锁问题
- 生产者-消费者:
未加锁的程序:
1 public class Synchronize_Demo { 2 public static int ticket = 5; 3 public static void main(String[] args) throws Exception { 4 Runnable body = () -> { 5 while(true) { 6 if(ticket > 0) { 7 try { 8 Thread.sleep(1000); 9 }catch(InterruptedException e) { 10 e.printStackTrace(); 11 } 12 System.out.println("["+Thread.currentThread().getName()+"]售票,当前剩余票数:"+ (--ticket)); 13 }else { 14 break; 15 } 16 } 17 }; 18 for(int x = 0 ; x <5 ; x ++) { 19 new Thread(body,"票贩子-" + x).start(); 20 } 21 } 22 }
加锁的程序(同步代码块):
1 public class Synchronize_Demo { 2 public static int ticket = 5; 3 public static void main(String[] args) throws Exception { 4 Synchronize_Demo SynDemo = new Synchronize_Demo(); 5 Runnable body = () -> { 6 while(true) { 7 synchronized(SynDemo) { 8 if(ticket > 0) { 9 try { 10 Thread.sleep(1000); 11 }catch(InterruptedException e) { 12 e.printStackTrace(); 13 } 14 System.out.println("["+Thread.currentThread().getName()+"]售票,当前剩余票数:"+ (--ticket)); 15 }else { 16 break; 17 } 18 } 19 } 20 }; 21 for(int x = 0 ; x <5 ; x ++) { 22 new Thread(body,"票贩子-" + x).start(); 23 } 24 } 25 }
或(同步方法):
1 public class Synchronize_Demo { 2 public static int ticket = 5; 3 public static void main(String[] args) throws Exception { 4 Synchronize_Demo SynDemo = new Synchronize_Demo(); 5 Runnable body = () -> { 6 while(sale()) {} 7 }; 8 for(int x = 0 ; x <5 ; x ++) { 9 new Thread(body,"票贩子-" + x).start(); 10 } 11 } 12 13 public static synchronized boolean sale() { 14 if(ticket > 0) { 15 try { 16 Thread.sleep(1000); 17 }catch(InterruptedException e) { 18 e.printStackTrace(); 19 } 20 System.out.println("["+Thread.currentThread().getName()+"]售票,当前剩余票数:"+ (--ticket)); 21 return true; 22 }else { 23 return false; 24 } 25 } 26 }
死锁:
1 class Book{} 2 class Paint{} 3 4 public class DeadLock_Demo { 5 public static void main(String[] args) throws Exception{ 6 Book book = new Book(); 7 Paint paint = new Paint(); 8 Thread threadA = new Thread(()->{ 9 synchronized(book) { 10 System.out.println("A对B说:你给我你的画,我再借你我的书"); 11 try { 12 Thread.sleep(1000); 13 14 synchronized(paint) { 15 Thread.sleep(1000); 16 System.out.println("A得到了B的画"); 17 } 18 } catch (InterruptedException e) { 19 e.printStackTrace(); 20 } 21 } 22 }); 23 Thread threadB = new Thread(()->{ 24 synchronized(paint) { 25 System.out.println("B对A说:你借我你的书,我再给你我的画"); 26 try { 27 Thread.sleep(1000); 28 synchronized(book) { 29 Thread.sleep(1000); 30 System.out.println("B得到了A的书"); 31 } 32 } catch (InterruptedException e) { 33 e.printStackTrace(); 34 } 35 } 36 }); 37 threadA.start(); 38 threadB.start(); 39 } 40 }
生产者与消费者
- 协同处理
- 生产需要很多供需,如果有一道工序没有完成,则消费者无法取走商品
- 生产者生产力很强,而消费者消费能力弱,则当某个商品已经生产完但未取走时,生产者应等待消费者取走后再进行后续的生产
- 如果消费者能力很强,且发现没有新的商品可取走,则消费者需等待有新的产品生产出后再取走
- 生产者和消费者是两个独立的线程
- 线程的同步、等待、唤醒机制,在synchronized方法中使用,但容易导致死锁,实际开发多使用JUC编程
- wait():等待
- notify():唤醒
- 实际应用:消息组件
- 程序1问题
- 生产者没有完成信息生产时消费者就已经获取数据了
- 若消费者的性能高于生产者,则会出现信息重复消费的问题
- 若生产者的性能高于消费者,则会出现信息重复生产的问题
- 程序2问题
- 仅解决了多线程并行修改数据的问题,但没有实现多线程间彼此协作的沟通
程序1
1 class Message{ 2 private String title; 3 private String content; 4 5 public void setTitle(String title) { 6 this.title = title; 7 } 8 9 public void setContent(String content) { 10 this.content = content; 11 } 12 13 public String getTitle() { 14 return title; 15 } 16 17 public String getContent() { 18 return content; 19 } 20 } 21 22 class ProducerThread implements Runnable{ 23 private Message message; 24 public ProducerThread(Message message) { 25 this.message = message; 26 } 27 @Override 28 public void run() { 29 for(int x = 0 ; x < 50 ; x ++ ) { 30 try { 31 if(x % 2 == 0) { 32 this.message.setTitle("李兴华"); 33 Thread.sleep(1000); 34 this.message.setContent("老师"); 35 }else { 36 this.message.setTitle("沐言"); 37 Thread.sleep(1000); 38 this.message.setContent("www.yootk.com"); 39 } 40 }catch(Exception e) {} 41 } 42 } 43 } 44 45 class ConsumerThread implements Runnable{ 46 private Message message; 47 public ConsumerThread(Message message) { 48 this.message = message; 49 } 50 @Override 51 public void run() { 52 for(int x = 0 ; x < 50 ; x ++ ) { 53 System.out.println("[消费者]title="+this.message.getTitle()+", content = " + this.message.getContent()); 54 try { 55 Thread.sleep(1000); 56 } catch (InterruptedException e) { 57 e.printStackTrace(); 58 } 59 } 60 } 61 }
程序2(解决同步问题)
1 class Message{ 2 private String title; 3 private String content; 4 public synchronized void set(String title, String content) { 5 this.title = title; 6 try { 7 Thread.sleep(100); 8 } catch (InterruptedException e) { 9 e.printStackTrace(); 10 } 11 this.content = content; 12 } 13 public synchronized String get() { 14 try { 15 Thread.sleep(100); 16 } catch (InterruptedException e) { 17 e.printStackTrace(); 18 } 19 return "title = " + this.title + ", content = " + this.content; 20 } 21 } 22 23 class ProducerThread implements Runnable{ 24 private Message message; 25 public ProducerThread(Message message) { 26 this.message = message; 27 } 28 @Override 29 public void run() { 30 for(int x = 0 ; x < 50 ; x ++ ) { 31 if(x % 2 == 0) { 32 this.message.set("李兴华","老师"); 33 }else { 34 this.message.set("沐言","www.yootk.com"); 35 } 36 } 37 } 38 } 39 40 class ConsumerThread implements Runnable{ 41 private Message message; 42 public ConsumerThread(Message message) { 43 this.message = message; 44 } 45 @Override 46 public void run() { 47 for(int x = 0 ; x < 50 ; x ++ ) { 48 System.out.println("[消费者]"+this.message.get()); 49 } 50 } 51 } 52 53 public class PC_Demo { 54 public static void main(String[] args) throws Exception{ 55 Message message = new Message(); 56 new Thread(new ProducerThread(message)).start(); 57 new Thread(new ConsumerThread(message)).start(); 58 } 59 }
程序3(解决线程重复操作问题)
1 class Message{ 2 private String title; 3 private String content; 4 private boolean flag = true; 5 // flag = true:可以生产,但无法进行消费 6 // flag = false:可以消费,但无法进行生产 7 public synchronized void set(String title, String content) { 8 if(this.flag == false) { 9 try { 10 super.wait(); 11 } catch (InterruptedException e) { 12 e.printStackTrace(); 13 } 14 } 15 this.title = title; 16 try { 17 Thread.sleep(100); 18 } catch (InterruptedException e) { 19 e.printStackTrace(); 20 } 21 this.content = content; 22 this.flag = false; 23 super.notify(); 24 } 25 public synchronized String get() { 26 if(this.flag == true) { 27 try { 28 super.wait(); 29 } catch (InterruptedException e) { 30 e.printStackTrace(); 31 } 32 } 33 try { 34 Thread.sleep(100); 35 } catch (InterruptedException e) { 36 e.printStackTrace(); 37 } 38 this.flag = true; 39 super.notify(); 40 return "title = " + this.title + ", content = " + this.content; 41 } 42 } 43 44 class ProducerThread implements Runnable{ 45 private Message message; 46 public ProducerThread(Message message) { 47 this.message = message; 48 } 49 @Override 50 public void run() { 51 for(int x = 0 ; x < 50 ; x ++ ) { 52 if(x % 2 == 0) { 53 this.message.set("李兴华","老师"); 54 }else { 55 this.message.set("沐言","www.yootk.com"); 56 } 57 } 58 } 59 } 60 61 class ConsumerThread implements Runnable{ 62 private Message message; 63 public ConsumerThread(Message message) { 64 this.message = message; 65 } 66 @Override 67 public void run() { 68 for(int x = 0 ; x < 50 ; x ++ ) { 69 System.out.println("[消费者]"+this.message.get()); 70 } 71 } 72 } 73 74 public class PC_Demo { 75 public static void main(String[] args) throws Exception{ 76 Message message = new Message(); 77 new Thread(new ProducerThread(message)).start(); 78 new Thread(new ConsumerThread(message)).start(); 79 } 80 }
停止线程
1 class Thread_Stop implements Runnable{ 2 private boolean flag = false; //停止标志 3 @Override 4 public void run() { 5 for(int x = 0 ; x < 1000; x++) { 6 if(this.flag) { 7 break; 8 } 9 try { 10 Thread.sleep(100); 11 } catch (InterruptedException e) { 12 e.printStackTrace(); 13 } 14 System.out.println("[Message信息输出——" + x + "]沐言科技:www.yootk.com"); 15 } 16 } 17 public void stop() { 18 this.flag = true; 19 } 20 } 21 22 public class Daemon_Thread_Demo { 23 public static void main(String[] args) throws Exception{ 24 Thread_Stop message = new Thread_Stop(); 25 new Thread(message).start(); 26 Thread.sleep(500); 27 message.stop(); 28 } 29 }
守护线程
- 守护线程工作在后台,用于监控服务器的状态
1 class Daemon_Thread implements Runnable{ 2 public Daemon_Thread() { 3 Thread daemonThread = new Thread(()->{ 4 for(int x = 0 ; x < Integer.MAX_VALUE ; x++) { 5 try { 6 Thread.sleep(100); 7 } catch (InterruptedException e) { 8 e.printStackTrace(); 9 } 10 System.out.println("[守护线程]编程训练营"); 11 } 12 }); 13 daemonThread.setDaemon(true); //设置为守护线程 14 daemonThread.start(); //守护线程启动 15 } 16 @Override 17 public void run() { 18 for(int x = 0 ; x < 10; x++) { 19 try { 20 Thread.sleep(100); 21 } catch (InterruptedException e) { 22 e.printStackTrace(); 23 } 24 System.out.println("[Message信息输出——" + x + "]沐言科技:www.yootk.com"); 25 } 26 } 27 } 28 29 public class Daemon_Thread_Demo { 30 public static void main(String[] args) throws Exception{ 31 Daemon_Thread message = new Daemon_Thread(); //实例化 32 new Thread(message).start(); //启动线程 33 } 34 }
volatile关键字
- 多线程开发的本质在于多个线程彼此协作,更好地发挥硬件性能,在单位时间内执行更多的任务
- volatile定义的属性不具有同步操作的特点
- 传统程序变量的操作:
- 如果进行变量的修改,一定要通过主内存将某个变量的内容拷贝到线程的内存中,此时为一个变量副本
- 随后在线程内存里针对于变量的副本数据进行处理操作(赋值、修改等)
- 当所有线程内存中的变量修改完成后则需与主内存中的变量内容进行同步处理
- 线程内存和主内存间的变量就会有同步延迟
- volatile关键字的作用:不再进行副本的定义,直接操作主内存中的变量。避免了各种变量的拷贝及同步带来的延迟损耗(存在同步问题),可直接更快地访问主内存变量(直接进行主内存变量操作,可减少延迟,看起来好像是“同步”了)
- 使用volatile定义的变量可直接进行底层内存变量操作,使数据的同步更加高效