多线程与并发编程【守护线程、线程同步】(三)-全面详解(学习总结---从入门到深化)

news/2025/3/15 5:08:40/

 

 

目录

守护线程

 什么是守护线程

 守护线程的使用

线程同步

实现线程同步

线程同步的使用


守护线程

 什么是守护线程

  在Java中有两类线程:

        User Thread(用户线程):就是应用程序里的自定义线程。

        Daemon Thread(守护线程):比如垃圾回收线程,就是最典型的守护线程。

 守护线程(即Daemon Thread),是一个服务线程,准确地来说 就是服务其他的线程,这是它的作用,而其他的线程只有一种,那 就是用户线程。

 守护线程特点

      守护线程会随着用户线程死亡而死亡。

守护线程与用户线程的区别

用户线程,不随着主线程的死亡而死亡。用户线程只有两种情况会 死掉,1在run中异常终止。2正常把run执行完毕,线程死亡。

守护线程,随着用户线程的死亡而死亡,当用户线程死亡守护线程 也会随之死亡。

 守护线程的使用

/**
* 守护线程类
*/
class Daemon implements  Runnable{@Overridepublic void run() {for(int i=0;i<20;i++){System.out.println(Thread.currentThread().getName()+" "+i);try {Thread.sleep(2000);} catch (InterruptedException e){e.printStackTrace();}}}
}
class UsersThread implements Runnable{@Overridepublic void run() {Thread t = new Thread(new Daemon(),"Daemon");//将该线程设置为守护线程t.setDaemon(true);t.start();for(int i=0;i<5;i++){System.out.println(Thread.currentThread().getName()+" "+i);try {Thread.sleep(500);} catch (InterruptedException e){e.printStackTrace();}}}
}
public class DaemonThread {public static void main(String[] args)throws Exception {Thread t = new Thread(new UsersThread(),"UsersThread");t.start();Thread.sleep(1000);System.out.println("主线程结束");}
}

线程同步

什么是线程同步

线程冲突现象

 同步问题的提出

现实生活中,我们会遇到“同一个资源,多个人都想使用”的问题。 比如:教室里,只有一台电脑,多个人都想使用。天然的解决办法 就是,在电脑旁边,大家排队。前一人使用完后,后一人再使用。

 线程同步的概念

处理多线程问题时,多个线程访问同一个对象,并且某些线程还想 修改这个对象。 这时候,我们就需要用到“线程同步”。 线程同步其 实就是一种等待机制,多个需要同时访问此对象的线程进入这个对 象的等待池形成队列,等待前面的线程使用完毕后,下一个线程再 使用。

线程冲突案例演示 

我们以银行取款经典案例来演示线程冲突现象。 银行取钱的基本流程基本上可以分为如下几个步骤。

(1)用户输入账户、密码,系统判断用户的账户、密码是否匹配。

(2)用户输入取款金额

(3)系统判断账户余额是否大于或等于取款金额

(4)如果余额大于或等于取款金额,则取钱成功;如果余额小于取 款金额,则取钱失败。

/**
* 账户类
*/
class Account{//账号private String accountNo;//账户的余额private double balance;public Account() {}public Account(String accountNo, double balance) {this.accountNo = accountNo;this.balance = balance;}public String getAccountNo() { return accountNo;}public void setAccountNo(String accountNo) {this.accountNo = accountNo;}public double getBalance() {return balance;}public void setBalance(double balance) {this.balance = balance;}
}
/**
* 取款线程
*/
class DrawThread implements Runnable{//账户对象private Account account;//取款金额private double drawMoney;public DrawThread(Account account,double drawMoney){this.account = account;this.drawMoney = drawMoney;}/*** 取款线程*/@Overridepublic void run() {//判断当前账户余额是否大于或等于取款金额if(this.account.getBalance() >= this.drawMoney){System.out.println(Thread.currentThread().getName()+" 取钱成功!吐出钞
票:"+this.drawMoney);try {Thread.sleep(1000);} catch (InterruptedException e){e.printStackTrace();}//更新账户余额this.account.setBalance(this.account.getBalance()- this.drawMoney);System.out.println("\t 余额为:"+this.account.getBalance());}else{System.out.println(Thread.currentThread().getName()+" 取钱失败,余额不足");}}
}
public class TestDrawMoneyThread {public static void main(String[] args) {Account account = new Account("1234",1000);new Thread(new DrawThread(account,800),"老公").start();new Thread(new DrawThread(account,800),"老婆").start();}
}

实现线程同步

由于同一进程的多个线程共享同一块存储空间,在带来方便的同 时,也带来了访问冲突的问题。Java语言提供了专门机制以解决这 种冲突,有效避免了同一个数据对象被多个线程同时访问造成的这 种问题。这套机制就是synchronized关键字。

 synchronized语法结构:

synchronized(锁对象){ 同步代码}

synchronized关键字使用时需要考虑的问题:

    需要对那部分的代码在执行时具有线程互斥的能力(线程互斥:并行变串行)。

   需要对哪些线程中的代码具有互斥能力(通过synchronized锁对象来决定)。

它包括两种用法:

synchronized 方法和 synchronized 块。

 1 synchronized 方法 

    通过在方法声明中加入 synchronized关键字来声明,语法如 下:

    

public  synchronized  void accessVal(int newVal);

synchronized 在方法声明时使用:放在访问控制符(public)之前 或之后。这时同一个对象下synchronized方法在多线程中执行 时,该方法是同步的,即一次只能有一个线程进入该方法,其他 线程要想在此时调用该方法,只能排队等候,当前线程(就是在 synchronized方法内部的线程)执行完该方法后,别的线程才能 进入。

2 synchronized块

synchronized 方法的缺陷:若将一个大的方法声明为 synchronized 将会大大影响效率。 Java 为我们提供了更好的解决办法,那就是 synchronized 块。 块可以让我们精确地控制到具体的“成员变量”,缩小同步的范 围,提高效率。

修改线程冲突案例演示

/**
* 账户类
*/
class Account{//账号private String accountNO;//账户余额private double balance;public Account() {}public Account(String accountNO, double balance) {this.accountNO = accountNO;this.balance = balance;}public String getAccountNO() {return accountNO;}public void setAccountNO(String accountNO) {this.accountNO = accountNO;
}public double getBalance() {return balance;}public void setBalance(double balance) {this.balance = balance;}
}
/**
* 取款线程
*/
class DrawThread implements Runnable{//账户对象private Account account;//取款金额private double drawMoney;public DrawThread(){}public DrawThread(Account account,double drawMoney){this.account = account;this.drawMoney = drawMoney;}/*** 取款线程体*/@Overridepublic void run() {synchronized (this.account){//判断当前账户余额是否大于或等于取款金额if(this.account.getBalance() >= this.drawMoney){System.out.println(Thread.currentThread().getName()+" 取钱成功!突出钞票"+this.drawMoney);try {Thread.sleep(1000);} catch(InterruptedException e) {e.printStackTrace();}//更新账户余额this.account.setBalance(this.account.getBalance() - this.drawMoney);System.out.println("\t 余额为:"+this.account.getBalance());}else{System.out.println(Thread.currentThread().getName()+" 取钱失败,余额不足");}}}
}
public class TestDrawMoneyThread {public static void main(String[] args) {Account account = new Account("1234",1000);new Thread(new DrawThread(account,800),"老公").start();new Thread(new DrawThread(account,800),"老婆").start();}
}

线程同步的使用

使用this作为线程对象锁

 语法结构:

synchronized(this){//同步代码}

public  synchronized  void accessVal(int newVal){//同步代码
}
/**
* 定义程序员类
*/
class Programmer{private String name;public Programmer(String name){this.name = name;}/*** 打开电脑*/synchronized  public  void computer(){try {System.out.println(this.name + " 接通电源");Thread.sleep(500);System.out.println(this.name + " 按开机按键");Thread.sleep(500);System.out.println(this.name + " 系统启动中");Thread.sleep(500);System.out.println(this.name + " 系统启动成功");} catch (InterruptedException e){e.printStackTrace();}}/*** 编码*/synchronized public void coding(){try {System.out.println(this.name + " 双击Idea");Thread.sleep(500);System.out.println(this.name + " Idea启动完毕");Thread.sleep(500);System.out.println(this.name + " 开开心心的写代码");} catch (InterruptedException e){e.printStackTrace();}}
}
/**
* 打开电脑的工作线程
*/
class Working1 extends Thread{private  Programmer p;public Working1(Programmer p){this.p = p;}@Overridepublic void run() {this.p.computer();}
}
/**
* 编写代码的工作线程
*/
class Working2 extends Thread{private  Programmer p;public Working2(Programmer p){this.p = p;}@Overridepublic void run() {this.p.coding();}
}
public class TestSyncThread {public static void main(String[] args) {Programmer p = new Programmer("张三");new Working1(p).start();new Working2(p).start();}
}

使用字符串作为线程对象锁

 语法结构:

synchronized(“字符串”){//同步代码}
/**
* 定义程序员类
*/
class Programmer{private String name;public Programmer(String name){this.name = name;}/*** 打开电脑*/synchronized  public  void computer(){try {System.out.println(this.name + " 接通电源");Thread.sleep(500);System.out.println(this.name + " 按开机按键");Thread.sleep(500);System.out.println(this.name + " 系统启动中");Thread.sleep(500);System.out.println(this.name + " 系统启动成功");} catch (InterruptedExceptione) {e.printStackTrace();}}/*** 编码*/synchronized public void coding(){try {System.out.println(this.name + " 双击Idea");Thread.sleep(500);System.out.println(this.name + " Idea启动完毕");Thread.sleep(500);System.out.println(this.name + " 开开心心的写代码");} catch (InterruptedExceptione) {e.printStackTrace();}}/*** 去卫生间*/public void wc(){synchronized ("suibian") {try {System.out.println(this.name + " 打开卫生间门");Thread.sleep(500);System.out.println(this.name + " 开始排泄");Thread.sleep(500);System.out.println(this.name + " 冲水");Thread.sleep(500);System.out.println(this.name + " 离开卫生间");} catch (InterruptedExceptione) {e.printStackTrace();}}}
}
/**
* 打开电脑的工作线程
*/
class Working1 extends Thread{private  Programmer p;public Working1(Programmer p){this.p = p;}@Overridepublic void run() {this.p.computer();}
}
/**
* 编写代码的工作线程
*/
class Working2 extends Thread{private  Programmer p;public Working2(Programmer p){this.p = p;}@Overridepublic void run() {this.p.coding();}
}
/**
* 去卫生间的线程
*/
class WC extends Thread{private  Programmer p;public WC(Programmer p){this.p = p;}@Overridepublic void run() {this.p.wc();}
}
public class TestSyncThread {public static void main(String[] args)
{Programmer p = new Programmer("张三");Programmer p1 = new Programmer("李四");Programmer p2 = new Programmer("王五");new WC(p).start();new WC(p1).start();new WC(p2).start();}
}

http://www.ppmy.cn/news/813767.html

相关文章

telegram 常见问题

目录 一般的问题 问&#xff1a;什么是电报&#xff1f;我在这里做什么 问&#xff1a;谁是电报&#xff1f; 问&#xff1a;电报与WhatsApp不同&#xff1f; 问&#xff1a;电报多大&#xff1f; 问&#xff1a;我可以使用哪些设备&#xff1f; 问&#xff1a;电报背后…

Android开源控件收集整理

一 、基本控件 TextView HTextView 一款支持TextView文字动画效果的Android组件库。GitHub - hanks-zyh/HTextView: Animation effects to text, not really textview ScrollNumber 滚动数字控件 https://github.com/a-voyager/ScrollNumber ticker 滚动数字控件 GitHu…

8万字带你入门Rust

Rust ?? 学习建议&#xff1a; 先从 整体出发&#xff0c;不要让自己陷入到细节中去和自己已知的知识建立联系rust 和go一样采用 组合的手段实现代码复用&#xff0c;不要深思为什么不是继承学会阅读源码&#xff0c;从源码中学习Rust设计哲学 使用 cargo new 项目名 在终端…

掌握未来趋势的测试工程师成长之路

入门 计算机基础 计算基础知识 掌握技能 了解计算机软件和硬件系统。 熟练应用键盘快速打字。 熟练使用百度搜索引擎的技巧。 熟练掌握Excel操作。 课程亮点 …

搜集的超全的E71快捷键、设置、键盘命令总结方法

搜集的超全的E71快捷键、设置、键盘命令总结方法 此帖对" 诺基亚"的评论 个性化设置-主屏幕-模式设置-主屏应用程序里面可以设置所有桌面显示方案 一、输入法切换&#xff1a; 切换中文/英文输入法&#xff1a;↗键&#xff0b;CTRL键&#xff0b;空格键 切换拼音/…

〖Python APP 自动化测试实战篇⑤〗- appium Capability详解

订阅 Python全栈白宝书-零基础入门篇 可报销!白嫖入口-请点击我。推荐他人订阅,可获取扣除平台费用后的35%收益,文末名片加V!说明:该文属于 Python全栈白宝书专栏,免费阶段订阅数量4300+,购买任意白宝书体系化专栏可加入TFS-CLUB 私域社区。福利:加入社区的小伙伴们,除…

听说你们害怕异常?保姆式的图文手把手教你如何调试出程序的 bug

订阅 Python全栈白宝书-零基础入门篇 可报销!白嫖入口-请点击我。推荐他人订阅,可获取扣除平台费用后的35%收益,文末名片加V!说明:该文属于 Python全栈白宝书专栏,免费阶段订阅数量4300+,购买任意白宝书体系化专栏可加入TFS-CLUB 私域社区。福利:加入社区的小伙伴们,除…

android view设置按钮颜色_建议收藏!最全 Android 常用开源库总结!

作者 | i小灰 地址 | https://www.jianshu.com/p/3fde87405411 前言 收集了一些比较常见的开源库&#xff0c;特此记录(已收录350)。另外&#xff0c;本文将持续更新&#xff0c;大家有关于Android 优秀的开源库&#xff0c;也可以在下面留言。 基本控件 TextView HTextView …