180921 -------------------------------------volatile关键字------------------------------------------------
volatile关键字:主要作用是使变量在多个线程间可见;
java中,没一个线程都会有一块工作内存区,其中存放着所有线程共享的主内存中变量值的拷贝。当线程执行时,线程在自己的这块内存区中操作这些变量。为了存取这些共享变量,一个线程通常先获取锁定并去清除他的内存工作区,把这些共享变量从所有线程的共享内存区中正确地装入到他自己所在的工作内存区中,当线程解锁时保证该工作内存区中变量的值回写回共享内存中。
一个线程可以执行的操作有使用(use)、赋值(assign)、装载(load)、存储(store)、锁定(lock)、解锁(unlock)。而主内存可以执行的操作有读(read)、写(write)、锁定(lock)、解锁(unlock),每个操作都是原子的。
volatile关键字的作用就是强制线程到主内存(共享内存)里边去读取变量而不是去线程的工作内存区中取读取,从而实现了多个线程间的变量可见,有就是满足线程安全的可见性。
示例代码:
//private volatile boolean isRunning = true;
private boolean isRunning = true;
public void setRuning(boolean isRunning) {
this.isRunning = isRunning;
}
public void run() {
System.err.println("进入run方法...");
while(isRunning == true) {
//System.err.println("run一直在奔跑...\t.");
}
System.err.println("isRunning = "+isRunning);
System.err.println("run终于停止了奔跑");
}
main测试:
public static void main(String[] args) throws InterruptedException {
MyObject myObj = new MyObject();
myObj.start();
Thread.sleep(2*1000);
myObj.setRuning(false);
System.err.println("总部叫你停下来");
Thread.sleep(1*1000);
System.err.println(myObj.isRunning);
}
测试结果:
//当不加volatile关键字时,main线程里边对isRunning的修改不会影响到t1线程里边isRunning的值
进入run方法...
总部叫你停下来
false
//此时main线程停止了,但t1线程依旧在继续运行
//当加了volatile关键字以后,t1线程里边isRunning的值将会受到main线程的影响。
进入run方法...
总部叫你停下来
isRunning = false
run终于停止了奔跑
false
---------------------------------------------------------------------------------------------------------------------------180921 end
180922 ----------------------------------------------volatile关键字的非原子性-------------------------------------------------
volatile关键字的非原子性:volatile关键字拥有多个线程之间的可见性,却不具备原子性(即其操作可拆分),可以算得上是一个轻量级的synchronized,性能比synchronized关键字强,不会造成阻塞。针对volatile关键字的特性,使volatile关键字一般只用于针对多个线程可见的变量操作而不是替代synchronized的同步功能。若要强调原子性则atomic类的系列对象将是更好地选择对象(atomic类只保证操作本身的原子性,并不保证多次操作的原子性)。
以代码示例:
public final class VolatileNoAtomic extends Thread{
private static volatile int count_NoAtomic = 0;
private static AtomicInteger count_Atomic = new AtomicInteger(0);
private static void runTest() {
for(int index=0;index<1000;index++) {
{
//count_NoAtomic ++;
count_Atomic.incrementAndGet(); //等价于++
}或者
{
count_NoAtomic ++;
//count_Atomic.incrementAndGet(); //等价于++
}
}
System.err.println("count_NoAtomic:
"+count_NoAtomic+"\tcount_Atomic:"+count_Atomic);
}
public void run() {runTest();}
public static void main(String[] args) {
int len = 10;
VolatileNoAtomic[] arr = new VolatileNoAtomic[len];
for(int i = 0;i<len;i++)
arr[i] = new VolatileNoAtomic();
for(VolatileNoAtomic v : arr) {
v.start();
}
}
}
上面分别定义了变量:count_NoAtomic(volatile int)、count_Atomic(AtomicInteger)来对volatile的原子性进行比较说明, 在主方法里定义10个线程,同时对上面两个变量的值进行 ‘++‘ 操作,每一个线程在完成对两个变量的1000次 ‘++’ 操作后都会打印两个变量的值,由于volatile关键字不具备原子性,变量count_NoAtomic最后输出的值不一定是10000,而变量count_Atomic最后输出的值则一定是10000。
测试结果:
//测试一
count_NoAtomic: 0 count_Atomic:2738
count_NoAtomic: 0 count_Atomic:3000
count_NoAtomic: 0 count_Atomic:4000
count_NoAtomic: 0 count_Atomic:5835
count_NoAtomic: 0 count_Atomic:2662
count_NoAtomic: 0 count_Atomic:6000
count_NoAtomic: 0 count_Atomic:7000
count_NoAtomic: 0 count_Atomic:8581
count_NoAtomic: 0 count_Atomic:9000
count_NoAtomic: 0 count_Atomic:10000
//测试二
count_NoAtomic: 1346 count_Atomic:0
count_NoAtomic: 6000 count_Atomic:0
count_NoAtomic: 7421 count_Atomic:0
count_NoAtomic: 8385 count_Atomic:0
count_NoAtomic: 2000 count_Atomic:0
count_NoAtomic: 5000 count_Atomic:0
count_NoAtomic: 4000 count_Atomic:0
count_NoAtomic: 3773 count_Atomic:0
count_NoAtomic: 7505 count_Atomic:0
count_NoAtomic: 9385 count_Atomic:0
---------------------------------------------------------------------------------------------------------------------------------------------------------volatile end