• Java基础学习09--线程同步与死锁


    1.线程同步

    1.1 存在线程安全问题的代码

     1 package com.example.concurrency;
     2 
     3 import java.util.Arrays;
     4 
     5 public class demo09 {
     6 
     7     private static int index = 0;
     8 
     9     public static void main(String[] args) throws Exception {
    10         String[] s = new String[5];
    11 
    12         Runnable ra = new Runnable() {
    13             @Override
    14             public void run() {
    15                 s[index] = "hello";
    16                 index++;
    17             }
    18         };
    19 
    20         Runnable rb = new Runnable() {
    21 
    22             @Override
    23             public void run() {
    24                 s[index] = "world";
    25                 index++;
    26 
    27             }
    28         };
    29 
    30         Thread a = new Thread(ra, "a");
    31         Thread b = new Thread(rb, "b");
    32         a.start();
    33         b.start();
    34 
    35         a.join();
    36         b.join();
    37         
    38         System.out.println(Arrays.toString(s));
    39     }
    40 }

    反复执行,有可能得到如下的结果:

    1.2 使用同步代码块

     1 package com.example.concurrency;
     2 
     3 import java.util.Arrays;
     4 
     5 public class demo09 {
     6 
     7     private static int index = 0;
     8 
     9     public static void main(String[] args) throws Exception {
    10         String[] s = new String[5];
    11 
    12         Runnable ra = new Runnable() {
    13             @Override
    14             public void run() {
    15                 synchronized (s) {
    16                     s[index] = "hello";
    17                     index++;
    18                 }
    19             }
    20         };
    21 
    22         Runnable rb = new Runnable() {
    23 
    24             @Override
    25             public void run() {
    26                 synchronized (s) {
    27                     s[index] = "world";
    28                     index++;
    29                 }
    30             }
    31         };
    32 
    33         Thread a = new Thread(ra, "a");
    34         Thread b = new Thread(rb, "b");
    35         a.start();
    36         b.start();
    37 
    38         a.join();
    39         b.join();
    40 
    41         System.out.println(Arrays.toString(s));
    42     }
    43 }

    注意第15行和26行,增加了同步关键字synchronized,在同步代码块中,临界资源s被锁住,保证同时只能有一个线程执行这段代码。

    1.3使用同步方法,解决买票问题

     1 package com.example.concurrency;
     2 
     3 public class MyTicket implements Runnable {
     4     
     5     private int ticket = 100;
     6 
     7     @Override
     8     public void run() {
     9         while (true) {
    10             if (!sale()) {
    11                 break;
    12             }
    13         }
    14     }
    15 
    16     private synchronized boolean sale() {
    17         if (ticket <= 0) {
    18             return false;
    19         }
    20         System.out.println(Thread.currentThread().getName() + "卖了第" + ticket + "张票");
    21         ticket--;
    22         return true;
    23     }
    24 }
     1 package com.example.concurrency;
     2 
     3 public class TestTicket {
     4     public static void main(String[] args) {
     5         // TODO Auto-generated method stub
     6         MyTicket ticket = new MyTicket();
     7         Thread t1 = new Thread(ticket, "win1");
     8         Thread t2 = new Thread(ticket, "win2");
     9         Thread t3 = new Thread(ticket, "win3");
    10         Thread t4 = new Thread(ticket, "win4");
    11 
    12         t1.start();
    13         t2.start();
    14         t3.start();
    15         t4.start();
    16     }
    17 }

    使用同步方法,就不会再将同一张票重复销售了。

    每个对象都有一个互斥锁标记,用来分配给线程。

    只有拥有对象互斥锁标记的线程,才能进入对该新对象加锁的同步代码块。

    线程退出同步代码块时,会释放相应的互斥锁标记。

    1.4存钱取钱问题

     1 package com.example.concurrency;
     2 
     3 public class BankCard {
     4     private double money;
     5 
     6     public double getMoney() {
     7         return money;
     8     }
     9 
    10     public void setMoney(double money) {
    11         this.money = money;
    12     }
    13 
    14     public void add(double money) {
    15         this.money += money;
    16     }
    17 
    18     public void sub(double money) {
    19         this.money -= money;
    20     }
    21 }
     1 package com.example.concurrency;
     2 
     3 public class TestBankCard {
     4 
     5     public static void main(String[] args) {
     6         // TODO Auto-generated method stub
     7         BankCard card = new BankCard();
     8 
     9         Runnable add = new Runnable() {
    10 
    11             @Override
    12             public void run() {
    13                 for (int i = 0; i < 10; i++) {
    14                     synchronized (card) {
    15                         card.add(100);
    16                         System.out.println("存入100元,当前余额为:" + card.getMoney());
    17                     }
    18                 }
    19 
    20             }
    21         };
    22 
    23         Runnable sub = new Runnable() {
    24             @Override
    25             public void run() {
    26                 // TODO Auto-generated method stub
    27                 for (int i = 0; i < 10; i++) {
    28                     synchronized (card) {
    29                         if (card.getMoney() >= 100) {
    30                             card.sub(100);
    31                             System.out.println("取出100元,当前余额为:" + card.getMoney());
    32                         } else {
    33                             System.out.println("余额不足");
    34                             i--;
    35                         }
    36                     }
    37                 }
    38             }
    39         };
    40 
    41         new Thread(add).start();
    42         new Thread(sub).start();
    43     }
    44 }

    同步规则:

    只有再调用包含同步代码块的方法,或者同步方法时,才需要对象的锁标记。

    如调用不包含同步代码得方法,或者普通方法时,则不需要锁标记,可直接使用。

    JDK中实现的同步数据结构有:StringBuffer、Vector、Hashtable。

    2.线程死锁

    2.1存在死锁问题的代码演示:

    1 package com.example.concurrency;
    2 
    3 public class MyLock {
    4     public static Object a = new Object();
    5     public static Object b = new Object();    
    6 }
     1 package com.example.concurrency;
     2 
     3 public class Boy extends Thread {    
     4     @Override
     5     public void run() {        
     6         synchronized (MyLock.a) {
     7             System.out.println("boy拿到了a");
     8             synchronized (MyLock.b) {
     9                 System.out.println("boy拿到了b");
    10                 System.out.println("boy获取了全部资源");    
    11             }            
    12         }
    13     }
    14 }
     1 package com.example.concurrency;
     2 
     3 public class Girl extends Thread {
     4     @Override
     5     public void run() {
     6         
     7         synchronized (MyLock.b) {
     8             System.out.println("girl拿到了b");
     9             synchronized (MyLock.a) {
    10                 System.out.println("girl拿到了a");
    11                 System.out.println("girl获取了全部资源");    
    12             }            
    13         }
    14     }
    15 }
     1 package com.example.concurrency;
     2 
     3 public class TestEat {
     4     public static void main(String[] args) {
     5         // TODO Auto-generated method stub
     6         Boy boy = new Boy();
     7         Girl girl = new Girl();
     8         
     9         boy.start();
    10         girl.start();
    11     }
    12 }

    执行TestEat中的main方法,有可能出现死锁。boy线程和girl线程,都在等待对方释放锁,但由于自己没有释放锁,对方也不能释放锁。

    2.2使用线程通信,避免出现死锁

     1 package com.example.concurrency;
     2 
     3 public class BankCard {
     4     private double money;
     5     
     6     private boolean flag = false;//标记当前是否有钱,true有钱,false没钱
     7 
     8     public double getMoney() {
     9         return money;
    10     }
    11 
    12     public void setMoney(double money) {
    13         this.money = money;
    14     }
    15 
    16     public synchronized void add(double money) throws Exception {
    17         while (flag) {
    18             this.wait();
    19 //            System.out.println("现在想存钱,但是账户里有钱,等待先取走");
    20         }
    21 
    22         this.money += money;
    23         this.flag = true;
    24         System.out.println("存入100元,账户余额:" + this.money);
    25         this.notifyAll();// 通知其他线程,可以获取锁
    26     }
    27 
    28     public synchronized void sub(double money) throws Exception {
    29         while (!flag) {
    30             this.wait();
    31 //            System.out.println("现在想取钱,但是账户没有钱,等待先存入");
    32         }
    33 
    34         this.money -= money;
    35         this.flag = false;
    36         System.out.println("取出100元,账户余额:" + this.money);
    37         this.notifyAll();
    38     }
    39 }
     1 package com.example.concurrency;
     2 
     3 public class TestBankCard {
     4 
     5     public static void main(String[] args) {
     6         // TODO Auto-generated method stub
     7         BankCard card = new BankCard();
     8 
     9         Runnable add = new Runnable() {
    10 
    11             @Override
    12             public void run() {
    13                 for (int i = 0; i < 10; i++) {
    14                     try {
    15                         card.add(100);
    16                     } catch (Exception e) {
    17                         // TODO Auto-generated catch block
    18                         e.printStackTrace();
    19                     }
    20                 }
    21 
    22             }
    23         };
    24 
    25         Runnable sub = new Runnable() {
    26             @Override
    27             public void run() {
    28                 // TODO Auto-generated method stub
    29                 for (int i = 0; i < 10; i++) {
    30                     try {
    31                         card.sub(100);
    32                     } catch (Exception e) {
    33                         // TODO Auto-generated catch block
    34                         e.printStackTrace();
    35                     }
    36                 }
    37             }
    38         };
    39 
    40         new Thread(add).start();
    41         new Thread(sub).start();
    42     }
    43 }

    执行结果如下:

     1 存入100元,账户余额:100.0
     2 取出100元,账户余额:0.0
     3 存入100元,账户余额:100.0
     4 取出100元,账户余额:0.0
     5 存入100元,账户余额:100.0
     6 取出100元,账户余额:0.0
     7 存入100元,账户余额:100.0
     8 取出100元,账户余额:0.0
     9 存入100元,账户余额:100.0
    10 取出100元,账户余额:0.0
    11 存入100元,账户余额:100.0
    12 取出100元,账户余额:0.0
    13 存入100元,账户余额:100.0
    14 取出100元,账户余额:0.0
    15 存入100元,账户余额:100.0
    16 取出100元,账户余额:0.0
    17 存入100元,账户余额:100.0
    18 取出100元,账户余额:0.0
    19 存入100元,账户余额:100.0
    20 取出100元,账户余额:0.0

    使用wait()和notifyAll()实现了线程的通信,完成“一存一取,交叉运行”的效果。

    3.生产者消费者问题

     1 package com.example.concurrency;
     2 
     3 public class Bread {
     4     private Integer id;
     5     private String name;    
     6     
     7     public Integer getId() {
     8         return id;
     9     }
    10     public void setId(Integer id) {
    11         this.id = id;
    12     }
    13     public String getName() {
    14         return name;
    15     }
    16     public void setName(String name) {
    17         this.name = name;
    18     }    
    19     
    20     public Bread() {
    21         super();
    22         // TODO Auto-generated constructor stub
    23     }
    24     public Bread(Integer id, String name) {
    25         super();
    26         this.id = id;
    27         this.name = name;
    28     }    
    29 }
     1 package com.example.concurrency;
     2 
     3 public class BreadFactory {
     4     private Bread[] factory = new Bread[10];
     5 
     6     int index = 0;
     7 
     8     public synchronized void product(Bread b) {
     9         while (index >= 5) {
    10             try {
    11                 this.wait();
    12             } catch (InterruptedException e) {
    13                 // TODO Auto-generated catch block
    14                 e.printStackTrace();
    15             }
    16         }
    17 
    18         factory[index] = b;
    19         index++;
    20         System.out.println(Thread.currentThread().getName() + "生产了1个面包,库存还有" + index + "个");
    21         this.notifyAll();
    22     }
    23 
    24     public synchronized Bread sale() {
    25         while (index <= 0) {
    26             try {
    27                 this.wait();
    28             } catch (InterruptedException e) {
    29                 // TODO Auto-generated catch block
    30                 e.printStackTrace();
    31             }
    32         }
    33         index--;
    34         Bread b = factory[index];
    35         factory[index] = null;
    36         System.out.println(Thread.currentThread().getName() + "销售了1个面包,库存还有" + index + "个");
    37         this.notifyAll();
    38         return b;
    39     }
    40 }
     1 package com.example.concurrency;
     2 
     3 public class BreadProd implements Runnable {
     4     private BreadFactory factory = new BreadFactory();
     5 
     6     private String name;
     7 
     8     public String getName() {
     9         return name;
    10     }
    11 
    12     public void setName(String name) {
    13         this.name = name;
    14     }        
    15 
    16     public BreadProd() {
    17         super();
    18         // TODO Auto-generated constructor stub
    19     }
    20 
    21     public BreadProd(BreadFactory factory, String name) {
    22         super();
    23         this.factory = factory;
    24         this.name = name;
    25     }
    26 
    27     @Override
    28     public void run() {
    29         // TODO Auto-generated method stub
    30         for (int i = 0; i < 20; i++) {
    31             factory.product(new Bread(i, this.name));
    32         }
    33     }
    34 }
     1 package com.example.concurrency;
     2 
     3 public class BreadSaler implements Runnable {
     4     private BreadFactory factory = new BreadFactory();
     5 
     6     private String name;
     7 
     8     public String getName() {
     9         return name;
    10     }
    11 
    12     public void setName(String name) {
    13         this.name = name;
    14     }    
    15 
    16     public BreadSaler() {
    17         super();
    18         // TODO Auto-generated constructor stub
    19     }
    20 
    21     public BreadSaler(BreadFactory factory, String name) {
    22         super();
    23         this.factory = factory;
    24         this.name = name;
    25     }
    26 
    27     @Override
    28     public void run() {
    29         // TODO Auto-generated method stub
    30         for (int i = 0; i < 20; i++) {
    31             factory.sale();
    32         }
    33     }
    34 }
     1 package com.example.concurrency;
     2 
     3 public class BreadTest {
     4     public static void main(String[] args) {
     5         BreadFactory factory = new BreadFactory();
     6         BreadProd prod = new BreadProd(factory, "顾客");
     7         BreadSaler saler = new BreadSaler(factory, "面包店");
     8 
     9         new Thread(prod).start();
    10         new Thread(saler).start();            
    11     }
    12 }

    执行结果可能为:

     1 Thread-0生产了1个面包,库存还有1个
     2 Thread-0生产了1个面包,库存还有2个
     3 Thread-0生产了1个面包,库存还有3个
     4 Thread-0生产了1个面包,库存还有4个
     5 Thread-0生产了1个面包,库存还有5个
     6 Thread-1销售了1个面包,库存还有4个
     7 Thread-1销售了1个面包,库存还有3个
     8 Thread-1销售了1个面包,库存还有2个
     9 Thread-1销售了1个面包,库存还有1个
    10 Thread-1销售了1个面包,库存还有0个
    11 Thread-0生产了1个面包,库存还有1个
    12 Thread-0生产了1个面包,库存还有2个
    13 Thread-0生产了1个面包,库存还有3个
    14 Thread-0生产了1个面包,库存还有4个
    15 Thread-0生产了1个面包,库存还有5个
    16 Thread-1销售了1个面包,库存还有4个
    17 Thread-1销售了1个面包,库存还有3个
    18 Thread-1销售了1个面包,库存还有2个
    19 Thread-1销售了1个面包,库存还有1个
    20 Thread-1销售了1个面包,库存还有0个
    21 Thread-0生产了1个面包,库存还有1个
    22 Thread-0生产了1个面包,库存还有2个
    23 Thread-0生产了1个面包,库存还有3个
    24 Thread-0生产了1个面包,库存还有4个
    25 Thread-0生产了1个面包,库存还有5个
    26 Thread-1销售了1个面包,库存还有4个
    27 Thread-1销售了1个面包,库存还有3个
    28 Thread-1销售了1个面包,库存还有2个
    29 Thread-1销售了1个面包,库存还有1个
    30 Thread-1销售了1个面包,库存还有0个
    31 Thread-0生产了1个面包,库存还有1个
    32 Thread-0生产了1个面包,库存还有2个
    33 Thread-0生产了1个面包,库存还有3个
    34 Thread-0生产了1个面包,库存还有4个
    35 Thread-0生产了1个面包,库存还有5个
    36 Thread-1销售了1个面包,库存还有4个
    37 Thread-1销售了1个面包,库存还有3个
    38 Thread-1销售了1个面包,库存还有2个
    39 Thread-1销售了1个面包,库存还有1个
    40 Thread-1销售了1个面包,库存还有0个

    生产的产品数量最多为5个,消费后的产品数量不会小于0个。 

  • 相关阅读:
    call和apply的区别
    淘宝镜像(cnpm)的安装和使用
    文件包含漏洞
    vue简单的日历
    微信小程序(mpvue)—解决视频播放bug的一种方式
    vue 异步组件
    vuex的学习笔记
    vue2.0 添加监听滚动事件
    jquery tmpl生成导航
    vue 控制视图
  • 原文地址:https://www.cnblogs.com/asenyang/p/14373236.html
Copyright © 2020-2023  润新知