マルチスレッド・並行プログラミング【デーモンスレッド・スレッド同期】(3) - 徹底解説(学習のまとめ --- 入門から深化まで)

 

 

目次

デーモンスレッド

 デーモンスレッドとは何ですか

 デーモンスレッドの使用

スレッドの同期

スレッド同期を実装する

スレッド同期の使用


デーモンスレッド

 デーモンスレッドとは何ですか

  Java には 2 種類のスレッドがあります。

        ユーザースレッド (ユーザースレッド): アプリケーション内のカスタムスレッドです。

        デーモン スレッド: たとえば、ガベージ コレクション スレッドは最も典型的なデーモン スレッドです。

 デーモンスレッド(デーモンスレッド) はサービススレッドであり、正確には他のスレッドにサービスを提供するのがその役割であり、他のスレッドはユーザースレッドの 1 つだけです。

 デーモン スレッドの機能:

      ユーザー スレッドが終了すると、デーモン スレッドも終了します。

デーモン スレッドとユーザー スレッドの違い:

ユーザースレッドは、メインスレッドが終了しても終了しません。ユーザースレッドが停止する状況は 2 つだけあり、1 つは実行中の異常終了です。2 実行は正常に実行され、スレッドは終了します。

デーモン スレッドはユーザー スレッドの終了とともに終了し、ユーザー スレッドが終了するとデーモン スレッドも終了します。

 デーモンスレッドの使用

/**
* 守护线程类
*/
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 台しかなく、複数の人がそれを使用したいと考えています。自然な解決策は、コンピューターの隣に並ぶことです。前の人が使い終わったら、後の人がまた使います。

 スレッド同期の概念

マルチスレッドの問題に対処する場合、複数のスレッドが同じオブジェクトにアクセスし、一部のスレッドはこのオブジェクトの変更も必要とします。このとき「スレッド同期」を利用する必要があります。スレッド同期は実際には待機メカニズムです。このオブジェクトに同時にアクセスする必要がある複数のスレッドは、このオブジェクトの待機プールに入ってキューを形成し、前のスレッドが使用されるまで待機してから、次のスレッドがそのスレッドを使用できるようになります。

スレッド競合ケースのデモ 

銀行引き出しの古典的なケースを使用して、スレッドの競合現象を示します。銀行出金の基本的なプロセスは、基本的に次のステップに分けることができます。

(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 キーワードを使用する場合に考慮すべき問題:

    実行時にコードのその部分に対してスレッド相互排他 (スレッド相互排他: 並列から直列へ) の機能が必要です。

   スレッドに相互排他機能 (同期ロック オブジェクトによって決定) が必要なコード。

これには 2 つの用途が含まれます。

同期メソッドと同期ブロック。

 1 synchronized 方法 

    メソッド宣言に synchronized キーワードを追加して宣言します。構文は次のとおりです。

    

public  synchronized  void accessVal(int newVal);

Synchronized は、メソッド宣言で、アクセス制御文字 (public) の前または後に使用されます。このとき、同じオブジェクトの下で同期されたメソッドが複数のスレッドで実行されると、メソッドは同期されます。つまり、一度に 1 つのスレッドだけがメソッドに入ることができます。このときに他のスレッドがそのメソッドを呼び出したい場合、それらのスレッドはメソッドを呼び出します。現在のスレッド (つまり、同期されたメソッド内のスレッド) メソッドが実行された後、他のスレッドが入ることができます。

2つの同期ブロック

同期メソッドの欠点: 大規模なメソッドが同期済みとして宣言されると、効率に大きな影響を与えます。Java は、同期ブロックというより良いソリューションを提供します。ブロックを使用すると、特定の「メンバー変数」を正確に制御し、同期の範囲を狭め、効率を向上させることができます。

スレッド競合ケースのデモを変更する

/**
* 账户类
*/
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();
   }
}

スレッド同期の使用

これをスレッドオブジェクトロックとして使用します

 文法構造:

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