关键字volatile 主要的功能是使变量在多个线程间可见
1 volatile 的作用就是强制从公共堆栈中取得变量的值,而不是从私有数据栈中取得变量的值
1 多线程的方式 -------- 解决同步死循环
下面的例子是产生死循环的代码
package printstring;
public class PrintString {
private boolean isContinuePrint = true;
public boolean isContinuePrint() {
return isContinuePrint;
}
public void setContinuePrint(boolean isContinuePrint) {
this.isContinuePrint = isContinuePrint;
}
public void printStringMethod() {
try {
while (isContinuePrint == true) {
System.out.println("run printStringMethod threadName="
+ Thread.currentThread().getName());
Thread.sleep(1000);
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
package test;
import printstring.PrintString;
public class Run {
public static void main(String[] args) {
PrintString printStringService = new PrintString();
printStringService.printStringMethod();
System.out.println("我要停止它!stopThread="
+ Thread.currentThread().getName());
printStringService.setContinuePrint(false);
}
}
1 多线程的方式 -------- 解决同步死循环代码
package printstring;
public class PrintString implements Runnable {
private boolean isContinuePrint = true;
public boolean isContinuePrint() {
return isContinuePrint;
}
public void setContinuePrint(boolean isContinuePrint) {
this.isContinuePrint = isContinuePrint;
}
public void printStringMethod() {
try {
while (isContinuePrint == true) {
System.out.println("run printStringMethod threadName="
+ Thread.currentThread().getName());
Thread.sleep(1000);
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
@Override
public void run() {
printStringMethod();
}
}
package test;
import extthread.RunThread;
public class Run {
public static void main(String[] args) {
try {
RunThread thread1 = new RunThread();
Thread thread =new Thread(thread1);
thread.start();
/**
* 线程被不被停止起决定性作用的是Thread.sleep(1000);
* 如果不设置时间的话,由于主线程比分线程运行的快,就一定能够停止,如果设置时间为3000 的话,由于主线程睡了3s ,
* 所以线程就无法停止了,还有一种情况就是如果线程设置了3s 但是while 循环有循环体的话:例如
* public void printStringMethod() {
while (isContinuePrint == true) {
System.out.println("run printStringMethod threadName="
+ Thread.currentThread().getName());
}
}
线程这种情况下也是可以被停止的,但是如果没有循环体的话
public void printStringMethod() {
while (isContinuePrint == true) {
}
}
线程依然是不能停止的
}
*/
Thread.sleep(3000);
thread1.setRunning(false);
System.out.println("已经赋值为false");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
我要停止它!stopThread=main
run printStringMethod threadName=Thread-0
像这种情况线程是不能停止的
同样 ,如果当上面的代码运行在-server 服务模式中64bit 的JVM 上时,会出现死循环,解决方法就是volatile
关键字volatile 的作用就是强制从公共堆栈中取得变量的值,而不是从私有数据栈中取得变量的值
是什么原因造成将JVM 设置为-server 时就出现死循环的呢,在启动RunThread.java 线程时,变量 private boolean isRunning = true; 存在于公共对战及线程私有堆栈中,在JVM 被设置为-server 模式时为了线程运行的效率,线程一直在私有堆栈中取得isRunning 的值是trrue,而代码thread.setRunning(false) 虽然被执行,更新的却是公共堆栈的isRunning 变量值false 所以一直就是死循环的状态,
2 加入volatile 的目的就是使变量在多个线程间可见类似于final(底层实现原理)
正常情况下,如果不加volatile 的话,例如在主内存中存在一个共享变量x 的值为0 如果线程1 想用这个变量x 那么会在工作内存1 中存在一个副本,把副本的值从0 改为10 ,如果线程2 想用这个变量x 也是用的是工作内存2 当中的副本从0 变为20 两个线程没有任何的关系在没有加volatile 之前,但是在线程1 中的变量x 的变化是没有办法让线程2 知道的但是volatile 就是做的这个,只要是加了这个关键字的话,就可以变量的变化在多个线程间是可见的
原来传统的做法就是,在这个变量上加一把锁或者在方法上加一把锁保证数据的一致性,不管哪个一个线程来都是尝试获取这个锁,只要是能拿到锁了
源码就是
package extthread;
public class MyThread extends Thread {
public static int count;
synchronized private static void addCount() {
for (int i = 0; i < 100; i++) {
count++;
}
System.out.println("count=" + count);
}
@Override
public void run() {
addCount();
}
}
但是这种方式效率是不高的,因为同一个时间只能有一个线程进来
线程无法停止的问题,只能是用volatile 就可以解决
package printstring;
public class PrintString implements Runnable {
volatile private boolean isContinuePrint = true;
public boolean isContinuePrint() {
return isContinuePrint;
}
public void setContinuePrint(boolean isContinuePrint) {
this.isContinuePrint = isContinuePrint;
}
public void printStringMethod() {
while (isContinuePrint == true) {
}
}
@Override
public void run() {
printStringMethod();
}
}
package test;
import printstring.PrintString;
public class Run {
public static void main(String[] args) throws InterruptedException {
PrintString printStringService = new PrintString();
new Thread(printStringService).start();
Thread.sleep(3000);
/**
* 线程被不被停止起决定性作用的是Thread.sleep(1000);
* 如果不设置时间的话,由于主线程比分线程运行的快,就一定能够停止,如果设置时间为3000 的话,由于主线程睡了3s ,
* 所以线程就无法停止了,还有一种情况就是如果线程设置了3s 但是while 循环有循环体的话:例如
* @Override
public void run() {
System.out.println("进入run了");
while (isRunning == true) {
System.out.println("run printStringMethod threadName="
+ Thread.currentThread().getName());
}
System.out.println("线程被停止了!");
线程这种情况下也是可以被停止的,但是如果没有循环体的话
@Override
public void run() {
System.out.println("进入run了");
while (isRunning == true) {
}
线程依然是不能停止的
}
*/
Thread.sleep(3000);
System.out.println("我要停止它!stopThread="
+ Thread.currentThread().getName());
printStringService.setContinuePrint(false);
}
}
jdk 中就是每一个线程都有自己独立的内存空间就是工作内存,在jdk 1.5 以后,不单单是有自己的独立的工作内存还有一块工作内存,主要就是用来放从主内存copy过来的副本,这样做的目的就是增加效率
证明volatile 不具备原子性,同步性
package extthread;
public class MyThread extends Thread {
volatile private static int count;
private static void addCount() {
for (int i = 0; i < 1000; i++) {
count++;
}
System.out.println("count=" + count);
}
@Override
public void run() {
addCount();
}
}
package test.run;
import extthread.MyThread;
public class Run {
public static void main(String[] args) {
MyThread[] mythreadArray = new MyThread[10];
for (int i = 0; i < 10; i++) {
mythreadArray[i] = new MyThread();
}
for (int i = 0; i < 10; i++) {
mythreadArray[i].start();
}
}
}
一共是10 个线程,每一个线程对这个count 变量加了1000次,一共应该是10000,但是结果不是这样的,虽然,这个变量用了volatile 修饰
count=2000
count=2000
count=3265
count=4215
count=4956
count=6288
count=6707
count=7299
count=8044
count=9044
但是结果不是10000证明volatile 不具备原子性
package extthread;
public class MyThread extends Thread {
private static int count;
synchronized private static void addCount() {
for (int i = 0; i < 1000; i++) {
count++;
}
System.out.println("count=" + count);
}
@Override
public void run() {
addCount();
}
}
count=1000
count=2000
count=3000
count=4000
count=5000
count=6000
count=7000
count=8000
count=9000
count=10000
也可以采用这种但是线程是不安全的
package extthread;
import java.util.concurrent.atomic.AtomicInteger;
public class MyThread extends Thread {
private static AtomicInteger count = new AtomicInteger();
synchronized private static void addCount() {
for (int i = 0; i < 1000; i++) {
count.incrementAndGet();
}
System.out.println("count=" + count);
}
@Override
public void run() {
addCount();
}
}
虽然不具备原子性,但是性能要比synchronized 要多,不会造成阻塞,但是在很多开源的框架里面,比如netty的底层代码就大量使用volatile ,可见netty性能一定是不错的,这里需要注意的是,volatile 用于只针对多个线程可见的变量操作,并不能代替synchronized 的同步功能。
synchronized 代码块有volatile 同步的功能
package extthread;
import service.Service;
public class ThreadA extends Thread {
private Service service;
public ThreadA(Service service) {
super();
this.service = service;
}
@Override
public void run() {
service.runMethod();
}
}
package extthread;
import service.Service;
public class ThreadB extends Thread {
private Service service;
public ThreadB(Service service) {
super();
this.service = service;
}
@Override
public void run() {
service.stopMethod();
}
}
package service;
public class Service {
private boolean isContinueRun = true;
public void runMethod() {
String anyString = new String();
while (isContinueRun == true) {
synchronized (anyString) {
}
}
System.out.println("停下来了!");
}
public void stopMethod() {
isContinueRun = false;
}
}
package test;
import service.Service;
import extthread.ThreadA;
import extthread.ThreadB;
public class Run {
public static void main(String[] args) {
try {
Service service = new Service();
ThreadA a = new ThreadA(service);
a.start();
Thread.sleep(1000);
ThreadB b = new ThreadB(service);
b.start();
System.out.println("已经发起停止的命令了!");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
已经发起停止的命令了!
停下来了!