3.3 线程状态观测
Thread.State
线程状态。线程可以处于以下状态之一:
- NEW
尚未启动的线程处于此状态。 - RUNNABLE
在 Java 虚拟机中执行的线程处于此状态。 - BLOCKED
被阻塞等待监视器锁定的线程处于此状态。 - WAITING
正在等待另一个线程执行特定动作的线程处于此状态。 - TERMINATED
已退出的线程处于此状态。
一个线程可以在给定时间点处于一个状态。
package com.can.ThreadState; //观察测试线程的状态 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();//启动线程 System.out.println(thread.getState()); //Run while (state != Thread.State.TERMINATED){ //只要线程不终止,就一直输出状态。 Thread.sleep(100); state = thread.getState(); System.out.println(state); } //死亡之后的线程是不能在启动的Exception in thread "main" java.lang.IllegalThreadStateException //thread.start(); } }
3.4 线程优先级
Java 提供一个线程调度器来监控程序中启动后进入就绪状态的所有线程,线程调度器按照优先级决定应该调度哪个线程来执行。
线程的优先级用数字表示,范围从1~10.
Thread.MIN_PRIORITY=1;
Thread.MAX_PRIORITY=10;
Thread.NORM_PRIORITY=5;
使用以下方式改变或获取优先级
getPriority().setPriority(int xxx)
优先级的设定建议在 start() 调度前
package com.example.democrud.democurd.test01; //测试线程的优先级,由高到低,越高越容易优先,但不一定 //优先级低只意味着获取调度的概念低,并不是优先级低就不会被优先调用,这都是看CPU的调度。 public class testPriority { public static void main(String[] args) { //主线程 优先级默认 是5 无法修改 System.out.println("线程名字"+Thread.currentThread().getName()+"线程优先级"+Thread.currentThread().getPriority()); Priority priority = new Priority(); Thread t1 = new Thread(priority,"t1"); Thread t2 = new Thread(priority,"t2"); Thread t3 = new Thread(priority,"t3"); Thread t4 = new Thread(priority,"t4"); Thread t5 = new Thread(priority,"t5"); Thread t6 = new Thread(priority,"t6"); //注意需要先设置优先级 然后启动线程 t1.start(); //最小优先级 1 t2.setPriority(1); t2.start(); t3.setPriority(4); t3.start(); //最大线程优先级 10 t4.setPriority(Thread.MAX_PRIORITY); t4.start(); } } class Priority implements Runnable{ @Override public void run() { System.out.println("线程名字"+Thread.currentThread().getName()+"线程优先级"+Thread.currentThread().getPriority()); } }
注意:
优先级低只意味着获取调度的概念低,并不是优先级低就不会被优先调用,这都是看CPU的调度。
默认优先级都是5,优先级不能小于1大于10否则会抛出异常
3.5 守护(daemon)线程
- 线程分为用户线程和守护线程
- 虚拟机必须确保用户线程执行完毕
- 虚拟机不用等待守护线程执行完毕
- 如,后台记录操作日志,监控内存垃圾回收等待…
package com.example.democrud.democurd.test01; //测试守护线程 //上帝守护你 //所有的用户线程结束了,守护线程才会结束了 //当只剩下守护线程时,JVM就退出了,守护线程不依赖于终端,而依赖于系统,与系统同生共死 public class TestDaemon { public static void main(String[] args) { God god = new God(); Yous you = new Yous(); Thread thread = new Thread(god); //设置守护线程必须要在线程启动前进行 thread.setDaemon(true); //默认是false,表示是用户线程,正常线程都是用户线程,设置成true变为守护线程 thread.start(); new Thread(you).start(); // 你 用户线程启动.. } } //上帝 class God implements Runnable{ @Override public void run() { while (true){ System.out.println("上帝保佑着你"); } } } //你 class Yous implements Runnable{ @Override public void run() { for (int i = 0; i < 36500; i++) { System.out.println("你一生都开心的活着"); } System.out.println("-=========goodbye! world!=========="); }
3.6 线程同步(并发)
同一个对象被多个线程同时操作
现实生活中,我们会遇到”同一个资源,多个人都想使用”的问题,比如,食堂排队打饭,每个人都想吃饭,最天然的解决办法就是,排队一个个来。
处理多线程问题时,多个线程访问同一个对象,并且某些线程还想修改这个对象.这时候我们就需要线程同步.线程同步其实就是一种等待机制,多个需要同时访问!此对象的线程进入这个对象的等待池形成队列,等待前面线程使用完毕,下一个线程再使用。
形成线程安全的条件:
队列和锁:
在多线程场合下,最重要的就是保障数据的一致性问题,而保障数据一致性问题,就需要借助于锁了。
其实我们在多线程的场景下应该搞清楚一个问题,就是到底什么需要保护?并不是所有的的数据都需要加锁保护,只有那些涉及到被多线程访问的共享的数据才需要加锁保护。
锁的本质其实就是确保在同一时刻,只有一个线程在访问共享数据,那么此时该共享数据就能得到有效的保护。
4、线程同步
由于同一进程的多个线程共享同一块存储空间,在带来方便的同时,也带来了访问冲突问题,为了保证数据在方法中被访问时的正确性,在访问时加入锁机制synchronized,当一个线程获得对象的排它锁,独占资源,其他线程必须等待,使用后释放锁即可.存在以下问题:
- 一个线程持有锁会导致其他所有需要此锁的线程挂起;
- 在多线程竞争下,加锁,释放锁会导致比较多的上下文切换和调度延时,引起性能问题;
- 如果一个优先级高的线程等待一个优先级低的线程释放锁会导致优先级倒置,引起性能问题.
4.1 线程不安全举例
下面的售票,取钱示例,那么需要多线程模拟客户去抢票或者取钱。
举例:不安全的售票
package com.example.democrud.democurd.test01; //不安全的买票 //线程不安全 //3人 可能会拿到同一张票,甚至在最后一张票的时候可能都会将最后一张票执行,就会有-1 public class UnsafeBuyTick { 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 tickNums=10; Boolean flag=true; @Override public void run() { //买票 while (flag){ try { buy(); } catch (InterruptedException e) { e.printStackTrace(); } } } //判断是否有票 public void buy() throws InterruptedException { //无票; if (tickNums<=0){ flag=false; return; } Thread.sleep(1000); System.out.println(Thread.currentThread().getName()+"拿到了,第"+tickNums--); } }
举例:银行取钱\
package com.example.democrud.democurd.test01; import com.example.democrud.democurd.Prototype.demo01.Video; //不安全的取钱 //两人去银行取钱 public class testUnsafeBank { public static void main(String[] args) { Account account = new Account(100, "结婚"); Drawing you = new Drawing(account,50,"你"); Drawing girl = new Drawing(account,100,"媳妇"); you.start(); girl.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; //现在手里有多少钱 String name; public Drawing(Account account, int drawingMoney, String name) { this.account = account; this.drawingMoney = drawingMoney; this.name = name; } //取钱 @Override public void run(){ if (account.money-drawingMoney<0){ System.out.println(Thread.currentThread().getName()+"取了"+drawingMoney+"钱不够了抱歉"); } try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } //卡内余额=余额-取的钱 account.money=account.money-drawingMoney; //手里的钱+取的钱 nowMoney=nowMoney+drawingMoney; System.out.println(account.name+"余额:"+account.money); System.out.println(this.name+"手里的钱"+nowMoney); } }
举例:线程不安全的集合
可参考:ArrayList为什么是线程不安全的:https://blog.csdn.net/qq_42183409/article/details/100586255
package com.can.syn; import java.util.ArrayList; import java.util.List; //线程不安全的集合 //不够安全的原因是因为,两个线程同一瞬间,操作了同一个位置上,把两个数组添加到了同一位置,就覆盖掉了,元素就会少,少的元素就这么来的 public class UnsafeList { public static void main(String[] args) throws InterruptedException { List<String> list = new ArrayList<>(); for (int i = 0; i < 10000; i++) { //lambda表达式 new Thread( ()->{ // synchronized (list) { 解决并发问题 list.add(Thread.currentThread().getName()); // } }).start(); } Thread.sleep(100); //输出大小 System.out.println(list.size()); } }
举例:JUC 线程安全的集合:CopyOnWriteArrayList
package com.example.democrud.democurd.test01; import java.util.concurrent.CopyOnWriteArrayList; public class TestJuc { public static void main(String[] args) { //list线程安全的 CopyOnWriteArrayList<String> list= new CopyOnWriteArrayList<String>(); for (int i = 0; i < 1000; i++) { new Thread(()->{ list.add(Thread.currentThread().getName()); }).start(); } try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(list.size()); } }