一.问题引入
我们自己在设计程序的时候,一定要考虑问题的整体,不然就会出现数据不一致的错误,很经典的问题就是脏读.如以下代码.
public class Test2 {
private String username="w";
private String password="123";
public synchronized void setValue(String username,String password){
this.username=username;
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.password=password;
}
public void getValue(){
System.out.println("username: "+username+" password: "+password);
}
public static void main(String[] args) {
Test2 test = new Test2();
new Thread(new Runnable() {
@Override
public void run() {
test.setValue("l","456"); //这个语句锁住了test对象
}
}).start();
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
test.getValue();
}
}
代码运行结果如下:
本来结果应该是l,456的,但是却发生了逻辑的混乱,出现了这样的结果,原因就是getValue方法没有加synchronized.
补充一下知识,当一个方法加了synchronized之后,它的锁对象是什么呢?
非静态方法:this,即调用该方法的对象
静态方法:当前类的字节码对象
所以呢?解决办法就是在getVlaue方法上加锁.如下.
public class Test2 {
private String username="w";
private String password="123";
public synchronized void setValue(String username,String password){
this.username=username;
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.password=password;
}
public synchronized void getValue(){
System.out.println("username: "+username+" password: "+password);
}
public static void main(String[] args) {
Test2 test = new Test2();
new Thread(new Runnable() {
@Override
public void run() {
test.setValue("l","456"); //thread1线程,这个语句锁住了test对象
}
},"thread1").start();
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
//main线程,因为方法上加了锁,所以会尝试获取test对象锁,此时被thread1线程占用,进入阻塞状态
test.getValue();
}
}
执行结果就是我们想要的样子,不会发生脏读,具体原因参考注释.
二.总结
在我们对一个对象的方法加锁的时候,要考虑业务的整体性,即为setValue/getValue方法同时加上synchronized同步关键字,保证业务的原子性,不然会出现错误(也从侧面保证业务的原子性)