这一章节我们来讨论一下脏读(DirtyRead)。
1.为什么出现脏读?
因为代码没有做同步,虽然set方法同步,但是由于get方法一般都会忘了,导致读的值是被写过的
2.代码清单
(1)由于程序没有同步,并且执行太快,导致脏读
package com.ray.deepintothread.ch02.topic_4;
public class DirtyRead {
public static void main(String[] args) throws InterruptedException {
MyTestObjectOne myTestObjectOne = new MyTestObjectOne();
ThreadOne threadTwo = new ThreadOne(myTestObjectOne);
Thread thread = new Thread(threadTwo);
thread.start();
System.out.println("id:" + myTestObjectOne.getId() + " name:" + myTestObjectOne.getName());
}
}
class ThreadOne implements Runnable {
private MyTestObjectOne myTestObjectOne;
public ThreadOne(MyTestObjectOne myTestObjectOne) {
this.myTestObjectOne = myTestObjectOne;
}
@Override
public void run() {
myTestObjectOne.setId(1);
myTestObjectOne.setName("ray");
}
}
class MyTestObjectOne {
private int id = 0;
private String name = "init";
public int getId() {
return id;
}
public synchronized void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public synchronized void setName(String name) {
this.name = name;
}
}
输出:
id:0 name:init
由于没有做同步,因此在set的线程启动时,已经get了初始化的值出来
(2)由于没有做同步,代码的执行先后顺序和执行时间无法控制,导致读取的值是已经修改过的
package com.ray.deepintothread.ch02.topic_4;
public class DirtyRead2 {
public static void main(String[] args) throws InterruptedException {
MyTestObjectTwo myTestObjectTwo = new MyTestObjectTwo();
ThreadTwo threadTwo = new ThreadTwo(myTestObjectTwo);
Thread thread = new Thread(threadTwo);
thread.start();
Thread.sleep(100);// 是否出现脏读,这里程序的执行时间,跟下面的执行时间有密切关系
System.out.println("id:" + myTestObjectTwo.getId() + " name:" + myTestObjectTwo.getName());
}
}
class ThreadTwo implements Runnable {
private MyTestObjectTwo myTestObjectTwo;
public ThreadTwo(MyTestObjectTwo myTestObjectTwo) {
this.myTestObjectTwo = myTestObjectTwo;
}
@Override
public void run() {
myTestObjectTwo.setId(1);
try {
Thread.sleep(1000);// 代码执行时间
} catch (InterruptedException e) {
e.printStackTrace();
}
myTestObjectTwo.setName("ray");
}
}
class MyTestObjectTwo {
private int id = 0;
private String name = "init";
public int getId() {
return id;
}
public synchronized void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public synchronized void setName(String name) {
this.name = name;
}
}
输出:
id:1 name:init
从输出很明显的可以看到,id已经被修改了,但是还没有修改name的时候,就已经开始读取了。
3.解决方案-同步,而且是有技巧的同步
package com.ray.deepintothread.ch02.topic_4;
public class DirtyRead3 {
public static void main(String[] args) throws InterruptedException {
MyTestObjectThree myTestObjectThree = new MyTestObjectThree();
ThreadThree threadThree = new ThreadThree(myTestObjectThree);
Thread thread = new Thread(threadThree);
thread.start();
ThreadFour threadFour = new ThreadFour(myTestObjectThree);
Thread thread1 = new Thread(threadFour);
thread1.start();
}
}
class ThreadThree implements Runnable {
private MyTestObjectThree myTestObjectThree;
public ThreadThree(MyTestObjectThree myTestObjectThree) {
this.myTestObjectThree = myTestObjectThree;
}
@Override
public void run() {
myTestObjectThree.setValue(1, "ray");
}
}
class ThreadFour implements Runnable {
private MyTestObjectThree myTestObjectThree;
public ThreadFour(MyTestObjectThree myTestObjectThree) {
this.myTestObjectThree = myTestObjectThree;
}
@Override
public void run() {
myTestObjectThree.getValue();
}
}
class MyTestObjectThree {
private int id = 0;
private String name = "init";
public synchronized void setValue(int id, String name) {
this.id = id;
this.name = name;
}
public synchronized void getValue() {
System.out.println("id:" + id + " name:" + name);
}
}
输出:
id:1 name:ray
同步的确能够解决上面的问题,但是有一点需要注意的是,上面的set方法,是把两个属性一起设置的,如果分开了,就不行的,同理,get的方法同时得到两者,分开的那种不行。
总结:这一章节我们讨论了脏读的形成与解决方案。
这一章节就到这里,谢谢
------------------------------------------------------------------------------------
我的github:https://github.com/raylee2015/DeepIntoThread
目录:http://blog.csdn.net/raylee2007/article/details/51204573