多线程(二)线程同步

线程同步问题:多线程的同步问题指的是多个线程同时修改一个数据的时候,可能导致的问题 -多线程的问题,又叫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

原文地址:https://www.cnblogs.com/xuechengmeigui/p/13160155.html