目录
守护线程
什么是守护线程
守护线程的使用
线程同步
实现线程同步
线程同步的使用
守护线程
什么是守护线程
在Java中有两类线程:
User Thread(用户线程):就是应用程序里的自定义线程。
Daemon Thread(守护线程):比如垃圾回收线程,就是最典型的守护线程。
守护线程(即Daemon Thread),是一个服务线程,准确地来说 就是服务其他的线程,这是它的作用,而其他的线程只有一种,那 就是用户线程。
守护线程特点:
守护线程会随着用户线程死亡而死亡。
守护线程与用户线程的区别:
用户线程,不随着主线程的死亡而死亡。用户线程只有两种情况会 死掉,1在run中异常终止。2正常把run执行完毕,线程死亡。
守护线程,随着用户线程的死亡而死亡,当用户线程死亡守护线程 也会随之死亡。
守护线程的使用
/**
* 守护线程类
*/
class Daemon implements Runnable{
@Override
public 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{
@Override
public 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;
}
/**
* 取款线程
*/
@Override
public 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;
}
/**
* 取款线程体
*/
@Override
public 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;
}
@Override
public void run() {
this.p.computer();
}
}
/**
* 编写代码的工作线程
*/
class Working2 extends Thread{
private Programmer p;
public Working2(Programmer p){
this.p = p;
}
@Override
public 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;
}
@Override
public void run() {
this.p.computer();
}
}
/**
* 编写代码的工作线程
*/
class Working2 extends Thread{
private Programmer p;
public Working2(Programmer p){
this.p = p;
}
@Override
public void run() {
this.p.coding();
}
}
/**
* 去卫生间的线程
*/
class WC extends Thread{
private Programmer p;
public WC(Programmer p){
this.p = p;
}
@Override
public 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();
}
}