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

 

 

目录

守护线程

 什么是守护线程

 守护线程的使用

线程同步

实现线程同步

线程同步的使用


守护线程

 什么是守护线程

  在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();
   }
}

猜你喜欢

转载自blog.csdn.net/m0_58719994/article/details/131648281
今日推荐