マルチスレッドと並行プログラミング[スレッドオブジェクトのロック、デッドロックとその解決、スレッド同時連携、プロデューサーとコンシューマーモード](4) - 包括的詳細解説(学習のまとめ --- 入門から深化まで)

 

目次

クラスをスレッド オブジェクト ロックとして使用する

カスタム オブジェクトをスレッド オブジェクト ロックとして使用する

デッドロックと解決策

スレッド同時連携(プロデューサー/コンシューマーモード)

 プロデューサーとコンシューマーのパターンを実装する


 

クラスをスレッド オブジェクト ロックとして使用する

 文法構造:

synchronized(XX.class){
      //同步代码
 }

また

synchronized public static void accessVal()
/**
* 定义销售员工类
*/
class Sale{
    private String name;
    public Sale(String name){
this.name = name;
   }
    /**
     * 领取奖金
     */
    synchronized  public static void money(){
            try {
              System.out.println(Thread.currentThread().getName() + " 被领导表扬");
                Thread.sleep(500);
              System.out.println(Thread.currentThread().getName() + " 拿钱");
                Thread.sleep(500);
              System.out.println(Thread.currentThread().getName() + " 对公司表示感谢");
                Thread.sleep(500);
              System.out.println(Thread.currentThread().getName() + " 开开心心的拿钱走人");
           } catch (InterruptedExceptione) {
                e.printStackTrace();
           }
       }
}
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();
           }
       }
   }
    /**
     * 领取奖金
     */
    public void money(){
        synchronized (Programmer.class) {
            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();
   }
}
/**
* 程序员领取奖金
*/
class ProgrammerMoney extends Thread{
    private  Programmer p;
    public ProgrammerMoney(Programmer p){
        this.p = p;
   }
    @Override
    public void run() {
        this.p.money();
   }
}
/**
* 销售部门领取奖金
*/
class SaleMoney extends  Thread{
    private  Sale p;
    public SaleMoneyThread(Sale p){
        this.p = p;
   }
    @Override
    public void run() {
        this.p.money();
   }
}
public class TestSyncThread {
    public static void main(String[] args)
{
       /* Programmer p = new Programmer("张三");
        Programmer p1 = new Programmer("李四");
        new ProgrammerMoney(p).start();
        new ProgrammerMoney(p1).start();*/
        
        Sale s = new Sale("张晓丽");
        Sale s1 = new Sale("王晓红");
        new SaleMoney(s).start();
        new SaleMoney(s1).start();
   }
}

カスタム オブジェクトをスレッド オブジェクト ロックとして使用する

文法構造:

synchronized(自定义对象){
      //同步代码
}
/**
* 定义销售员工类
*/
class Sale{
    private String name;
    public Sale(String name){
        this.name = name;
   }
    /**
     * 领取奖金
     */
    synchronized  public static void money(){
            try {
                System.out.println(Thread.currentThread(). getName() + " 被领导表扬");
                Thread.sleep(500);
              System.out.println(Thread.currentThread().getName() + " 拿钱");
                Thread.sleep(500);
              System.out.println(Thread.currentThread().getName() + " 对公司表示感谢");
                Thread.sleep(500);
              System.out.println(Thread.currentThread().getName() + " 开开心心的拿钱走人");
           } catch (InterruptedExceptione) {
                e.printStackTrace();
           }
       }
}
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();
           }
       }
    /**
     * 去卫生间
     */
    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 (InterruptedException e) {
                e.printStackTrace();
           }
       }
 }
    /**
     * 领取奖金
     */
    public void money(){
        synchronized (Programmer.class) {
            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();
           }
       }
   }
}
class Manager{
    private String name;
    public Manager(String name){
        this.name = name;
}
    public String getName(){
        return this.name;
   }
    /**
     * 敬酒
     */
    public void cheers(String mName,String eName){
            try {
                System.out.println(mName + " 来到 " + eName + " 面前");
                Thread.sleep(500);
                System.out.println(eName + " 拿起酒杯");
                Thread.sleep(500);
                System.out.println(mName + " 和 " + eName + " 干杯");
           } 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();
   }
}
/**
* 去卫生间的线程
*/
class WC extends Thread{
    private  Programmer p;
    public WC(Programmer p){
        this.p = p;
   }
    @Override
    public void run() {
        this.p.wc();
   }
}
/**
* 程序员领取奖金
*/
class ProgrammerMoney extends Thread{
    private  Programmer p;
    public ProgrammerMoney(Programmer p){
        this.p = p;
   }
    @Override
    public void run() {
        this.p.money();
   }
}
/**
* 销售部门领取奖金
*/
class SaleMoneyThread extends  Thread{
    private  Sale p;
    public SaleMoneyThread(Sale p){
        this.p = p;
   }
    @Override
    public void run() {
        this.p.money();
   }
}
/**
* 敬酒线程类
*/
class CheersThread extends Thread{
    private Manager manager;
    private String name;
    public CheersThread(String name,Manager manager){
        this.name = name;
        this.manager = manager;
   }
    @Override
    public void run() {
        synchronized (this.manager) {
          
this.manager.cheers(this.manager.getName() , name);
       }
   }
}
public class TestSyncThread {
    public static void main(String[] args)
{
        Manager manager = new Manager("张三丰");
        new CheersThread("张三",manager).start();
        new CheersThread("李四",manager).start();
   }
}

デッドロックと解決策

デッドロックの概念

 「デッドロック」とは、複数のスレッドがそれぞれ共有リソースを占有し、他のスレッドが占有する互いのリソースが続行するのを待ち、その結果、2 つ以上のスレッドが互いのリソースを解放して実行を停止するのを待機することを指します。

同期ブロックが「2 つ以上のオブジェクトのロック」を同時に所有する必要がある場合、「デッドロック」の問題が発生する可能性があります。たとえば、「メイクアップ スレッド」が同期ブロックを実行するには、「ミラー オブジェクト」と「口紅オブジェクト」の両方が必要です。そして、実際の運用では、「小さな女の子のメイクアップスレッド」には「鏡オブジェクト」が、「大きな女の子のメイクアップスレッド」には「口紅オブジェクト」があり、メイクアップする前にお互いのリソースが解放されるのを待ちます。このようにして、2 つのスレッドは互いに待機し、実行を続行できない「デッドロック状態」を形成します。

 デッドロックケースのデモンストレーション

/**
* 口红类
*/
   class Lipstick{
     }
/**
* 镜子类
*/
   class Mirror{
   }
/**
* 化妆线程类
*/
class Makeup extends Thread{
    private int flag; //flag=0:拿着口红。 flag!=0:拿着镜子
    private String girlName;
    static Lipstick lipstick = new Lipstick();
    static Mirror mirror = new Mirror();
    public Makeup(int flag,String girlName){
        this.flag  = flag;
        this.girlName = girlName;
   }
    @Override
    public void run() {
        this.doMakeup();
   }
    /**
     * 开始化妆
     */
    public void doMakeup(){
        if(flag == 0){
            synchronized (lipstick){
              System.out.println(this.girlName+" 拿着口红");
                try {
                    Thread.sleep(1000);
               } catch (InterruptedException e) {
                    e.printStackTrace();
               }
                synchronized (mirror){
               System.out.println(this.girlName+" 拿着镜子");
               }
           }
       }else{
            synchronized (mirror){
              System.out.println(this.girlName+" 拿着镜子");
                try {
                    Thread.sleep(2000);
               } catch(InterruptedException e) {
                    e.printStackTrace();
               }
                synchronized (lipstick){
                  System.out.println(this.girlName+" 拿着口红");
               }
           }
       }
   }
}
public class DeadLockThread {
    public static void main(String[] args) {
        new Makeup(0,"大丫").start();
        new Makeup(1,"小丫").start();
   }
}

デッドロック問題の解決策

デッドロックは、「同期ブロックが同時に複数のオブジェクト ロックを保持する必要がある」ことが原因で発生します。この問題を解決するための考え方は非常に単純です。同じコード ブロックが同時に 2 つのオブジェクト ロックを保持すべきではないということです。

/**
* 口红类
*/
    class Lipstick{
     }
/**
* 镜子类
*/
    class Mirror{
    }
/**
* 化妆线程类
*/
class Makeup extends Thread{
    private int flag; //flag=0:拿着口红。 flag!=0:拿着镜子
    private String girlName;
    static Lipstick lipstick = new Lipstick();
    static Mirror mirror = new Mirror();
    public void setFlag(int flag) {
        this.flag = flag;
      }
    public void setGirlName(String girlName)
      {
        this.girlName = girlName;
      }
    @Override
    public void run() {
        this.doMakeup();
     }
    /**
     * 开始化妆
     */
    public void doMakeup(){
        if(flag == 0){
            synchronized (lipstick){
              System.out.println(this.girlName+" 拿着口红");
                try {
                    Thread.sleep(1000);
               } catch(InterruptedException e) {
                    e.printStackTrace();
               }
           }
            synchronized (mirror){
             System.out.println(this.girlName+" 拿着镜子");
           }
       }else{
            synchronized (mirror){
              System.out.println(this.girlName+" 拿着镜子");
                try {
                    Thread.sleep(2000);
               } catch(InterruptedException e) {
                    e.printStackTrace();
               }
           }
            synchronized (lipstick){
              System.out.println(this.girlName+" 拿着口红");
           }
       }
   }
}
public class DeadLockThread {
    public static void main(String[] args) {
        Makeup makeup = new Makeup();
        makeup.setFlag(0);
        makeup.setGirlName("大丫");
        Makeup makeup1 = new Makeup();
        makeup1.setFlag(1);
        makeup1.setGirlName("小丫");
        makeup.start();
        makeup1.start();
   }
}

デッドロック問題の解決策

デッドロックは、「同期ブロックが同時に複数のオブジェクト ロックを保持する必要がある」ことが原因で発生します。この問題を解決するための考え方は非常に単純です。同じコード ブロックが同時に 2 つのオブジェクト ロックを保持すべきではないということです。

/**
* 口红类
*/
     class Lipstick{
     }
/**
* 镜子类
*/
    class Mirror{
    }
/**
* 化妆线程类
*/
class Makeup extends Thread{
    private int flag; //flag = 0 :拿着口红,flag != 0 :拿着镜子
  private String girlName;
    static Lipstick lipstick = new Lipstick();
    static Mirror mirror = new Mirror();
    public Makeup(int flag,String girlName){
        this.flag = flag;
        this.girlName = girlName;
   }
    @Override
    public void run() {
        this.doMakeup();
   }
    /**
     * 开始化妆
     */
    public void doMakeup(){
        if(this.flag == 0){
            synchronized (lipstick){
               System.out.println(this.girlName+" 拿着口红");
                try {
                    Thread.sleep(1000);
               } catch(InterruptedException e) {
                    e.printStackTrace();
               }
           }
 synchronized (mirror){
              System.out.println(this.girlName+" 拿着镜子");
           }
       }else{
            synchronized (mirror){
              System.out.println(this.girlName+" 拿着镜子");
                try {
                    Thread.sleep(2000);
               } catch(InterruptedException e) {
                    e.printStackTrace();
               }
           }
            synchronized (lipstick){
              System.out.println(this.girlName+" 拿着口红");
           }
       }
   }
}
public class DeadLockThread {
    public static void main(String[] args) {
  new Makeup(0,"小丫").start();
        new Makeup(1,"大丫").start();
   }
}

スレッド同時連携(プロデューサー/コンシューマーモード)

 マルチスレッド環境では、多くの場合、複数のスレッドの同時実行性と連携が必要になります。このとき、重要なマルチスレッド同時連携モデル「プロデューサー/コンシューマーモード」を理解する必要があります。

 キャラクター紹介

   プロデューサーとは何ですか?

      プロデューサは、データの生成を担当するモジュールを指します (ここでのモジュールは、メソッド、オブジェクト、スレッド、プロセスなどです)。

  Consumerとは?

    コンシューマは、データの処理を担当するモジュールを指します (ここでのモジュールには、メソッド、オブジェクト、スレッド、プロセスが含まれます)。

  バッファとは何ですか?

    コンシューマはプロデューサのデータを直接使用することはできず、コンシューマの間には「バッファ」が存在します。プロデューサーは生成したデータを「バッファ」に置き、コンシューマは処理対象のデータを「バッファ」から取り出します。

 バッファーは同時実行性を実現するための中核であり、バッファーの設定には次の 2 つの利点があります。

1 スレッドの同時連携を実現

バッファーを取得した後、プロデューサー スレッドはデータをバッファーに配置するだけで済み、コンシューマーの消費について気にする必要はありません。同様に、コンシューマーはバッファーからのデータを処理するだけでよく、プロデューサープロデュースのケース。このようにして、「プロデューサースレッド」と「コンシューマースレッド」の分離が論理的に実現され、プロデューサーとコンシューマーの結合が解放されます。

2 忙しさの偏りを解消し、効率を向上

プロデューサのデータ生成が遅い場合でも、バッファにはまだデータが存在するため、コンシューマの消費には影響しません。コンシューマのデータ処理が遅い場合でも、プロデューサはバッファにデータを配置し続けることができます。

 プロデューサーとコンシューマーのパターンを実装する

バッファを作成する

/**
* 定义馒头类
*/
class ManTou{
    private int id;
    public ManTou(int id){
        this.id = id;
   }
  public int getId(){
        return this.id;
   }
}
/**
* 定义缓冲区类
*/
class SyncStack{
    //定义存放馒头的盒子
    private ManTou[] mt = new ManTou[10];
    //定义操作盒子的索引
    private int index;
    /**
     * 放馒头
     */
    public synchronized void push(ManTou manTou){
        //判断盒子是否已满
        while(this.index == this.mt.length){
            try {
                /**
                 * 语法:wait(),该方法必须要在 synchronized块中调用。
                 * wait执行后,线程会将持有的对象锁释放,并进入阻塞状态,
                 * 其他需要该对象锁的线程就可以继续运行了。
                 */
                this.wait();
               } catch (InterruptedException e){
                e.printStackTrace();
           }
       }
        //唤醒取馒头的线程
        /**
         * 语法:该方法必须要在synchronized块中调用。
         * 该方法会唤醒处于等待状态队列中的一个线程。
         */
        this.notify();
        this.mt[this.index] = manTou;
        this.index++;
   }
    /**
     * 取馒头
     */
    public synchronized ManTou pop(){
        while(this.index == 0){
            try {
                /**
                 * 语法:wait(),该方法必须要在synchronized块中调用。
                 * wait执行后,线程会将持有的对象锁释放,并进入阻塞状态,
                 * 其他需要该对象锁的线程就可以继续运行了。
                 */
                this.wait();
           } catch (InterruptedException e)
           {
                e.printStackTrace();
           }
       }
        this.notify();
        this.index--;
        return this.mt[this.index];
   }
}
public class TestProduceThread {
    public static void main(String[] args) {
      
   }
}

プロデューサー・コンシューマー・スレッドを作成する

/**
* 定义馒头类
*/
class ManTou{
    private int id;
    public ManTou(int id){
        this.id = id;
   }
    public int getId(){
        return this.id;
   }
}
/**
* 定义缓冲区类
*/
class SyncStack{
    //定义存放馒头的盒子
    private ManTou[] mt = new ManTou[10];
    //定义操作盒子的索引
    private int index;
    /**
     * 放馒头
     */
    public synchronized void push(ManTou manTou){
        //判断盒子是否已满
        while(this.index == this.mt.length)
          {
            try {
                /**
                 * 语法:wait(),该方法必须要在 synchronized块中调用。
                 * wait执行后,线程会将持有的对象锁释放,并进入阻塞状态,
                 * 其他需要该对象锁的线程就可以继续运行了。
                 */
                this.wait();
           } catch (InterruptedException e) {
                e.printStackTrace();
          }
       }
        //唤醒取馒头的线程
        /**
         * 语法:该方法必须要在synchronized块中调用。
         * 该方法会唤醒处于等待状态队列中的一个线程。
         */
        this.notify();
        this.mt[this.index] = manTou;
        this.index++;
   }
    /**
     * 取馒头
     */
    public synchronized ManTou pop(){
        while(this.index == 0){
            try {
                /**
                 * 语法:wait(),该方法必须要在synchronized块中调用。
                 * wait执行后,线程会将持有的对象锁释放,并进入阻塞状态,
                 * 其他需要该对象锁的线程就可以继续运行了。
                 */
                this.wait();
           } catch (InterruptedException e) {
                e.printStackTrace();
           }
       }
        this.notify();
        this.index--;
        return this.mt[this.index];
   }
}
/**
* 定义生产者线程类
*/
class ShengChan extends Thread{
    private SyncStack ss;
    public ShengChan(SyncStack ss){
        this.ss = ss;
   }
    @Override
    public void run() {
       for(int i=0;i<10;i++){
           System.out.println("生产馒头:"+i);
           ManTou manTou = new ManTou(i);
           this.ss.push(manTou);
       }
   }
}
/**
* 定义消费者线程类
*/
class XiaoFei extends Thread{
    private SyncStack ss;
    public XiaoFei(SyncStack ss){
        this.ss = ss;
   }
    @Override
    public void run() {
        for(int i=0;i<10;i++){
           ManTou manTou = this.ss.pop();
            System.out.println("消费馒头:"+i);
       }
   }
}
public class ProduceThread {
    public static void main(String[] args)
{
        SyncStack ss = new SyncStack();
        new ShengChan(ss).start();
        new XiaoFei(ss).start();
   }
}

スレッドの同時実行と連携の概要

スレッド同時協力 (スレッド通信とも呼ばれます)

   生産者消費者のパターン:

1 生産者と消費者は同じ資源を共有しており、生産者と消費者は互いに依存し、相互に条件付けされています。

2 生産者にとって、製品を生産する前に、消費者は待機状態に入らなければなりません。製品が製造された後は、消費者に消費について直ちに通知する必要があります。

3 消費者にとっては、消費後、消費が終了したことを生産者に通知し、消費のために新しい製品を生産する必要があります。4 プロデューサーコンシューマー問題では、同期だけでは十分ではありません。同期を使用すると、同じ共有リソースへの同時更新を防ぐことができ、同期を実現できますが、同期を使用して異なるスレッド間でのメッセージ パッシング (通信) を実現することはできません。

5 スレッドはメッセージ パッシング (通信) にどのような方法を使用しますか? 以下の概要を参照してください。

 6 上記のメソッドは java.lang.Object クラスのメソッドです。

どちらも同期メソッドまたは同期コード ブロックでのみ使用でき、それ以外の場合は例外がスローされます。

OldLu は、このモードが実際の開発、特に「アーキテクチャ設計」で広く使用されることを示唆しました。初心者はこれだけで十分ですが、中級者や上級開発者になると必ずマスターする必要があります。

おすすめ

転載: blog.csdn.net/m0_58719994/article/details/131648388
おすすめ