脏读
脏读:多个线程共享资源调,一些线程还在进行写入修改操作,而另外一些线程读取到了尚未写入完成可能进行事务回滚的数据。
class User {
private String username = "A";
private String password = "AA";
synchronized public void setValue(String use, String pwd){
try {
//可能会进行事务的回滚,即写入数据不成功还是原来的值。
this.username = use;
Thread.sleep(5000);
this.password = pwd;
System.out.println(Thread.currentThread().getName() + ": " + username + " " + password);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void getValue(){
System.out.println(Thread.currentThread().getName() + ": " + username + " " + password);
}
}
class userA extends Thread{
private User use;
userA(User use,String name){
super(name);
this.use = use;
}
@Override
public void run() {
use.setValue("Splay","Treap");
}
}
class userB extends Thread{
private User use;
userB(User use,String name){
super(name);
this.use = use;
}
@Override
public void run() {
use.getValue();
}
}
public class Test {
public static void main(String[] args) {
User user = new User();
userA a = new userA(user,"A");
userB b = new userB(user,"B");
a.start();
b.start();
/*
脏读: A线程修改数据未完成时,B读到了A没有成功提交的数据。
sychronized只同步了setValue方法,并没有同步getValue方法。
此时A线程和B线程可以形成异步访问,A虽然拿到了实例化对象User的对象锁,
但是对象锁只锁了所有的同步方法,对于非同步方法并没有锁住。
所以B会脏读到A未提交的数据。解决方法就是将getValue方法变为同步方法。
*/
}
}
分析:最开始的username = “A”, password = “AA”。此时A线程先去修改用户名和密码,但是此时没有修改完成被B读取到了这些尚未完成提交的数据。但是A线程具体能否成功写入当前数据按照业务顺序来说是无法判断的。所以B读取到的username = "Spaly"是一个脏数据。解决方法就是将getValue方法也使用synchronized关键词进行同步。
结论: 这也从侧面反应出当多个线程共享一个实例化对象时,synchronized方法和普通方法是可以异步访问的;即一个线程抱到对象锁后,其他线程继续访问该对象的其他synchronized方法需要同步,但是可以通过异步的方式访问该对象的其他非synchronized的方法。