对于对象的同步和异步的方法,我们在设计自己的程序的时候,一定要考虑问题的整 体,不然就会出现数据不一致的错误,很经典的错误就是脏读( dirtyread)示例代码如 下:
package com.bjsxt.chapter07;
import java.util.concurrent.TimeUnit;
public class DirtyRead {
private String username = "bjsxt";
private String password = "123";
public synchronized void setValue(String username,String password){
this.username = username;
try{
TimeUnit.SECONDS.sleep(2);
}catch (InterruptedException e){
e.printStackTrace();
}
this.password = password;
System.out.println("set username : "+username+" password : "+password);
}
public void getValue(){
System.out.println("get username : "+username+" password : "+password);
}
public static void main(String[] args)throws InterruptedException {
DirtyRead dr = new DirtyRead();
new Thread(){
@Override
public void run() {
dr.setValue("zs","456");
}
}.start();
TimeUnit.SECONDS.sleep(1);
dr.getValue();// 不需要锁就能执行方法,会读写了一半的数据
}
}
运行结果
与预期不一样,想要的是 zs 456
出现这种结果是因为 get方法没加锁,导致线程2访问到了线程1未put完全的一半的数据,。
修改代码
package com.bjsxt.chapter07;
import java.util.concurrent.TimeUnit;
public class DirtyRead02 {
private String username = "bjsxt";
private String password = "123";
public synchronized void setValue(String username,String password){
this.username = username;
try{
TimeUnit.MINUTES.sleep(1);
}catch (InterruptedException e){
e.printStackTrace();
}
this.password = password;
System.out.println("set username : "+username+" password : "+password);
}
public synchronized void getValue(){// 加了锁,只有线程执行完所有的put,才会释放锁,才会让main执行读取操作
System.out.println("get username : "+username+" password : "+password);
}
public static void main(String[] args)throws InterruptedException {
DirtyRead02 dr = new DirtyRead02();
new Thread(){
@Override
public void run() {
dr.setValue("zs","456");
}
}.start();
TimeUnit.SECONDS.sleep(30);
dr.getValue();
}
}
运行结果
示例总结:
在我们对一个对象的方法加锁的时候,需要考虑业务的整体性,即为 setValue/getValue 方法同时加锁 synchronized 同步关键字,保证业务(service)的 原子性,不然会出现业务错误(也从侧面保证业务的一致性)。