脏读
脏读一定会出现在操作实例变量的情况下,这就是不同线程”争抢“实例变量的结果。
脏读示例
定义共享变量实例类
package com.chapter02.dirtyRead;
public class PublicVar {
public String username = "A";
public String password = "AA";
//添加synchronized
synchronized public void setVaule(String username, String password) {
try {
this.username = username;
Thread.sleep(5000);
this.password = password;
System.out.println("setValue method , thread name= " + Thread.currentThread().getName() +
", username = " + username +
", password = " + password);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//无 synchronized
public void getValue() {
System.out.println("getValue method , thread name= " + Thread.currentThread().getName() +
", username = " + username +
", password = " + password);
}
}
自定义线程类
package com.chapter02.dirtyRead;
public class ThreadA extends Thread {
private PublicVar publicVar;
public ThreadA(PublicVar publicVar) {
this.publicVar = publicVar;
}
@Override
public void run() {
super.run();
publicVar.setVaule("B", "BB");
}
}
启动类
package com.chapter02.dirtyRead;
public class TestRun {
public static void main(String[] args) {
try {
PublicVar publicVar = new PublicVar();
ThreadA threadA = new ThreadA(publicVar);
threadA.start();
Thread.sleep(2000); //打印结果受此值的影响
publicVar.getValue();
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
执行结果
//出现脏读
getValue method , thread name= main, username = B, password = AA
setValue method , thread name= Thread-0, username = B, password = BB
总结
当A线程调用PublicVar对象加入synchronized关键字的setVaule()方法时,A线程就获得了setVaule()方法的锁,更准确的讲,是获得了对象的锁,所以其他线程必须等到A线程执行完毕才可以调用setVaule()方法,但B线程可以随意调用其他的非synchronized方法。
防止脏读示例
修改
修改上面“定义共享变量实例类PublicVar ”中的getValue()方法,增加synchronized关键字,如下:
package com.chapter02.dirtyRead;
public class PublicVar {
public String username = "A";
public String password = "AA";
//添加synchronized
synchronized public void setVaule(String username, String password) {
try {
this.username = username;
Thread.sleep(5000);
this.password = password;
System.out.println("setValue method , thread name= " + Thread.currentThread().getName() +
", username = " + username +
", password = " + password);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//有 synchronized
synchronized public void getValue() {
System.out.println("getValue method , thread name= " + Thread.currentThread().getName() +
", username = " + username +
", password = " + password);
}
}
执行结果
setValue method , thread name= Thread-0, username = B, password = BB
getValue method , thread name= main, username = B, password = BB
总结
当A线程调用PublicVar对象加入synchronized关键字的setVaule()方法时,A线程就获得了setValue()方法所在对象的锁,所以其他线程必须等A线程执行完毕才可以调用setValue()方法,而B线程如果调用声明了synchronized关键字的getValue()方法时,必须等A线程将setValue()方法执行完,也就是释放对象锁后才可以调用。这时A线程执行了一个完整的任务,也就是说username和password这两个实例变量已经同时被赋值,不存在脏读的基本环境。