java 线程可见性,Java多线程之内存可见性
Java內存模型( JMM ) :
1) 所有的變量都存儲在主內存中
2) 每個線程都有自己獨立的工作內存, 里面保存該線程使用到的變量的副本 ( 主內存中該變量的一份拷貝 )
JMM兩條規(guī)定:
1) 線程對共享變量的所有操作都必須在自己的工作內存中進行
2) 不同線程之間無法直接訪問其他線程工作內存中的共享變量, 線程間共享變量值的傳遞必須通過主內存
線程間共享變量可見性實現(xiàn)的原理:
線程A 對共享變量的修改想被線程B 及時看到, 必須要經(jīng)過以下2個步驟:
1) 把線程A 工作內存中更新過的共享變量刷新到主內存中( store )
2) 將主內存中最新的共享變量的值共享到線程B 工作內存中( load )
Java 語言層面支持的可見性實現(xiàn)方式:
1) synchronized
2) volatile
JUC 包下的類也可以實現(xiàn)可見性
1) Atomic
2)?ReentrantLock
3) Semaphore
1. synchronized 實現(xiàn)可見性
JMM 關于 synchronized 的兩條規(guī)定:
1) 線程釋放鎖前, 必須把共享變量的最新值從該線程的工作內存刷新到主內存中
2) 線程持有鎖時, 將清空該線程工作內存中共享變量的值, 從主內存中讀取最新的值
synchronized 實現(xiàn)可見性的原因:
線程釋放鎖前對共享變量的修改在下次持有鎖時對其他線程可見
public?class?SynchronizedDemo?{
//?共享變量
private?int?result?=?0;
//?共享變量執(zhí)行自增操作
public?synchronized?void?increase()?{
result++;
}
public?int?getResult()?{
return?result;
}
public?static?void?main(String[]?args)?throws?InterruptedException?{
final?SynchronizedDemo?demo?=?new?SynchronizedDemo();
//?設置啟動500個線程
int?count?=?500;
//?創(chuàng)建一個?JUC?包下的同步同步計數(shù)器,?設置計數(shù)次數(shù)為線程數(shù)500
final?CountDownLatch?cdl?=?new?CountDownLatch(count);
for?(int?i?=?0;?i?
new?Thread(new?Runnable()?{
@Override
public?void?run()?{
demo.increase();
cdl.countDown();
}
}).start();
}
cdl.await();
System.out.println(demo.getResult());
}
}
Console 輸出: 500
synchronized 不僅可以實現(xiàn)可見性, 還可以實現(xiàn)原子性
volatile 如何實現(xiàn)可見性:
通過加入內存屏障和禁止指令重排序
1) 對 volatile 變量執(zhí)行寫操作時, 會在寫操作后加入一條 store 屏障指令
2) 對 volatile 變量執(zhí)行讀操作時, 會在讀操作前加入一條 load?屏障指令
public?class?VolatileDemo?{
//?使用?volatile?修飾共享變量
private?volatile?int?result?=?0;
//?共享變量?result?執(zhí)行自增操作,?無法保證原子性
public?void?increase()?{
result++;
}
public?int?getResult()?{
return?result;
}
public?static?void?main(String[]?args)?throws?InterruptedException?{
final?VolatileDemo?demo?=?new?VolatileDemo();
//?設置啟動500個線程
int?count?=?500;
//?創(chuàng)建一個?JUC?包下的同步同步計數(shù)器,?設置計數(shù)次數(shù)為線程數(shù)500
final?CountDownLatch?cdl?=?new?CountDownLatch(count);
for?(int?i?=?0;?i?
new?Thread(new?Runnable()?{
@Override
public?void?run()?{
demo.increase();
cdl.countDown();
}
}).start();
}
cdl.await();
System.out.println(demo.getResult());
}
}
Console 輸出: 498
volatile 關鍵字, 能保證 volatile 變量的可見性, 不能保證 volatile 變量操作的原子性( 如 ++/-- )
3. AtomicInteger 實現(xiàn)可見性
用 Atomic 類實現(xiàn)共享變量在線程中的原子性( 通過 CAS, 自旋 實現(xiàn))
public?class?AtomicIntegerDemo?{
//?共享變量
private?AtomicInteger?result?=?new?AtomicInteger(0);
//?使用?Atomic?類的?incrementAndGet()?方法(?原子操作?),?實現(xiàn)自增
public?void?increase()?{
result.incrementAndGet();
}
public?int?getResult()?{
return?result.get();
}
public?static?void?main(String[]?args)?throws?InterruptedException?{
final?AtomicIntegerDemo?demo?=?new?AtomicIntegerDemo();
//?設置啟動500個線程
int?count?=?500;
//?創(chuàng)建一個JUC包下的同步同步計數(shù)器,?設置計數(shù)次數(shù)為線程數(shù)500
final?CountDownLatch?cdl?=?new?CountDownLatch(count);
for?(int?i?=?0;?i?
new?Thread(new?Runnable()?{
@Override
public?void?run()?{
demo.increase();
cdl.countDown();
}
}).start();
}
cdl.await();
System.out.println(demo.getResult());
}
}
Console 輸出: 500
4. JUC 包下的 Lock 實現(xiàn)可見性
用 ReentrantLock 實現(xiàn)共享變量在線程中的原子性
public?class?LockDemo?{
//?共享變量
private?int?result?=?0;
//?可重入鎖
private?Lock?lock?=?new?ReentrantLock();
//?使用鎖機制,?保證鎖內代碼的原子性
public?void?increase()?{
//?加鎖
lock.lock();
try?{
result++;
}?finally?{
//?釋放鎖
lock.unlock();
}
}
public?int?getResult()?{
return?result;
}
public?static?void?main(String[]?args)?throws?InterruptedException?{
final?LockDemo?demo?=?new?LockDemo();
//?設置啟動500個線程
int?count?=?500;
//?創(chuàng)建一個JUC包下的同步同步計數(shù)器,?設置計數(shù)次數(shù)為線程數(shù)500
final?CountDownLatch?cdl?=?new?CountDownLatch(count);
for?(int?i?=?0;?i?
new?Thread(new?Runnable()?{
@Override
public?void?run()?{
demo.increase();
cdl.countDown();
}
}).start();
}
cdl.await();
System.out.println(demo.getResult());
}
}
Console 輸出: 500
5. Semaphore 實現(xiàn)可見性
用信號量機制實現(xiàn)共享變量在線程中的原子性
public?class?SemaphoreDemo?{
//?共享變量
private?int?result?=?0;
//?初始化信號量為1,?一次只能有1個線程訪問共享變量,?相當于互斥鎖
private?Semaphore?semaphore?=?new?Semaphore(1);
//?使用信號量機制,?保證共享變量自增操作的原子性
public?void?increase()?{
try?{
//?獲取1個信號量
semaphore.acquire();
}?catch?(InterruptedException?e)?{
e.printStackTrace();
}
result++;
//?釋放1個信號量
semaphore.release();
}
public?int?getResult()?{
return?result;
}
public?static?void?main(String[]?args)?throws?InterruptedException?{
final?SemaphoreDemo?demo?=?new?SemaphoreDemo();
//?設置啟動500個線程
int?count?=?500;
//?創(chuàng)建一個JUC包下的同步同步計數(shù)器,?設置計數(shù)次數(shù)為線程數(shù)500
final?CountDownLatch?cdl?=?new?CountDownLatch(count);
for?(int?i?=?0;?i?
new?Thread(new?Runnable()?{
@Override
public?void?run()?{
demo.increase();
cdl.countDown();
}
}).start();
}
cdl.await();
System.out.println(demo.getResult());
}
}
Console 輸出: 500
總結:
synchronized 代碼塊具備 可見性和 可見性
volatile變量具備可見性, 不具備原子性
總結
以上是生活随笔為你收集整理的java 线程可见性,Java多线程之内存可见性的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: php 中国标准时间,linux 系统时
- 下一篇: php国外地址生成,thinkphp u