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

这一章节我们来讨论一下解决由同步的synchronized (newobject()) 引起的脏读的方法。

1.造成上面脏读的原因:多线程多方法同时访问并修改某个属性域

2.解决思路

顺着上面的原因,笔者提出两种不是方法的方法

(1)只能单线程单方法访问并修改某个属性域这样就保证了执行的顺序(也就是需要同步所有访问并修改的步骤)(缺点:性能低下,优点:数据的强一致性)

(2)在多线程多方法的情况下,只能够访问多个属性域,不能够同时访问并修改某一个单一的属性域(根本目的就是分开运算,但是,在生产环境中,的确很难把一个属性域分成2个来运算,所以,笔者更趋向于第一种方法)

3.代码清单

(1)第一个思路

package com.ray.deepintothread.ch02.topic_15;

/**
 *
 * @author RayLee
 *
 */
public class SolutionOfDirtyRead {
	public static void main(String[] args) throws InterruptedException {
		MyService2 myService = new MyService2();
		ThreadThree threadThree = new ThreadThree(myService);
		Thread thread = new Thread(threadThree);
		thread.start();
		ThreadFour threadFour = new ThreadFour(myService);
		Thread thread2 = new Thread(threadFour);
		thread2.start();
	}
}

class ThreadThree implements Runnable {

	private MyService2 myService;

	public ThreadThree(MyService2 myService) {
		this.myService = myService;
	}

	@Override
	public void run() {
		try {
			myService.update();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}

class ThreadFour implements Runnable {

	private MyService2 myService;

	public ThreadFour(MyService2 myService) {
		this.myService = myService;
	}

	@Override
	public void run() {
		try {
			myService.update();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}

class MyService2 {

	private Integer id = 0;

	public void updateA() throws InterruptedException {
		synchronized (id) {
			for (int i = 0; i < 5; i++) {
				System.out.println(Thread.currentThread().getName() + " " + id++);
				Thread.sleep(50);
			}
		}
	}

	public void updateB() throws InterruptedException {
		synchronized (id) {
			for (int i = 0; i < 5; i++) {
				System.out.println(Thread.currentThread().getName() + " " + id++);
				Thread.sleep(100);
			}
		}
	}

	public synchronized void update() throws InterruptedException {
		updateA();
		updateB();
	}

}

输出:

Thread-0 0
Thread-0 1
Thread-0 2
Thread-0 3
Thread-0 4
Thread-0 5
Thread-0 6
Thread-0 7
Thread-0 8
Thread-0 9
Thread-1 10
Thread-1 11
Thread-1 12
Thread-1 13
Thread-1 14
Thread-1 15
Thread-1 16
Thread-1 17
Thread-1 18
Thread-1 19

我们把updateA和updateB组合成为一个方法, 然后做方法级的同步,或者将两个方法组合成方法块的同步,这样,输出的结果就可以是同步的,不会出现脏读

(2)第二种思路

package com.ray.deepintothread.ch02.topic_15;

/**
 *
 * @author RayLee
 *
 */
public class SolutionOfDirtyRead2 {
	public static void main(String[] args) throws InterruptedException {
		MyService myService = new MyService();
		ThreadOne threadOne = new ThreadOne(myService);
		Thread thread = new Thread(threadOne);
		thread.start();
		ThreadTwo threadTwo = new ThreadTwo(myService);
		Thread thread2 = new Thread(threadTwo);
		thread2.start();
	}
}

class ThreadOne implements Runnable {

	private MyService myService;

	public ThreadOne(MyService myService) {
		this.myService = myService;
	}

	@Override
	public void run() {
		try {
			myService.updateA();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}

class ThreadTwo implements Runnable {

	private MyService myService;

	public ThreadTwo(MyService myService) {
		this.myService = myService;
	}

	@Override
	public void run() {
		try {
			myService.updateB();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}

class MyService {

	private Integer id1 = 0;

	private Integer id2 = 0;

	public void updateA() throws InterruptedException {
		synchronized (this) {
			for (int i = 0; i < 5; i++) {
				System.out.println(Thread.currentThread().getName() + " id1:" + id1++);
				Thread.sleep(50);
			}
		}
	}

	public void updateB() throws InterruptedException {
		synchronized (this) {
			for (int i = 0; i < 5; i++) {
				System.out.println(Thread.currentThread().getName() + " id2:" + id2++);
				Thread.sleep(100);
			}
		}
	}

}

输出:

Thread-0 id1:0
Thread-0 id1:1
Thread-0 id1:2
Thread-0 id1:3
Thread-0 id1:4
Thread-1 id2:0
Thread-1 id2:1
Thread-1 id2:2
Thread-1 id2:3
Thread-1 id2:4

虽然输出的结果是同步的,但是也是上面所说的,一个属性域(一般是某个数的运算或者某种状态的运算)是很难分开来的。当然,如果可以分开来,这是最优解,因为既保证了同步,也保证了数据的强一致性。

总结:这一章节主要讨论了解决由同步的synchronized (newobject()) 引起的脏读的方法。

这一章节就到这里,谢谢

------------------------------------------------------------------------------------

我的github:https://github.com/raylee2015/DeepIntoThread

目录:http://blog.csdn.net/raylee2007/article/details/51204573

时间: 2024-12-22 23:29:54

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

从头认识多线程-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.17 同步方法与同步静态代码块持有的是不同的锁

这一章节我们来讨论一下同步方法与同步静态代码块持有的是不同的锁. 代码清单: package com.ray.deepintothread.ch02.topic_18; /** * * @author RayLee * */ public class SynchClass { public static void main(String[] args) throws InterruptedException { MyService myService = new MyService(); Thr

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

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

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

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

多线程总结四:线程同步(一)

1.线程安全问题 a.银行取钱问题:取钱时银行系统判断账户余额是否大于取款金额,如果是,吐出钞票,修改余额.这个流程在多线程并发的场景下就可能会出现问题. 1 /** 2 * @Title: Account.java 3 * @Package 4 * @author 任伟 5 * @date 2014-12-8 下午5:35:27 6 * @version V1.0 7 */ 8 9 /** 10 * @ClassName: Account 11 * @Description: 账户类 12 *

Java基础-多线程-③多线程的同步之synchronized

使用线程同步解决多线程安全问题 上一篇 Java基础-多线程-②多线程的安全问题 中我们说到多线程可能引发的安全问题,原因在于多个线程共享了数据,且一个线程在操作(多为写操作)数据的过程中,另一个线程也对数据进行了操作,从而导致数据出错.由此我们想到一个解决的思路:将操作共享数据的代码行作为一个整体,同一时间只允许一个线程执行,执行过程中其他线程不能参与执行.线程同步就是用来实现这样的机制. synchronized代码块 Java中提供了synchronized关键字,将可能引发安全问题的代码

Java多线程学习之线程的同步

多线程编程要解决的一个基本问题是:共享资源的竞争.而基本上使用并发模式在解决这个问题都采用序列化访问共享资源的方法.基本原理就是当共享资源被一个任务使用时,在其上加锁,其他任务在资源被解锁之前,无法访问它.在任务对其解锁后,另一个任务就可以锁定并使用它.下面看看Java支持的线程同步机制. 1.synchronized关键字 synchronized关键字即可应用于对象相关的同步,也可用于类层次的同步(static属性): 对象上应用synchronized可以实现对象方法的同步和代码块的同步.

多线程下的进程同步(线程同步问题总结篇)

之前写过两篇关于线程同步问题的文章(一,二),这篇中将对相关话题进行总结,本文中也对.NET 4.0中新增的一些同步机制进行了介绍. 首先需要说明的是为什么需要线程功能同步.MSDN中有这样一段话很好的解释了这个问题: 当多个线程可以调用单个对象的属性和方法时,对这些调用进行同步处理是非常重要的.否则,一个线程可能会中断另一个线程正在执行的任务,使该对象处于一种无效状态. 也就说在默认无同步的情况下,任何线程都可以随时访问任何方法或字段,但一次只能有一个线程访问这些对象.另外,MSDN中也给出定

Java多线程的同步机制(synchronized)

一段synchronized的代码被一个线程执行之前,他要先拿到执行这段代码的权限,在 java里边就是拿到某个同步对象的锁(一个对象只有一把锁): 如果这个时候同步对象的锁被其他线程拿走了,他(这个线程)就只能等了(线程阻塞在锁池 等待队列中). 取到锁后,他就开始执行同步代码(被synchronized修饰的代码):线程执行完同步代码后马上就把锁还给同步对象,其他在锁池中 等待的某个线程就可以拿到锁执行同步代码了.这样就保证了同步代码在统一时刻只有一个线程在执行. 众所周知,在Java多线程