学习java的第四十天,线程的优先级、守护线程、线程同步机制、死锁
一、线程的优先级(priority)
- Java提供一个线程调度器来监控程序中启动后进入就绪状态的所有线程,线程调度器按照优先级决定应该调度哪个线程来执行。
- 线程的优先级用数字表示,范围1~10
- Thread.Min_Priority = 1; 最小等级
- Thread.Max_Priority = 10; 最大等级
- Thread.Norm_Priority = 5; 默认等级
- 使用以下方式改变或获取优先级
- getPriority 获取当前等级
- setPriority 改变当前等级(int 1~10)
package com.Thread.state;
//测试线程的优先级 Priority
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,"t1"); //使用线程对象,将mypriority传入,定义线程名字为t1Thread t2 = new Thread(myPriority,"t2");Thread t3 = new Thread(myPriority,"t3");Thread t4 = new Thread(myPriority,"t4");Thread t5 = new Thread(myPriority,"t5");//先设置优先级,在启动t1.setPriority(2); //设置优先级2t1.start();t2.setPriority(10);t2.start();t3.setPriority(2);t3.start();t4.setPriority(9);t4.start();t5.setPriority(6);t5.start();}}class MyPriority implements Runnable{@Overridepublic void run() {//获取当前线程的名字+当前线程的等级!System.out.println(Thread.currentThread().getName()+"--->等级为:"+Thread.currentThread().getPriority());}
}
二、线程守护(Daemon)
- 线程分为用户线程和守护线程
- 虚拟机必须确保用户线程执行完毕
- 虚拟机不用等待守护线程执行完毕
- 如,后台记录操作日志,监控内存,垃圾回收等待。。
package com.Thread.state;
//测试守护线程
//上帝守护着你
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!离开世界!");}
}
结果图
三、线程同步机制
- 现实生活中,我们会遇到“同一个资源,多个人想使用”的问题,比如:食堂排队打饭,每个人都想吃饭,最天然的解决办法就是,排队,一个个来。
- 处理多线程问题时,多个线程访问同一个对象,并且某些线程还想修改这个对象,这时候我们就需要线程同步,线程同步其实就是一种等待机制,多个需要同时访问此对象的线程进入这个对象的等待池形成队列,等待前面线程使用完毕,下一个线程再使用!
- 由于同一个进程的多个线程共享同一块存储空间,在带来方便的同时,也带来了访问冲突问题,为了保证数据在方法中被访问时的正确性,在访问时加入锁机制synchronized,当一个线程获得对象的排队锁,独占资源,其他线程必须等待!使用后释放锁即可,存在以下问题:
- 一个线程持有锁会导致其他所有需要此锁的线程挂起;
- 在多线程竞争下,加锁,释放锁会导致比较多的上下文切换和调度延时,引起性能问题;
- 如果一个优先级高的线程等待一个优先级低的线程释放锁,会导致优先级倒置,引起性能问题!
四、三大不安全的案例
案例一:购买火车票(模拟真实抢票)
package com.Thread.syn;
//模拟不安全的买车票2
public class UnsafeBuyTicket2 {public static void main(String[] args) {BuyTicket2 buyTicket2 = new BuyTicket2();new Thread(buyTicket2,"学生").start();new Thread(buyTicket2,"老师").start();new Thread(buyTicket2,"黄牛党").start();}
}//买票的类
class BuyTicket2 implements Runnable{private int Ticketnums = 10; //票数量为10张boolean flag = true;//买票的方法public void buy(){//如果还有票,则flag为真,继续抢票while (flag){//如果没票了,则退出循环!if (Ticketnums<=0){flag = false;return;}System.out.println(Thread.currentThread().getName()+"抢到了第"+Ticketnums--+"张票!余票为:"+Ticketnums);}}@Overridepublic void run() {try {//模拟延时抢票Thread.sleep(200);} catch (InterruptedException e) {e.printStackTrace();}buy(); //调用抢票的方法!}
}
结果图
案例二:银行取钱(模拟真实场景)
package com.Thread.syn;
//模拟银行取钱 不安全的取钱
public class UnsafeBank2 {public static void main(String[] args) {//账户Account2 account2 = new Account2(1000,"结婚的礼金");//银行Drawing2 wo = new Drawing2(account2,510,"我"); //取钱的人Drawing2 duixiang = new Drawing2(account2,500,"对象"); //取钱的人//开启线程wo.start();duixiang.start();}
}//账户
class Account2{//账户中的余额int money;//卡号String cate;public Account2(int money, String cate) {this.money = money;this.cate = cate;}
}class Drawing2 extends Thread{//调用账户Account2 account2;//取了多少钱int drawingMoney;//手里有多少钱int money;//有参构造器public Drawing2(Account2 account2,int drawingMoney,String name){super(name);this.drawingMoney = drawingMoney;this.account2 = account2;}@Overridepublic void run() {//判断账户是否有钱if (account2.money-drawingMoney<0){System.out.println(Thread.currentThread().getName()+",您取钱失败,账户余额不足哦!");return;}//模拟延时try {Thread.sleep(200);} catch (InterruptedException e) {e.printStackTrace();}//卡内的余额account2.money = account2.money - drawingMoney;//手上的余额money = drawingMoney+money;//打印当前卡内的余额System.out.println("您当前卡内的余额为:"+account2.money);//打印当前您手上的余额System.out.println("您好,尊敬的"+this.getName()+",您的手上的余额为:"+money);//判断账户是否有钱}
}
结果图
案例三:ArrayList(集合存放数据)
package com.Thread.syn;import java.util.ArrayList;
import java.util.List;public class UnsafeList2 {public static void main(String[] args) {List<String> list = new ArrayList<>();for (int i = 0; i < 1000; i++) {new Thread(()->{//lambda表达式list.add(Thread.currentThread().getName());}).start();}//集合的长度System.out.println("集合的长度为:"+list.size());}
}
结果图
总结:从可以上面的代码以及运行结果能看出,线程都是不安全的,案例一(购买火车票):多人抢购到了同一张票,导致后面余票成了-1;案例二(模拟银行取钱):两个人同时取钱时,都看到了1000的余额,但是当同时去取钱的时候,超出了卡内余额,仍然可以取出来;案例三(不安全的集合):在使用ArrayList集合去添加元素时,很明显是不安全的,因为在同时添加进去的时候,并没有添加完成,便终止了添加,也就是说有多个集合存放到了同一个数组下。解决方法看下一个练习题
五、同步块
- 同步块:synchronized(obj){}
- Obj称之为同步监视器
- Obj可以是任何对象,但是推荐使用共享资源作为同步监视器
- 同步方法中无需指定同步监视器,因为同步方法的同步监视器就是this,就是这个对象本身,或者是class{反射中讲解}
- 同步监视器的执行过程
- 第一个线程访问,锁定同步监视器,执行其中代码。
- 第二个线程访问,发现同步监视器被锁定,无法访问
- 第一个线程访问完毕,解锁同步监视器!
- 第二个线程访问,发现同步监视器没有锁,然后锁定并访问!
案例一解决方法:
private synchronized void buy(){//如果没票了,则退出循环!if (Ticketnums<=0){flag = false;return;}System.out.println(Thread.currentThread().getName()+"抢到了第"+Ticketnums--+"张票!余票为:"+Ticketnums);}
结果图
总结:在买票的方法上加一个synchronized即可
案例二解决方法:
@Overridepublic void run() {//判断账户是否有钱synchronized (account2){if (account2.money-drawingMoney<0){System.out.println(Thread.currentThread().getName()+",您取钱失败,账户余额不足哦!");return;}//模拟延时try {Thread.sleep(200);} catch (InterruptedException e) {e.printStackTrace();}//卡内的余额account2.money = account2.money - drawingMoney;//手上的余额money = drawingMoney+money;//打印当前卡内的余额System.out.println("您当前卡内的余额为:"+account2.money);//打印当前您手上的余额System.out.println("您好,尊敬的"+this.getName()+",您的手上的余额为:"+money);//判断账户是否有钱}}
结果图:
总结:在重写run方法内,加上synchronized对象锁即可,注意:锁的对象需要是操作的对象,也就是账户,并不是银行!
案例三解决方法:
for (int i = 0; i < 1000; i++) {new Thread(()->{synchronized (list){//lambda表达式list.add(Thread.currentThread().getName());}}).start();}
结果图:
总结:在匿名内部类中将list对象锁上!使得循环的时候依次存放!
六、死锁
- 多个线程各自占有一些共享资源,并且互相等待其他线程占有的资源才能运行,而导致两个或者多个线程都在等待对方释放资源,都停止执行的庆幸,某一个同步块同时拥有“两个以上对象的锁”时,就可能会发生“死锁”的问题!
package com.Thread.Lock;//死锁:多个线程互相抱着对方需要的资源,然后行程僵持
public class DeadLock {public static void main(String[] args) {Makeup makeup1 = new Makeup(0,"小红");Makeup makeup2 = new Makeup(1,"小玲");makeup1.start();makeup2.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;}@Overridepublic void run() {//化妆try {makeup();} catch (InterruptedException e) {e.printStackTrace();}}//互相持有对方的锁,就是需要拿到对方的资源private void makeup() throws InterruptedException {if (choice == 0) { //如果选择的是0synchronized (lipstick) { //获得口红的锁System.out.println(this.girlName + "获得口红的锁");Thread.sleep(3000);synchronized (mirror) { //一秒钟后想获得镜子的System.out.println(this.girlName + "获得镜子的锁");}}} else {synchronized (mirror) { //获得镜子的锁System.out.println(this.girlName + "获得镜子的锁");Thread.sleep(3000);synchronized (lipstick) { //一秒钟后想获得口红的System.out.println(this.girlName + "获得镜子的锁");}}}}
}
结果图:
总结:如上所示,本应小红获得口红之后,下一步便是获得镜子,但是线程增加了同步机制,也就是synchronized,另一个线程的小玲在获取镜子,所以互不相让,导致死锁的发生,都关闭了门,无法拿到对应的锁,导致死锁!
死锁的避免方法
- 产生死锁的四个必要条件:
- 互斥条件:一个资源每次只能被一个进程使用
- 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
- 不剥夺条件:进程已获得的资源,在未使用完前,不能强行剥夺。
- 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系!
注:文章仅做个人学习日记,不做学习建议,学习来源:狂神说
学习java的第四十天,线程的优先级、守护线程、线程同步机制、死锁相关推荐
- Linux性能优化实战学习笔记:第四十六讲=====实战分析
Linux性能优化实战学习笔记:第四十六讲 一.上节回顾 不知不觉,我们已经学完了整个专栏的四大基础模块,即 CPU.内存.文件系统和磁盘 I/O.以及网络的性能分析和优化.相信你已经掌握了这些基础模 ...
- java经典算法四十题
java经典算法四十题 [程序9]题目:一个数如果恰好等于它的因子之和,这个数就称为 "完数 ".例如6=1+2+3.编程找出1000以内的所有完数. public class W ...
- 学习Java必须避开的十大致命雷区,新手入门千万不要踩!
本文梳理了学习Java需要注意的十大雷区和常用工具,祝各位大神看好学好,长生不老. 1.研究太多,实践太少 在编程方面缺乏实践是十分致命的.从第一天开始学习,就要每天练习编程.就如同学习拳 ...
- 强化学习的学习之路(四十四)2021-02-13 Monotonic Improvement with KL Divergence
作为一个新手,写这个强化学习-基础知识专栏是想和大家分享一下自己学习强化学习的学习历程,希望对大家能有所帮助.这个系列后面会不断更新,希望自己在2021年能保证平均每日一更的更新速度,主要是介绍强化学 ...
- 强化学习的学习之路(四十六)2021-02-15自然梯度法实现策略上的单调提升(Monotonic Improvement with Natural gradient descent)
作为一个新手,写这个强化学习-基础知识专栏是想和大家分享一下自己学习强化学习的学习历程,希望对大家能有所帮助.这个系列后面会不断更新,希望自己在2021年能保证平均每日一更的更新速度,主要是介绍强化学 ...
- 线程的优先级、等待线程、守护线程
线程的优先级.等待线程.守护线程 我们可以通过 public final void setPriority(int newPriority) 来设置线程的优先级,但是优先级并不是绝对的,只是相对来说比 ...
- java并发编程(十)使用wait/notify/notifyAll实现线程间通信
转载请注明出处:http://blog.csdn.net/ns_code/article/details/17225469 wait()方法:public final void wait() thr ...
- Java的知识点30——线程的优先级、终止线程的典型方式、获取线程基本信息的方法
线程的优先级 1-10 1. NORM_PRIORITY 5 默认 2. MIN_PRIORITY 1 3. MAX_PRIORITY 10 注意:优先级低只是意味着获得调度的概率低.并不 ...
- Java并发编程实战系列15之原子遍历与非阻塞同步机制(Atomic Variables and Non-blocking Synchronization)...
近年来,在并发算法领域的大多数研究都侧重于非阻塞算法,这种算法用底层的原子机器指令来代替锁来确保数据在并发访问中的一致性,非阻塞算法被广泛应用于OS和JVM中实现线程/进程调度机制和GC以及锁,并发数 ...
最新文章
- ATS 5.3.0中开启最高级别的缓存调试信息
- 第四周项目五-用递归方法求解(求1*3*5*...*n的递归式)
- php integer
- python代码覆盖率测试_利用coverage工具进行Python代码覆盖率测试
- dll放在unity哪个文件夹下_unity中调用dll文件总结
- C++基础14-类和对象之多继承与虚继承
- Burpsuite如何抓取使用了SSL或TLS传输的 IOS App流量
- 找出递增数组中所有相加为m的组合c语言,组合(1-m中选n个数)(示例代码)
- python下保持mysql连接,避免“MySQL server has gone away“方法
- Elasticseach 从零开始学习记录(四) - 整合springboot2.x
- lc300.最长递增子序列
- 性能测试用例、策略和方法
- 空间说说html,适合发空间说说的经典心情短语
- Java快逸报表展现demo_快逸报表导出成XML文件
- SAP项目上的疑难杂症-(制品区分)如何处理?
- mysql 重做日志_mysql redo log 重做日志
- 经典的搞笑反转段子,真是笑死人不偿命
- 用Beamer做slides-模板
- Windows7UltimateSP1x64安装及一些设置
- NuGet命令的用法