volatile 的可见性解读,synchronized 代码块有volatile 同步的功能

关键字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();
		}
	}
}
已经发起停止的命令了!
停下来了!

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值