标签:
这一章节我们来讨论一下脏读(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
标签:
原文地址:http://blog.csdn.net/raylee2007/article/details/51246405