volatile:當多個線程進行操作共享數據時,可以保證內存中的數據是可見的;
@Slf4j
public class MyVolatile {
public static void main(String[] args) {
ThreadRun threadRun = new ThreadRun();
new Thread(threadRun).start();
log.info("begin----flag:{}",threadRun.flag);
while (true){
if (threadRun.flag){
log.info("main ---flag:{}",threadRun.flag);
break;
}
}
}
static class ThreadRun implements Runnable{
private boolean flag = false;
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
flag = true;
log.info("threadRun----flag:{}",flag);
}
public boolean isFlag() {
return flag;
}
public void setFlag(boolean flag) {
this.flag = flag;
}
}
}
運行結果如下圖所示,程序一種在運行,無法結束。
代碼分析:主線程main運行之後,開啓了另外一個線程ThreadRun,ThreadRun線程睡眠1000毫秒之後,將flag標記變爲true。但是對於主線程來說,flag標記一直是false,ThreadRun改變後的flag的值對於主線程來說是不可見的,因此主線程一直在while循環中。
接下來再看使用了volatile關鍵字之後:
static class ThreadRun implements Runnable{
private volatile boolean flag = false;
//其他都一樣
}
代碼運行結果發生了改變。
volatile關鍵字比較適用於上述這種情況。但是對於計數這種情況來說,並不適用
public class VolatileTest {
//請求總數
private static int clientTotal = 5000;
//同時允許執行的線程總數
private static int threadTotal = 200;
private static volatile int count = 0;
public static void main(String[] args) throws InterruptedException {
final Semaphore semaphore = new Semaphore(threadTotal);
final CountDownLatch countDownLatch = new CountDownLatch(clientTotal);
ExecutorService executorService = Executors.newCachedThreadPool();
for (int i=0;i<clientTotal;i++){
executorService.execute(new Runnable() {
@Override
public void run() {
try {
semaphore.acquire();
count++;
semaphore.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
countDownLatch.countDown();
}
});
}
countDownLatch.await();
executorService.shutdown();
System.out.println("count="+count);
}
}
雖然count使用了volatile修飾,但程序運行的結果並不是我們預想的5000。具體原因可參考另一篇博文線程安全性--atomic中的內容進行理解。 本文只是覺得第一個例子很好的說明了volatile的使用場景。