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

Posted 李灵晖

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了从头认识多线程-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



以上是关于从头认识多线程-3.1 使用volatile解决异步死循环的主要内容,如果未能解决你的问题,请参考以下文章

多线程---再次认识volatile,Synchronize,lock

对volatile的认识

从头认识多线程-1.8 迫使线程停止的方法-暴力Stop方法

Java中,对多线程访问同一变量(并发访问)的认识

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

从头认识多线程-1.9 迫使线程停止的方法-return法