博客概述
本博客介绍可见性关键字volatile的相关技术细节。它的主要作用是在多个线程间可见。以及volatile,sync,ato关键字之间的联系。
案例与解析
RunThread
在java中,每一个线程都有自己的一块内存工作区,其中存放着所有线程共享的主内存的变量值的拷贝。当线程执行时,他在自己的工作内存中操作这些变量。为了存取一个共享的变量,一个线程通常先获取锁定并去清除他的内存工作区,把这些共享变量从所有的共享内存区中正确的装入到他自己所在的工作内存区中,当线程解锁时保证该工作内存中的变量的值写回到共享内存中。这个例子就会看到对线程的控制效果。
public class RunThread extends Thread{
private volatile boolean isRunning = true;
private void setRunning(boolean isRunning){
this.isRunning = isRunning;
}
public void run(){
System.out.println("进入run方法..");
int i = 0;
while(isRunning == true){
//..
}
System.out.println("线程停止");
}
public static void main(String[] args) throws InterruptedException {
RunThread rt = new RunThread();
rt.start();
Thread.sleep(1000);
rt.setRunning(false);
System.out.println("isRunning的值已经被设置了false");
}
}
早期时候的实现:在a上加一把锁(setter方法,变量),取的时候,都先获得锁,而不是自己内存空间的副本。就保证了一致性。也就是在变量上或者方法上加锁。但是效率低,同一时间只有一个线程在操作。
volatile的非原子性
并发的情况下volatile只能保证访问是同一个对象,是一起操作这个对象,但是操作没有原子性,在并发场景下不能保证最后的结果是我们想要的。原子性:一个线程在操作时,另一个线程不能在这个操作的过程中进入,这就是原子性。
import java.util.concurrent.atomic.AtomicInteger;
/**
* volatile关键字不具备synchronized关键字的原子性(同步)
* @author alienware
*
*/
public class VolatileNoAtomic extends Thread{
//private static volatile int count;
private static AtomicInteger count = new AtomicInteger(0);
private static void addCount(){
for (int i = 0; i < 1000; i++) {
//count++ ;
count.incrementAndGet();
}
System.out.println(count);
}
public void run(){
addCount();
}
public static void main(String[] args) {
VolatileNoAtomic[] arr = new VolatileNoAtomic[100];
for (int i = 0; i < 10; i++) {
arr[i] = new VolatileNoAtomic();
}
for (int i = 0; i < 10; i++) {
arr[i].start();
}
}
}
可以使用原子类变量配合静态关键字和解决这个问题。一个线程在增加或者减少的时候,其他线程不能插一脚,保证了最后结果是10000。volatile虽然拥有多个线程之间的可见性,但是却不具备同步性(也就是原子性),可以算得上是一个轻量级的sync关键字,性能要比sync强,不会造成阻塞,需要注意的是,volitale只用于多个线程之间的可见性,不能代替sync的同步功能。
原子类的使用
volatile关键字只具有可见性,没有原子性,要实现原子性建议使用atomic类的关键字,支持原子性操作,要实现多线程可见的原子性要加static关键字。要实现多次操作的业务原子性,可以使用sync代码块。
package com.bjsxt.base.sync007;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
public class AtomicUse {
private static AtomicInteger count = new AtomicInteger(0);
//多个addAndGet在一个方法内是非原子性的,需要加synchronized进行修饰,保证4个addAndGet整体原子性
/**synchronized*/
public synchronized int multiAdd(){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
count.addAndGet(1);
count.addAndGet(2);
count.addAndGet(3);
count.addAndGet(4); //+10
return count.get();
}
public static void main(String[] args) {
final AtomicUse au = new AtomicUse();
List<Thread> ts = new ArrayList<Thread>();
for (int i = 0; i < 100; i++) {
ts.add(new Thread(new Runnable() {
@Override
public void run() {
System.out.println(au.multiAdd());
}
}));
}
for(Thread t : ts){
t.start();
}
}
}