从头认识多线程-3.1 使用volatile解决异步死循环

这一章节我们来讨论一下使用volatile解决异步死循环。

1.在讨论上面的问题前,我们引入另一个例子:同步死循环

代码清单:

package com.ray.deepintothread.ch03.topic_1;

public class DeadFor {
	private boolean isStop = false;

	public boolean isStop() {
		return isStop;
	}

	public void setStop(boolean isStop) {
		this.isStop = isStop;
	}

	public void test() throws InterruptedException {
		while (!isStop) {
			System.out.println("Thread name:" + Thread.currentThread().getName());
			Thread.sleep(200);
		}
	}

	public static void main(String[] args) throws InterruptedException {
		DeadFor deadFor = new DeadFor();
		deadFor.test();
		deadFor.setStop(true);
	}
}

输出:

Thread name:main
Thread name:main
Thread name:main
Thread name:main
Thread name:main
Thread name:main
Thread name:main
Thread name:main
Thread name:main
Thread name:main
Thread name:main
Thread name:main

。。。。

上面的例子里面,test方法已经阻塞了main后面所有方法的执行。

2.解决同步死循环的方法:使用异步

package com.ray.deepintothread.ch03.topic_1;

public class SolveDeadFor implements Runnable {
	private boolean isStop = false;

	public boolean isStop() {
		return isStop;
	}

	public void setStop(boolean isStop) {
		this.isStop = isStop;
	}

	public void test() throws InterruptedException {
		while (!isStop) {
			System.out.println("Thread name:" + Thread.currentThread().getName());
			Thread.sleep(200);
		}
	}

	public static void main(String[] args) throws InterruptedException {
		SolveDeadFor deadFor = new SolveDeadFor();
		Thread thread = new Thread(deadFor);
		thread.start();

		Thread.sleep(1000);
		deadFor.setStop(true);
		System.out.println("-------stop--------");
	}

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

输出:

Thread name:Thread-0
Thread name:Thread-0
Thread name:Thread-0
Thread name:Thread-0
Thread name:Thread-0
-------stop--------

上面的方法我们需要注意的是:test方法我们是单独启动一个线程来运行的,不再是放在main里面运行

因此,test方法与main里面执行的方法是并行的两条线,永远不会发生交集,就会避免了上面的同步的时候,test方法执行后,阻塞下面所有方法的执行

当在等待时间过后,我们触发一下对象里面的状态域,使得test方法停下来

3.异步死循环

代码清单:

package com.ray.deepintothread.ch03.topic_1;

public class SolveDeadFor {

	public static void main(String[] args) throws InterruptedException {
		MyClassOne myClassOne = new MyClassOne();
		myClassOne.start();
		Thread.sleep(1000);
		myClassOne.setStop(true);
		System.out.println("---------stop----------");
	}
}

class MyClassOne extends Thread {
	private boolean isStop = false;

	public boolean isStop() {
		return isStop;
	}

	public void setStop(boolean isStop) {
		this.isStop = isStop;
	}

	@Override
	public void run() {
		System.out.println("running");
		while (!isStop) {
		}
		System.out.println("out");
	}
}

上面的代码注意:在这里run里面的循环必须是空循环才能够出现死循环

笔者估计,isStop经过编译器的编译,读取的是局部变量,因此,即便是外部修改了域,里面读取的地方不是我们想要的地方

jvm设置:

注意:这里必须要jvm是-server状态下才能够出现异步死循环。

输出:

running
---------stop----------

(不会出现out)

我们通过jvm的内存模型来解释上面的现象

注:图片摘自java多线程编程核心技术

由上面的内存模型可以看见,当我们线程起来之后,我们修改了线程私有的工作内存,还会把某些值写入到主内存里面去

然后,当我们修改这些工作内存的时候,一般会同步到主内存去

但是,我们上面的代码只是修改了工作内存,而没有同步到主内存,因此,当我们外部修改工作内存的时候,run里面读取的isStop是主内存里面的,没有被修改,会出现异步死循环。

我们下面展示一下不会出现异步死循环的代码,只需要在循环里面加一两句代码即可

package com.ray.deepintothread.ch03.topic_1;

public class DeadForSynch2 {

	public static void main(String[] args) throws InterruptedException {
		MyClassTwo myClassOne = new MyClassTwo();
		myClassOne.start();
		Thread.sleep(1000);
		myClassOne.setStop(true);
		System.out.println("---------stop----------");
	}
}

class MyClassTwo extends Thread {
	private boolean isStop = false;

	public boolean isStop() {
		return isStop;
	}

	public void setStop(boolean isStop) {
		this.isStop = isStop;
	}

	@Override
	public void run() {
		System.out.println("running");
		while (!isStop) {
			System.out.println(Thread.currentThread().getName());
			try {
				sleep(500);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		System.out.println("out");
	}
}

输出:

running
Thread-0
Thread-0
Thread-0
---------stop----------
out

4.异步死循环的解决方案

代码清单:

package com.ray.deepintothread.ch03.topic_1;

public class SolutionOfDeadForAsychn {

	public static void main(String[] args) throws InterruptedException {
		MyClassThree myClassThree = new MyClassThree();
		myClassThree.start();
		Thread.sleep(1000);
		myClassThree.setStop(true);
		System.out.println("---------stop----------");
	}
}

class MyClassThree extends Thread {
	private volatile boolean isStop = false;

	public boolean isStop() {
		return isStop;
	}

	public void setStop(boolean isStop) {
		this.isStop = isStop;
	}

	@Override
	public void run() {
		System.out.println("running");
		while (!isStop) {
		}
		System.out.println("out");
	}
}

输出:

running
out
---------stop----------

我们上面使用volatile来把isStop强制同步到主内存去,使得其他线程能够可见。

总结:这一章节讨论了使用volatile解决异步死循环。

这一章节就到这里,谢谢

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

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

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

时间: 2024-10-09 01:18:27

从头认识多线程-3.1 使用volatile解决异步死循环的相关文章

从头认识多线程-3.3 synchronized某些解决不了的可视性问题,只能使用volatile来解决

这一章节我们讨论一下synchronized某些解决不了的可视性问题. 1.引入之前的异步死循环代码: package com.ray.deepintothread.ch03.topic_3; public class DeadForAsychn { public static void main(String[] args) throws InterruptedException { MyClassOne myClassOne = new MyClassOne(); myClassOne.st

2.3.3解决异步死循环

测试 package com.cky.test; public class RunThread extends Thread{ private boolean isRunning = true; public boolean isRunning() { return isRunning; } public void setRunning(boolean isRunning) { this.isRunning = isRunning; } @Override public void run() {

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

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

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

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

Android多线程分析之一:使用Thread异步下载图像

罗朝辉 (http://blog.csdn.net/kesalin) CC 许可,转载请注明出处 打算整理一下对 Android Framework 中多线程相关知识的理解,主要集中在 Framework 层的 Thread, Handler, Looper, MessageQueue, Message, AysncTask,当然不可避免地要涉及到 native 方法,因此也会分析 dalvik 中和线程以及消息处理相关的代码:如 dalvik 中的 C++ Thread 类以及 Message

Android多线程分析之五:使用AsyncTask异步下载图像

Android多线程分析之五:使用AsyncTask异步下载图像 罗朝辉 (http://blog.csdn.net/kesalin) CC 许可,转载请注明出处 在本系列文章的第一篇<Android多线程分析之中的一个:使用Thread异步下载图像>中.曾演示了怎样使用 Thread 来完毕异步任务. Android 为了简化在 UI 线程中完毕异步任务(毕竟 UI 线程是 app 最重要的线程).实现了一个名为 AysncTask 的模板类.使用 AysncTask 能够在异步任务进行的同

jquery.Deferred promise解决异步回调

我们先来看一下编写AJAX编码经常遇到的几个问题: 1.由于AJAX是异步的,所有依赖AJAX返回结果的代码必需写在AJAX回调函数中.这就不可避免地形成了嵌套,ajax等异步操作越多,嵌套层次就会越深,代码可读性就会越差. $.ajax({ url: url, data: dataObject, success: function(){ console.log("I depend on ajax result."); }, error: function(){} }); consol

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

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

Java多线程(三)volatile域

相关文章 Java多线程(一)线程定义.状态和属性 Java多线程(二)同步 Android多线程(一)线程池 Android多线程(二)AsyncTask源码分析 前言 有时仅仅为了读写一个或者两个实例域就使用同步的话,显得开销过大,volatile关键字为实例域的同步访问提供了免锁的机制.如果声明一个域为volatile,那么编译器和虚拟机就知道该域是可能被另一个线程并发更新的.再讲到volatile关键字之前我们需要了解一下内存模型的相关概念以及并发编程中的三个特性:原子性,可见性和有序性