标签:set name 账户 银行 t1 output balance 模拟 String
很重要,对于理解 锁的是哪个对象很重要以及volatile的可见性并不能代表数据的一致性以及数据的原子性,因此volatile并非是数据安全的。
模拟银行账户读写,数据是否一致
public class Account_01 {
private String name;
private int balance;
void set(String name, int balance) {
// 此处睡两秒是为了保证 main 主线程先执行一次getBalance方法
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.name = name;
this.balance = balance;
}
int getBalance(String name) {
return balance;
}
public static void main(String[] args) throws InterruptedException {
Account_01 a = new Account_01();
new Thread(() -> a.set("zhangsan", 2), "t1").start();
System.out.println("thread t1 exe set before balance value : " + a.getBalance("zhangsan"));
// 此处睡3 秒是为了保证线程 t1 set 方法 执行完毕
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("thread t1 exe set after balance value: " + a.getBalance("zhangsan"));
}
}
输出结果:
thread t1 exe set before balance value : 0
thread t1 exe set after balance value: 2
出现了脏读现象,现在为了保持 写入的数据和读到的数据保持一致,代码做以下更改。
加synchronized
public class Account_01 {
private String name;
private int balance;
synchronized void set(String name, int balance) {
// 此处睡两秒是为了保证 main 主线程先执行一次getBalance方法
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.name = name;
this.balance = balance;
}
synchronized int getBalance(String name) {
return balance;
}
public static void main(String[] args) throws InterruptedException {
Account_01 a = new Account_01();
new Thread(() -> a.set("zhangsan", 2), "t1").start();
System.out.println("thread t1 exe set before balance value : " + a.getBalance("zhangsan"));
// 此处睡3 秒是为了保证线程 t1 set 方法 执行完毕
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("thread t1 exe set after balance value: " + a.getBalance("zhangsan"));
}
}
输出结果:
thread t1 exe set before balance value : 2
thread t1 exe set after balance value: 2
读和写结果是一致的,此处说明了 synchronized 锁定的是当前对象,当 我们去调用get 方法时提示有锁,需要等待set方法执行完毕之后才可以执行 get方法。 重点,方法上的锁是对象的。
为啥不用volatile 呢, 试试看下输出结果。
public class Account_01 {
private String name;
private volatile int balance;
synchronized void set(String name, int balance) {
// 此处睡两秒是为了保证 main 主线程先执行一次getBalance方法
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.name = name;
this.balance = balance;
}
int getBalance(String name) {
return balance;
}
public static void main(String[] args) throws InterruptedException {
Account_01 a = new Account_01();
new Thread(() -> a.set("zhangsan", 2), "t1").start();
System.out.println("thread t1 exe set before balance value : " + a.getBalance("zhangsan"));
// 此处睡3 秒是为了保证线程 t1 set 方法 执行完毕
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("thread t1 exe set after balance value: " + a.getBalance("zhangsan"));
}
}
输出结果:
thread t1 exe set before balance value : 0
thread t1 exe set after balance value: 2
输出结果 是读和写不一致,再一次说明了 volatile 只能保持数据的可见性,并不能说明数据的一致性,synchronized 才可以保证数据的原子性和一致性。
用了 synchronized 之后还需要用volatile 吗? 不需要。****synchronized 已经保证了多线程之间数据的一致性了。
模拟多线程写,用map存
public class Account_01 {
private String name;
private volatile int balance;
private Map<String, Integer> map = new HashMap<>();
synchronized void set(String name, int balance) {
balance = (Objects.isNull(map.get(name)) ? 0 : map.get(name)) + balance;
map.put(name, balance);
}
synchronized int getBalance(String name) {
return (Objects.isNull(map.get(name)) ? 0 : map.get(name));
}
public static void main(String[] args) throws InterruptedException {
Account_01 a = new Account_01();
CountDownLatch start = new CountDownLatch(1);
CountDownLatch end = new CountDownLatch(20);
for (int i = 0; i < 20; i++) {
new Thread(() -> {
try {
start.await();
a.set("zhangsan", 2);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
end.countDown();
}
}, "t1").start();
}
start.countDown();
end.await();
System.out.println("输出结果: " + a.getBalance("zhangsan"));
}
}
输出结果:
输出结果: 40 // get 方法加锁了,等到 set 完成释放锁之后才拿到锁执行get 方法获取到值
是否为同一把锁
@SuppressWarnings("all")
public class Account_01 {
private String name;
private volatile int balance;
private ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
void set(String name, int balance) {
synchronized (this) {
balance = (Objects.isNull(map.get(name)) ? 0 : map.get(name)) + balance;
}
map.put(name, balance);
}
synchronized int getBalance(String name) {
return (Objects.isNull(map.get(name)) ? 0 : map.get(name));
}
public static void main(String[] args) throws InterruptedException {
for (int n = 0; n < 500000; n++) {
Account_01 a = new Account_01();
CountDownLatch start = new CountDownLatch(1);
CountDownLatch end = new CountDownLatch(20);
for (int i = 0; i < 20; i++) {
new Thread(() -> {
try {
start.await();
a.set("zhangsan", 2);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
end.countDown();
}
}, "t1").start();
}
start.countDown();
end.await();
if (a.getBalance("zhangsan") != 40) {
System.out.println("Incorrect result output: " + a.getBalance("zhangsan"));
}
System.out.println("Correct result output: " + a.getBalance("zhangsan"));
}
}
}
输出结果:
Correct result output: 40
Correct result output: 40
Incorrect result output: 38
Correct result output: 38
Correct result output: 40
Correct result output: 40
Incorrect result output: 38
Correct result output: 38
Correct result output: 40
Correct result output: 40
Correct result output: 40
Correct result output: 40
Correct result output: 40
Correct result output: 40
Correct result output: 40
Correct result output: 40‘
很显然输出结果里面有读写不一致的情况。这个代码里面我们用到了ConcurrentHashMap 为啥还读写不一致呢,个人认为 读和写并不是同一把锁导致的。所以要体现出读写一致,必定需要使用同一把锁
标签:set,name,账户,银行,t1,output,balance,模拟,String 来源: https://www.cnblogs.com/xin-one/p/14956680.html
本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享; 2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关; 3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关; 4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除; 5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。