talk is cheap,show me the code
這裏創建了包含10個線程的線程池,每個線程池對變量做1000次自增
如果去掉synchronized,最後的結果就不是10000,因爲自增操作不是原子性的
舉個栗子:
假如某個時刻變量inc的值爲10,
線程1對變量進行自增操作,線程1先讀取了變量inc的原始值,然後線程1被阻塞了;
然後線程2對變量進行自增操作,線程2也去讀取變量inc的原始值,由於線程1只是對變量inc進行讀取操作,而沒有對變量進行修改操作,所以不會導致線程2的工作內存中緩存變量inc的緩存行無效,所以線程2會直接去主存讀取inc的值,發現inc的值時10,然後進行加1操作,並把11寫入工作內存,最後寫入主存。
然後線程1接着進行加1操作,由於已經讀取了inc的值,注意此時在線程1的工作內存中inc的值仍然爲10,所以線程1對inc進行加1操作後inc的值爲11,然後將11寫入工作內存,最後寫入主存。
那麼兩個線程分別進行了一次自增操作後,inc只增加了1。
最後說明一點,操作的原子性是靠鎖保證的,僅僅用volatile關鍵字修飾是沒有用的
因爲volatile僅僅保證了變量進行操作時的可見性,也就是讀取到最新的值,但是無法保證對變量的操作的原子性
那麼volatile關鍵字修飾有啥用呢?
- 使用volatile關鍵字會強制將修改的值立即寫入主存;
- 使用volatile關鍵字的話,當線程2進行修改時,會導致線程1的工作內存中緩存變量stop的緩存行無效(反映到硬件層的話,就是CPU的L1或者L2緩存中對應的緩存行無效);
- 由於線程1的工作內存中緩存變量stop的緩存行無效,所以線程1再次讀取變量stop的值時會去主存讀取。
package learn_synchronized;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class test_volatile {
// 可見性
public volatile int inc = 0;
public synchronized void increase() {
inc++;
}
public static void main(String[] args) {
final test_volatile test = new test_volatile();
ExecutorService addTreads = Executors.newFixedThreadPool(10);
for (int i =1; i <= 10;i++){
final int index=i ;
addTreads.execute(new Runnable(){
@Override
public void run() {
System.out.println("\n第"+index+"個線程"+Thread.currentThread().getName());
for(int j = 0;j < 1000;j++) {
test.increase();
}
}
});
}
addTreads.shutdown();
// 若關閉後所有任務都已完成,則返回true。
// 注意除非首先調用shutdown或shutdownNow,否則isTerminated永不爲true。
// 返回:若關閉後所有任務都已完成,則返回true
while (!addTreads.isTerminated()) {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println("\n結束了!");
System.out.println("\n"+test.inc);
}
}