从头认识多线程-2.3 脏读(DirtyRead)

这一章节我们来讨论一下脏读(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

时间: 2024-08-09 13:13:55

从头认识多线程-2.3 脏读(DirtyRead)的相关文章

从头认识多线程-2.8 缓解同步方法的隐患-同步代码块

这一章节我们来讨论一下缓解同步方法的隐患-同步代码块. 1.思路:把同步方法,降低同步的粒度,同步到代码块 2.根据上一章节的例子,我们把代码修改一下 (1)第一种方法,把同步标记移到更新的那一栏里面去,一般来说大部分都是更新的时候需要同步数据 package com.ray.deepintothread.ch02.topic_9; /** * 从头认识多线程-2.8 缓解同步方法的隐患-同步代码块<br> * * @author RayLee * */ public class Relief

从头认识多线程-2.14 解决由同步的synchronized (newobject()) 引起的脏读的方法

这一章节我们来讨论一下解决由同步的synchronized (newobject()) 引起的脏读的方法. 1.造成上面脏读的原因:多线程多方法同时访问并修改某个属性域 2.解决思路 顺着上面的原因,笔者提出两种不是方法的方法 (1)只能单线程单方法访问并修改某个属性域这样就保证了执行的顺序(也就是需要同步所有访问并修改的步骤)(缺点:性能低下,优点:数据的强一致性) (2)在多线程多方法的情况下,只能够访问多个属性域,不能够同时访问并修改某一个单一的属性域(根本目的就是分开运算,但是,在生产环

从头认识多线程-2.13 由同步的synchronized (newobject()) 引起的异步现象和脏读

这一章节我们来讨论一下由同步的synchronized (newobject()) 引起的异步现象和脏读. 1.代码清单 package com.ray.deepintothread.ch02.topic_14; /** * * @author RayLee * */ public class AsynchOfSynchWithUnSameObject { public static void main(String[] args) throws InterruptedException { M

从头认识多线程-2.15 证明使用属性域作为多线程监视器是不同步的

这一章节接着上一章节最后的错误的思路,我们来证明使用整数属性域作为多线程监视器是不同步的. 1.用同一个属性域作为多线程监视器,是不同步的 package com.ray.deepintothread.ch02.topic_16; /** * * @author RayLee * */ public class DirtyReadWithSynchBlock { public static void main(String[] args) throws InterruptedException

java多线程,如何防止脏读数据

多线程容易“非线程安全”的情况,是由于用了全局变量,而又没有很好的控制起情况.所以无论做什么程序,谨慎使用全局变量 "非线程安全"其实会在多个线程对同一个对象中的实例变量进行并发访问时发生,产生的后果就是“脏”读. 如以下代码就会出现“脏”读的情况: package com.ming.thread.t1; public class Count { int count=0; public void add(String str){ if("a".equals(str)

从头认识多线程-4.1 对象的发布(Publish)、逸出(Escape)以及逸出的解决方案

这一章节我们来讨论一下对象的发布与逸出. 其实在前两个章节我们都有想应的讨论,只不过有一些不用补充的问题,我将会放到这个章节里面去. 1.发布(Publish) 当一个对象能够给其他代码引用. package com.ray.deepintothread.ch04.topic_1; import java.util.HashMap; public class Publish { private HashMap<String, Object> map = null; public HashMap

从头认识多线程-2.2 synchronized持有对象锁与类锁的相同点

这一章节我们来讨论一下synchronized持有对象锁与类锁的相同点. 1.当所有方法都不使用同步的时候 代码清单 package com.ray.deepintothread.ch02.topic_2; public class SynchInstance1 { public static void main(String[] args) throws InterruptedException { MyTestObjectOne myTestObjectOne = new MyTestObj

从头认识多线程-4.3 ThreadLocal使用时需要注意的地方

4.3 这一章节我们来讨论一下关于ThreadLocal的使用的时候需要注意的地方 ThreadLocal主要的使用是get.set.initialValue这几个方法,具体的使用我们这里不做介绍,下面只是举一些它使用的时候需要注意的地方. 1.在get方法的时候出现null package com.ray.deepintothread.ch04.topic_3; public class ThreadLocalGetNull { private int count = 0; public Th

从头认识多线程-3.2 使用volatile声明的变量的写操作是非原子性的

这一章节我们来讨论一下使用volatile声明的变量的各种操作是非原子性的. 1.上一章节我们已经提到,volatile把工作内存里面变量的改变同步到主内存, 使得各个线程能够把该变量当成是整体的状态控制 2.但是,使用volatile声明的变量的写操作是非原子性的 代码清单: package com.ray.deepintothread.ch03.topic_2; public class VolatileTest extends Thread { private volatile int i