volatile
我們在代碼中開啓一個線程之後,想要依據某些條件去控制線程是否結束的時候,可以通過定義一個 volatile 關鍵字修飾的變量去進行操作。示例代碼如下:
public class RunThread extends Thread {
private volatile boolean isRunning;
public void setRunning(boolean running) {
isRunning = running;
}
@Override
public void run() {
System.out.println("進入線程。。。");
while (isRunning) {
// do something
}
System.out.println("線程結束");
}
public static void main(String[] arg0) throws InterruptedException {
RunThread t1 = new RunThread();
t1.start();
Thread.sleep(3000);
t1.setRunning(false);
Thread.sleep(1000);
System.out.println("t1 isRunnung : " + t1.isRunning);
}
}
打印結果:
結果正常,沒毛病。
那麼我們多個線程調用呢?是否還能達到預期效果?我們來試一下:
public class RunThread1 extends Thread {
private static volatile int count;
private static void addCount() {
for (int i = 0; i < 1000; i++) {
count++;
}
System.out.println(count);
}
@Override
public void run() {
addCount();
}
public static void main(String[] arg0) {
RunThread1[] ts = new RunThread1[10];
for (int i = 0; i < 10; i++) {
ts[i] = new RunThread1();
}
for (int i = 0; i < 10; i++) {
ts[i].start();
}
}
}
運行結果:
正常來說,我們最終的結果肯定要是 10000 纔是正常的,但是這個輸出的結果,明顯與我們的預期不相符。
驗證結果:volatile 線程間可見性,非原子性,屬於線程不安全。
Atomic
那麼如果我們上面的多線程調用情況我們要怎麼辦呢? 這裏提供一種寫法:
public class RunThread2 extends Thread {
private static AtomicInteger count = new AtomicInteger();
private static void addCount() {
for (int i = 0; i < 1000; i++) {
count.getAndIncrement();
}
System.out.println(count);
}
@Override
public void run() {
addCount();
}
public static void main(String[] arg0) {
RunThread2[] ts = new RunThread2[10];
for (int i = 0; i < 10; i++) {
ts[i] = new RunThread2();
}
for (int i = 0; i < 10; i++) {
ts[i].start();
}
}
}
打印結果:
最終輸出結果,是不是有 10000 了, 有時候編譯器有些延遲,但是不影響我們每次的打印結果都有 10000 。
這裏簡略的介紹一下 Atomic 包:Java從JDK1.5開始提供了java.util.concurrent.atomic包,方便程序員在多線程環境下,無鎖的進行原子操作。原子變量的底層使用了處理器提供的原子指令,但是不同的CPU架構可能提供的原子指令不一樣,也有可能需要某種形式的內部鎖,所以該方法不能絕對保證線程不被阻塞。
當然 Atomic 包中還有其他原子更新方式,比如 AtomicBoolean , AtomicLong , AtomicIntegerArray , AtomicLongArray , AtomicReferenceArray.. 等等。有需要的還請各位自行 Google.
學習筆記整理。