1. 程式人生 > >多線程之鎖機制

多線程之鎖機制

並且 not 什麽 釋放 ble 後來 多線程 結果 void

前言

  在Java並發編程實戰,會經常遇到多個線程訪問同一個資源的情況,這個時候就需要維護數據的一致性,否則會出現各種數據錯誤,其中一種同步方式就是利用Synchronized關鍵字執行鎖機制,鎖機制是先給共享資源上鎖,只有拿到鎖的線程才可以訪問共享資源,其他線程進入等待狀態。下面將以實例代碼講解一下

一、wait()、nofity()、nofityAll()講解

  示例代碼

package thread;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.TimeUnit;

/** * Created by StoneGeek on 2018/5/19. * 博客地址:http://www.cnblogs.com/sxkgeek * 當線程執行wait()時,會把當前的鎖釋放,然後讓出CPU,進入等待狀態。 * 當線程執行notify()/notifyAll()方法時,會喚醒一個處於等待狀態該對象鎖的線程,然後繼續往下執行,直到執行完退出對象鎖鎖住的區域(synchronized修飾的代碼塊)後再釋放鎖 * 個人認為synachronized(){}執行完後會釋放鎖 */ public class WaitNotify { static boolean flag = true
; static Object lock = new Object(); public static void main(String[] args) throws Exception { Thread waitThread = new Thread(new Wait(), "WaitThread"); waitThread.start(); TimeUnit.SECONDS.sleep(1); Thread notifyThread = new Thread(new Notify(), "NotifyThread
"); notifyThread.start(); } static class Wait implements Runnable { public void run() { // 加鎖,擁有lock的Monitor synchronized (lock) { // 當條件不滿足時,繼續wait,同時釋放了lock的鎖 while (flag) { System.out.println(Thread.currentThread().getName() + " flag is true. wait@ " + new SimpleDateFormat("HH:mm:ss") .format(new Date())); try { lock.wait(); System.out.println("此處繼續執行"+Thread.currentThread().getName()); // flag=true; } catch (InterruptedException e) { e.printStackTrace(); } } // 條件滿足時,完成工作 System.out.println(Thread.currentThread().getName() + " flag is false. running@ " + new SimpleDateFormat("HH:mm:ss").format(new Date())); } synchronized (lock){ System.out.println(Thread.currentThread().getName()+"執行結束"); } } } // wait()會立刻釋放synchronized(obj)中的obj鎖,以便其他線程可以執行obj.notify() // 但是notify()不會立刻立刻釋放sycronized(obj)中的obj鎖,必須要等notify()所在線程執行完synchronized(obj)塊中的所有代碼才會釋放這把鎖. // yield(),sleep()不會釋放鎖 static class Notify implements Runnable { public void run() { // 加鎖,擁有lock的Monitor synchronized (lock) { // 獲取lock的鎖,然後進行通知,通知時不會釋放lock的鎖, // 直到當前線程釋放了lock後,WaitThread才能從wait方法中返回 System.out.println(Thread.currentThread().getName() + " hold lock. notify @ " + new SimpleDateFormat("HH:mm:ss").format(new Date())); lock.notifyAll(); flag = false; } // 再次加鎖 synchronized (lock) { System.out.println(Thread.currentThread().getName() + " hold lock again. sleep@ " + new SimpleDateFormat("HH:mm:ss").format(new Date())); } synchronized (lock){ System.out.println(Thread.currentThread().getName()+"執行結束"); } } } }

  執行結果如下

1  WaitThread flag is true. wait@ 20:50:39
2  NotifyThread hold lock. notify @ 20:50:40
3  NotifyThread hold lock again. sleep@ 20:50:40
4  NotifyThread執行結束
5  此處繼續執行WaitThread
6  WaitThread flag is false. running@ 20:50:40
7  WaitThread執行結束

  解釋:

  首先創建一個lock對象,然後給這個lock上鎖來對多個進程同步,flag是一個標誌,用來跳出while循環。

  當線程執行wait()時,會把當前的鎖釋放,然後讓出CPU,進入等待狀態。

  此時輪到notifythread線程,並且執行notifyAll(),這個意思是能夠喚醒所有正在等待這個lock對象的monitor的線程,但是

  必須要等notify()所在線程執行完synchronized(obj)塊中的所有代碼才會釋放這把鎖,

  此時接著waitthread被喚醒,繼續執行while循環,執行完之後,由於flag在notifythread中置為false,所以跳出while循環(如果在實例代碼的wail()後加flag=true結果是截然不同,由於notirythread進程執行完,此時會一直陷入wait,大家可以試試),

  執行console打印5 6 7

  notify()與notifyAll()的區別 

  notify()方法能夠喚醒一個正在等待該對象的monitor的線程,當有多個線程都在等待該對象

  的monitor的話,則只能喚醒其中一個線程

  而調用notifyAll()方法能夠喚醒所有正在等待這個對象的monitor的線程

  當時的疑惑

  (1)既然notify或者notifyAll需要執行完synchronized塊中的內容,那麽他還有什麽存在的價值的

    後來執行完之後,才發現要是沒有這個方法,那麽synchronized塊執行完之後,waitthread還是在等待狀態,無法被喚醒。

  (2)wait被notify喚醒之後,是接著執行,所以console打印5,並不是從頭執行(如果在實例代碼的wail()後加flag=true結果是截然不同,由於notirythread進程執行完,waitthread進程重新執行wait方法,此時會一直陷入wait,無其他進程喚醒此進程)

二、sychronized(object)跟sychroized(this)的區別、使用場景

  (1)首先創建一個objeck,然後sychronzied(object){}

    static Object lock = new Object();
           synchronized (lock) {}

  這種情況是當多個線程執行不同的代碼但是希望分別執行,不同時進行,當然可以加wait、notify來進行鎖機制同步

  (2)sychronized(this)

synchronized (lock) {}

  這種情況是多個線程執行相同的代碼,但是希望分別執行.

三、wait()/wait(long)和sleep(long)方法的區別

  將示例代碼中的lock.wait()改為Thread.sleep(1000),console打印

WaitThread flag is true. wait@ 21:29:49
此處繼續執行WaitThread
WaitThread flag is true. wait@ 21:29:50
此處繼續執行WaitThread
WaitThread flag is true. wait@ 21:29:51
此處繼續執行WaitThread
WaitThread flag is true. wait@ 21:29:52
此處繼續執行WaitThread

  由此說明sleep並沒有釋放鎖。

  區別:

    1、Sleep(long)是Thread的方法,而wait()/wait(long)是Object的方法

    2、Sleep(long)可以放在sychnoized塊內也可以不放在裏面,但是wait()/wait(long)必須放在語句塊內

    3、Sleep(long)不釋放鎖,只是讓當前線程暫停一段時間,而wait()/wait(long)是釋放鎖

    4、wait()將當前線程放到阻塞隊列,只有調用notify()/notifyAll()方法後,才將其從阻塞隊列中移動到就緒隊列,等待被CPU調度,而wait(long)方法執行後就是放到阻塞隊列,等待時間到期或者被wait()/wait(long)喚醒後就可以放到就緒隊列被CPU調度

  目前還有一個疑惑,就是線程中的run方法有兩個同樣的synchroized(lock),是不是跟一個synchroized(lock)效果是一樣的,目前就運行結果來看是這樣子的!

多線程之鎖機制