執行緒安全問題(迸發)入門知識總結
關於Java解決執行緒衝突的方法簡單總結
1.在方法面前使用synchronized或者使用方法塊
2.使用各種鎖lock,Reentrantlock,讀寫鎖
3.使用volatile保證可見性
4.使用ThreadLock複製變數副本
5.java.util.concurrent的API及StringBuffer
解決執行緒安全問題的各種方法的具體實現
A.Synchronized
synchronized的具體使用方法有兩種
1.直接寫在方法的修飾符或靜態符後面
public synchronized void methodANeedSync(){//同步的程式碼}
2.在方法內使用synchronized塊
public void methodANeedSync(){
synchronized{
//同步的程式碼
}
}
B.鎖
以Reentrantlock的使用為例
在方法外例項化鎖
class Demo
{
Lock lock=new Reentrantlock();
void methodANeedLock(){
lock.lock()
try{//需要鎖的程式碼}
catch(Exception e){...}
finall{lock.unlock; //解鎖 }
}
}
注意不能讓鎖變成方法的區域性變數,因為每個執行緒都擁有一個區域性變數的副本,會使得獲取的鎖不一樣;
關於讀寫鎖ReadWriteLock
具體實現 ReentrantReadWriteLock
與ReentrantLock的實現基本相同只是上鎖和解鎖的時候需要註明是讀還是寫的鎖
如:
ReentrantReadWriteLock rrwl=new ReentrantReadWriteLock();
void methodANeedLock(){
rrwls.readlock.lock()
try{//需要鎖的程式碼}
catch(Exception e){...}
finall{rrwl.readlock.jianlock; //解鎖 }
}
C.volatile
此關鍵字修飾的的變量表示這個變數是易變的,不需要編譯優化,一旦發生修改即可重新整理到主記憶體中,保證變數的可見性
D.ThreadLocal
可以理解為本地區域性變數的意思,對於儲存在裡面的變數(最好是隻有一個變數),使用ThreadLocal.get()方法訪問儲存在ThreadLocalMap的
變數時,get()方法會判斷當前時哪個執行緒然後基於一個變數副本,就像是一個在方法外部的區域性變數一樣;
如使用ThreadLcocal實現執行緒安全的DAO層程式碼:
public class ThreadLocalDAO {
private static final ThreadLocal<Connection> conLocal=new ThreadLocal<Connection>();
public Connection getCon(){
Connection con=conLocal.get();
if(con==null){
try {
Class.forName("com.mysql.jdbc.Driver");
try {
con= DriverManager.getConnection("#url","#uesr","#password");
} catch (SQLException e) {
e.printStackTrace();
}
conLocal.set(con);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
return con;
}
}
E.各種已經封裝好且執行緒安全的集合類或其他API(多數在concurrent包內)
如常用的有:
a.實現BlockingQueue和DequeBlocking介面的所有類
{
ArrayBlockingQueue;
DelayQueue;
LinkedBlockingQueue;
PriorityBlockingQueue;
SynchronousQueue;
LinkedBlockingQueue;
}
b.所有以concurrent開頭的集合類 如:concurrentHashMap
c.StringBuffer,Vector,Stack
d.concurrent包內的其他API