zoukankan      html  css  js  c++  java
  • 多线程(二)线程同步

    线程同步问题:多线程的同步问题指的是多个线程同时修改一个数据的时候,可能导致的问题 -多线程的问题,又叫Concurrency 问题 。

    同步问题举例

    假设有一个数x=1000,有两个线程,一个给x加一操作、另一个给x减一操作,

    那么在执行n次之后,讲道理x应该还是等于1000的,但是因为线程同步的问题,最后会出现x!=1000的情况。

     1 package UML;
     2 
     3 public class Concurrency_test {
     4 
     5     public static void main(String[] args) {
     6         // TODO Auto-generated method stub
     7         final Hero gareen = new Hero();
     8         gareen.name = "盖伦";
     9         gareen.hp = 10000;
    10            
    11         System.out.printf("盖伦的初始血量是%.0f%n", gareen.hp);
    12            
    13         //多线程同步问题指的是多个线程同时修改一个数据的时候,导致的问题
    14            
    15         //假设盖伦有10000滴血,并且在基地里,同时又被对方多个英雄攻击
    16            
    17         //用JAVA代码来表示,就是有多个线程在减少盖伦的hp
    18         //同时又有多个线程在恢复盖伦的hp
    19            
    20         //n个线程增加盖伦的hp
    21            
    22         int n = 10000;
    23    
    24         Thread[] addThreads = new Thread[n];
    25         Thread[] reduceThreads = new Thread[n];
    26            
    27         for (int i = 0; i < n; i++) {
    28             Thread t = new Thread(){
    29                 public void run(){
    30                     gareen.recover();
    31                     try {
    32                         Thread.sleep(100);
    33                     } catch (InterruptedException e) {
    34                         // TODO Auto-generated catch block
    35                         e.printStackTrace();
    36                     }
    37                 }
    38             };
    39             t.start();
    40             addThreads[i] = t;
    41                
    42         }
    43            
    44         //n个线程减少盖伦的hp
    45         for (int i = 0; i < n; i++) {
    46             Thread t = new Thread(){
    47                 public void run(){
    48                     gareen.hurt();
    49                     try {
    50                         Thread.sleep(100);
    51                     } catch (InterruptedException e) {
    52                         // TODO Auto-generated catch block
    53                         e.printStackTrace();
    54                     }
    55                 }
    56             };
    57             t.start();
    58             reduceThreads[i] = t;
    59         }
    60            
    61         //等待所有增加线程结束
    62         for (Thread t : addThreads) {
    63             try {
    64                 t.join();
    65             } catch (InterruptedException e) {
    66                 // TODO Auto-generated catch block
    67                 e.printStackTrace();
    68             }
    69         }
    70         //等待所有减少线程结束
    71         for (Thread t : reduceThreads) {
    72             try {
    73                 t.join();
    74             } catch (InterruptedException e) {
    75                 // TODO Auto-generated catch block
    76                 e.printStackTrace();
    77             }
    78         }
    79            
    80         //代码执行到这里,所有增加和减少线程都结束了
    81            
    82         //增加和减少线程的数量是一样的,每次都增加,减少1.
    83         //那么所有线程都结束后,盖伦的hp应该还是初始值
    84            
    85         //但是事实上观察到的是:
    86                    
    87         System.out.printf("%d个增加线程和%d个减少线程结束后%n盖伦的血量变成了 %.0f%n", n,n,gareen.hp);
    88     }
    89 
    90 }
    Concurrency_test.java
     1 package UML;
     2 import java.util.*;
     3 public class Hero implements Comparable<Hero>{
     4     public String name;
     5     public float hp;
     6     public int damage;
     7     public Hero() {
     8     }
     9     // 增加一个初始化name的构造方法
    10     public Hero(String name) {
    11  
    12         this.name = name;
    13     }
    14     // 重写toString方法
    15     public String toString() {
    16         //return String.format("[name:%s hp:%.0f damage:%.0f]%n", name,hp,damage);
    17         return "Hero [name=" + name + ", hp=" + hp + ", damage=" + damage + "]
    ";
    18     }
    19     public Hero(String name,int hp,int damage) {
    20         this.name=name;
    21         this.hp=hp;
    22         this.damage=damage;
    23     }
    24     @Override
    25     public int compareTo(Hero anotherHero) {
    26         if(damage<anotherHero.damage)
    27             return 1; 
    28         else
    29             return -1;
    30     }
    31     public void attackHero(Hero h) {
    32         //把暂停时间去掉,多条线程各自会尽力去占有CPU资源
    33         //线程的优先级效果才可以看得出来
    34         /*try {
    35             Thread.sleep(1000);
    36         }catch (InterruptedException e) {
    37             // TODO Auto-generated catch block
    38             e.printStackTrace();
    39         }*/
    40         h.hp-=damage;
    41         System.out.format("%s is attacking %s,%s'blood now is %.0f%n",name,h.name,h.name,h.hp);
    42         if(h.isDead())
    43             System.out.println(h.name +"死了!");
    44     }
    45     public boolean isDead() {
    46         return 0>=hp?true:false;
    47     }
    48     //回血
    49     public void recover(){
    50         hp=hp+1;
    51     }
    52      
    53     //掉血
    54     public void hurt(){
    55         hp=hp-1;
    56     }
    57 }
    Hero.java

    同步问题产生的原因

    1. 假设增加线程先进入,得到的hp是10000
    2. 进行增加运算
    3. 正在做增加运算的时候,还没有来得及修改hp的值,减少线程来了
    4. 减少线程得到的hp的值也是10000
    5. 减少线程进行减少运算
    6. 增加线程运算结束,得到值10001,并把这个值赋予hp
    7. 减少线程也运算结束,得到值9999,并把这个值赋予hp
    hp,最后的值就是9999
    虽然经历了两个线程各自增减了一次,本来期望还是原值10000,但是却得到了一个9999
    这个时候的值9999是一个错误的值,在业务上又叫做脏数据

    同步问题解决思路是: 在增加线程访问hp期间,其他线程不可以访问hp

    1. 增加线程获取到hp的值,并进行运算
    2. 在运算期间,减少线程试图来获取hp的值,但是不被允许
    3. 增加线程运算结束,并成功修改hp的值为10001
    4. 减少线程,在增加线程做完后,才能访问hp的值,即10001
    5. 减少线程运算,并得到新的值10000

     本文参考自:

    https://how2j.cn/k/thread/thread-synchronized/355.html

    https://blog.csdn.net/qq_41658124/article/details/103337513

    https://blog.csdn.net/qq_41658124/category_9152220.html

    https://blog.csdn.net/weixin_40271838/article/details/79998327

  • 相关阅读:
    zufeoj NO.1(结构体简单题)
    用链表写插入排序
    用链表写插入排序
    POJ 3468 A Simple Problem with Integers(线段树)
    POJ 3468 A Simple Problem with Integers(线段树)
    HDU 3065 病毒侵袭持续中(AC自动机)
    HDU 3065 病毒侵袭持续中(AC自动机)
    ZOJ 3436 July Number(DFS)
    poj 3414 Pots(广搜BFS+路径输出)
    AOP技术应用和研究--AOP简单应用
  • 原文地址:https://www.cnblogs.com/xuechengmeigui/p/13160155.html
Copyright © 2011-2022 走看看