1. 程式人生 > >java的併發程式設計需要注意的問題

java的併發程式設計需要注意的問題

yield方法,意思是使當前執行緒從執行狀態(執行狀態)變為可執行態(就緒狀態)。cpu會從眾多的可執行態裡選擇,也就是說,當前也就是剛剛的那個執行緒還是有可能會被再次執行到的,並不是說一定會執行其他執行緒而該執行緒在下一次中不會執行到了。

當多執行緒的情況下,遇到共享變數的情況下,為了保持資料的一致性,可以採用synchronized關鍵字,可以加方法上,表示對這個物件this加鎖,但是效率不高,可以用synchronized程式碼段,只對共享變數的地方加鎖,還有如果共享變數是數字時,用Atomic關鍵字修飾變數。可以不用synchronized。這個物件是java併發包給我提供的類,裡面包含有加減運算

volatile關鍵字 ,cpu是有快取的,當變數的值已經改變,但是cpu還是從快取中取值就會出現錯誤,volatile關鍵字就是告訴cpu必須從記憶體中取值,不走快取,volatile可以實現可見性,不能實現原子性問題。 不能解決併發問題
synchronize可以實現可見性和原子性問題。 可以解決併發問題

ThreadLocal 作用是和當前執行緒繫結,這個物件有個get,set方法,可以把物件放進去,不會出現多執行緒爭奪共享變數的情況

同步容器: vector hashtable Collections.synchronize() 這些是一些老的api,原始碼基本上都是採用synchronize來實現的,很笨重。

**併發容器:**ConcurrentHashMap 分段鎖 java 1.5提供的api
ConcurrentHashMap map = new ConcurrentHashMap ();
map .putIfAbsent(1,2) 這個方法是 如果key不存在就放進去,存在就返回none。

CopyOnWriteArrayList /Set 這兩個實現的基本原理是操作的時候先上一把鎖,然後把共享資源複製一份,然後再複製的資源裡面搞搞搞,使用的場景是讀的多,寫的少的操作

BlocakingQueue 阻塞佇列。有兩個方法,一個put放東西,take取東西 佇列特點就是先進先出,就像一根水管,往裡面塞東西。先進的肯定先出。

閉鎖 柵欄 訊號量

閉鎖的意思就是當執行緒都執行完了,才放行,如何實現呢?CountDownLatch 倒計時的意思

CountDownLatch  latch = new  CountDownLatch (2);   //表示有兩個執行緒

latch.countDown()      //一般執行緒執行完之後呼叫這個方法,表示這個執行緒執行完了

latch.awit()          //這個方法必須是latch裡面的執行緒都執行完了,才放行。

柵欄 的相當於水壩的意思,就是有個執行緒達到某種狀態之後,然後這個執行緒才能繼續操作

CyclicBarrier barrier = new CyclicBarrier (4)

barrier.await()   //只有當wait的執行緒等於4,才會繼續往下走。不然都得等著。

訊號量 生產者和消費者人數不對等的情況下,比如5個機器,8個人要用,剛開始每人一個,然後有人釋放掉,另外一個工人就補上、

int t=8  //8個工人
Semaphore semaphore = new  Semaphore(5) //機器的數目

semaphore.acquire();   //意思是從5臺機器中獲取一個

semaphore.release();    //意思是機器釋放,下一個工人就頂上。

執行緒池

當遇到併發時,肯定需要用到執行緒池,因為你不可能沒來一次請求就建立一個執行緒吧,那樣的話你伺服器會受不了的。

Executor executor = Executors.newFixedThreadPool(100);//意思是這個執行緒池有100個執行緒

executor.execute(執行緒)  //把執行緒扔進去,就可以了。他就執行執行緒的start方法。

Executor的實現類ExecutorService   這個類是Executor的實現類,裡面有幾個非常常用的方法。

定時任務

java提供的Timer這個類可以實現定時任務。
注意:timer有兩個缺點,一個是當有多個任務的時候,比如原本第二個任務是1秒之後執行,但是第一個任務執行兩秒才結束,那麼第二個任務就必須等第一個任務執行完再執行。
還有一個就是當第一個任務報錯之後,timer裡面的任務全都掛掉了。這是非常嚴重的後果。

Timer timer=new Timer();
timer.schedule(執行緒,1000);//意思是1000毫秒之後執行執行緒


//如何解決這個問題呢?
//建立一個可排程的執行緒池
ScheduledExecutorService ExecutorService = Executors.newScheduleduledThreadPool(100);
EsxecutorService.schedule(執行緒,1000,TimerUnit.MILLISECONDS)//最後一個是指明1000的時間單位,這裡是毫秒

死鎖

巢狀鎖會造成死鎖
解決辦法:1避免巢狀鎖,2巢狀鎖的順序,3引入超時機制

Lock鎖

是java1.5提供的一種鎖,可以很靈活的控制。

Lock lock=new ReentrantLock();     //建立一個可重入鎖

//主要有兩個方法
lock.lock();      //加鎖
locak.unlock();  //解鎖
ReentrantReadWriteLock rwl= new  ReentrantReadWriteLock() ;  //建立一個既可以讀又可以寫的鎖
rwl.writeLock().lock()   //建立一個寫鎖, 只能一個執行緒進來
rwl.writeLock().unlock() //釋放寫鎖

rwl.readLock().lock()   //建立一個讀鎖, 可以有多個執行緒進來
rwl.readLock().unlock() //釋放讀鎖

悲觀鎖和樂觀鎖 —–資料庫層面的

悲觀鎖:只有一個執行緒在操作,不管讀還是寫,直接把這個表鎖住了,其他執行緒進不來,實現語句
後面加for update

select * from user  for update

樂觀鎖:表中加version欄位。

分散式鎖:redis zk