天天看點

Lock接口與讀寫鎖(lock 和 synchronized 的比較)

出現:JDK1.5 實作類: ReentrantLock(重入鎖、手工鎖)

ReentrantReadWriteLock.ReadLock  ReentrantReadWriteLock.WriteLock 主要方法: lock() unlock() 使用: TestLock.java

1

public class TestLock implement Runnable{      

2

3

   ReentrantLock lock = new ReentrantLock();      

4

5

   @Override      

6

   public void run(){      

7

8

           try{      

9

               //加鎖      

10

               lock.lock();      

11

               //執行同步代碼      

12

           }      

13

           finally{      

14

               //釋放鎖      

15

               lock.unlock();      

16

           }      

17

18

}      

和synchronized互斥鎖的比較: (1)synchronized關鍵字修飾的代碼是完全互斥的,而Lock更加 靈活,可以實作多線程同時讀共享資料(讀鎖和讀鎖不互斥),但讀鎖和寫鎖互斥。寫鎖和寫鎖互斥( 讀寫鎖)。 (2)ReentrainLock有 tryLock方法:

1

public class ReentrainLockTest{      

2

   //擷取鎖      

3

   Lock lock = new ReentrainLock();      

4

   //線程1的方法:擷取鎖      

5

   void m1(){      

6

       lock.lock();//鎖定      

7

       try{      

8

           for(int i=0;i<10;i++){//鎖定10秒      

9

               TimeUtil.SECONDS.sleep(1);//sleep 1秒      

10

           }      

11

       }catch(InterruptException e){      

12

           e.printStackTrace();      

13

       }finally{      

14

           lock.unlock();//10秒後釋放鎖      

15

       }      

16

   }      

17

   //線程2的方法      

18

   void m2(){      

19

       boolean locked = lock.tryLcok();//嘗試鎖定,如果傳回為false,則擷取鎖失敗,但程式繼續執行。      

20

       Systerm.out.println("是否獲得鎖:" + locked);      

21

       if(locked) lock.unlock();//如果獲得鎖,則解鎖      

22

   }      

23

24

   public static void main(String[] args){      

25

       //開啟兩個線程,線程1調用m1方法,線程2調用m2方法      

26

   }       

27

}      

(3)lockInterruptibly方法持有的鎖,如果沒擷取到鎖可被其它線程打斷

1

public class{      

2

   public static void main(Stirng[] args){      

3

       Lock lock = new ReentrainLock();      

4

       //1.建立t1線程,一直持有鎖      

5

       Thread t1 = new Thread(()-> {      

6

           lock.lock();      

7

           try{      

8

               while(true){//一直持有鎖      

9

                   TimeUtils.SECOND.sleep(1);      

10

               }      

11

           }catch(InterruptException e){      

12

               e.printStackTrace();      

13

           }fainally{      

14

               lock.unlock();//釋放鎖      

15

           }      

16

       });      

17

       t1.start();      

18

19

       //2.啟動t2線程,嘗試擷取鎖,但是一直擷取失敗(t1線程一直持有鎖)      

20

       Tread t2 = new Thread(()->{      

21

           try{      

22

               boolean isLocked = lock.tryLock();      

23

               lock.lockInterruptibly();//表示可以對其它線程的interrupt方法做出響應      

24

               System.out.println("t2線程啟動!");      

25

           }catch(InterruptException e){      

26

               e.printStackTrace();      

27

           }finally{      

28

               if(isLocked) lock.unlock();      

29

           }      

30

31

       });      

32

       t2.start();      

33

34

       //3.主線程1秒後打斷t2線程的等待鎖狀态,t2線程結束,抛出異常      

35

       try{      

36

           timeUtils.SECONDS.sleep(1)      

37

       }catch(InterruptException e){      

38

           e.printStackTrace();      

39

       }      

40

       t2.interrupt();      

41

   }      

42

}      

(4)reentrianLock可以指定公平鎖(synchronized為不公平鎖) synchronized在鎖釋放時,所有線程都在競争鎖(不确定誰能拿到鎖),很可能有的線程一次都拿不到,不公平

reentrianLock可以在鎖競争時,判斷哪個線程等待的時間長,就先讓哪個線程擷取鎖

1

Lock lock = new ReentrainLock(true);//傳入true為公平鎖,預設為非公平鎖      

讀寫鎖接口: ReadWriteLock 實作類: ReentranReadWriteLock 方法: readLock、writeLock(傳回Lock對象) 使用:

1

ReadWriteLock rwl = new ReenTranReadWriteLock();      

2

rwl.readLock().lock();//擷取讀鎖      

3

rwl.readLock().unLock();//釋放讀鎖      

4

5

rwl.writeLock().lock();//擷取寫鎖      

6

rwl.writeLock().unLock();//釋放寫鎖      

例子:使用者從銀行讀取餘額(讀鎖,多使用者同時讀一個賬号)。使用者從銀行存取錢(寫鎖,每次隻能一個使用者從同一個賬号存取錢)

使用者賬戶類:MyAccount.java

1

public class MyCount {        

2

        private String oid;         //賬号        

3

        private int cash;             //賬戶餘額        

4

5

        MyCount(String oid, int cash) {        

6

                this.oid = oid;        

7

                this.cash = cash;        

8

        }        

9

10

        public String getOid() {        

11

                return oid;        

12

        }        

13

14

        public void setOid(String oid) {        

15

                this.oid = oid;        

16

        }        

17

18

        public int getCash() {        

19

                return cash;        

20

        }        

21

22

        public void setCash(int cash) {        

23

                this.cash = cash;        

24

        }        

25

26

        @Override        

27

        public String toString() {        

28

                return "MyCount{" +        

29

                                "oid='" + oid + '\'' +        

30

                                ", cash=" + cash +        

31

                                '}';        

32

        }        

33

}        

線程類(多使用者同時通路一個賬号):UserThread.java

1

public class UserThread implements Runnable {        

2

        private String name;                 //使用者名        

3

        private MyCount myCount;         //所要操作的賬戶        

4

        private int iocash;                 //操作的金額,當然有正負之分了        

5

        private ReadWriteLock myLock;                 //執行操作所需的鎖對象        

6

        private boolean ischeck;         //是否查詢        

7

8

        public UserThread(String name, MyCount myCount, int iocash, ReadWriteLock myLock, boolean ischeck) {        

9

                this.name = name;        

10

                this.myCount = myCount;        

11

                this.iocash = iocash;        

12

                this.myLock = myLock;        

13

                this.ischeck = ischeck;//是否檢視餘額        

14

        }        

15

16

        public void run() {        

17

                if (ischeck) {  //檢視餘額      

18

                        //擷取讀鎖        

19

                        myLock.readLock().lock();        

20

                        System.out.println("讀:" + name + "正在查詢" + myCount + "賬戶,目前金額為" + myCount.getCash());        

21

                        //釋放讀鎖        

22

                        myLock.readLock().unlock();        

23

                } else {  //存取錢      

24

                        //擷取寫鎖        

25

                        myLock.writeLock().lock();        

26

                        //執行現金業務        

27

                        System.out.println("寫:" + name + "正在操作" + myCount + "賬戶,金額為" + iocash +",目前金額為" + myCount.getCash());        

28

                        myCount.setCash(myCount.getCash() + iocash);        

29

                        System.out.println("寫:" + name + "操作" + myCount + "賬戶成功,金額為" + iocash +",目前金額為" + myCount.getCash());        

30

                        //釋放寫鎖        

31

                        myLock.writeLock().unlock();        

32

                }        

33

        }        

34

}        

測試類:Test.java

1

public class Test {        

2

        public static void main(String[] args) {        

3

                //建立并發通路的賬戶        

4

                MyCount myCount = new MyCount("95599200901215522", 10000);        

5

                //建立一個鎖對象        

6

                ReadWriteLock lock = new ReentrantReadWriteLock(false);        

7

                //建立一個線程池        

8

                ExecutorService pool = Executors.newFixedThreadPool(2);        

9

                //建立一些并發通路使用者,一個信用卡,存的存,取的取,好熱鬧啊        

10

                User u1 = new User("Mike", myCount, -4000, lock, false);        

11

                User u2 = new User("Mike's Dad", myCount, 6000, lock, false);        

12

                User u3 = new User("Mike's Brother", myCount, -8000, lock, false);        

13

                User u4 = new User("Mike's Mom", myCount, 800, lock, false);        

14

                User u5 = new User("Mike's son", myCount, 0, lock, true);        

15

                //線上程池中執行各個使用者的操作        

16

                pool.execute(u1);        

17

                pool.execute(u2);        

18

                pool.execute(u3);        

19

                pool.execute(u4);        

20

                pool.execute(u5);        

21

                //關閉線程池        

22

                pool.shutdown();        

23

        }        

24

}        

鎖更新和降級原則: 讀鎖不可更新成寫鎖(讀鎖釋放前不可以加寫鎖)

寫鎖可以降級成讀鎖(寫鎖釋放前可以加讀鎖)

官方文檔讀寫鎖例子:

1

class CachedData {      

2

  Object data;      

3

  volatile boolean cacheValid;      

4

  final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();      

5

6

  void processCachedData() {      

7

    rwl.readLock().lock();      

8

    if (!cacheValid) {//第一重檢測,沒有緩存資料,釋放讀鎖,加寫鎖寫資料      

9

       // 在寫鎖前必須釋放讀鎖      

10

       rwl.readLock().unlock();      

11

       rwl.writeLock().lock();      

12

       try {      

13

         //第二重檢測,寫資料前再次確定緩存沒有資料,防止在第10行釋放了讀鎖後,被其它線程的寫鎖拿到,往緩存裡寫了資料      

14

         if (!cacheValid) {      

15

           data = ...//資料庫寫入修改操作      

16

           cacheValid = true;      

17

         }      

18

         // 鎖降級,在寫鎖裡面先上讀鎖,把寫鎖降級成讀鎖      

19

         rwl.readLock().lock();      

20

       } finally {      

21

         rwl.writeLock().unlock(); // 在讀鎖狀态下釋放寫鎖      

22

       }      

23

    }      

24

25

    try {      

26

      use(data);      

27

    } finally {      

28

      rwl.readLock().unlock();      

29

    }      

30

  }      

31

}      

繼續閱讀