關於線程的同步,可以使用synchronized關鍵字,或者是使用JDK 5中提供的java.util.concurrent.lock包中的Lock對象。本文探討Lock對象。
synchronized與java.util.concurrent.locks.Lock 的相同點:Lock能完成synchronized所實現的所有功能;主要不同點:Lock有比synchronized更精確的線程語義和更好的性能。synchronized會自動釋放鎖,而Lock一定要求程序員手工釋放, 並且必須在finally從句中釋放。
一:先來一段簡單的代碼
這段代碼以前曾經用synchronized關鍵字實現同步(Java:多線程,線程同步,synchronized關鍵字的用法(同步代碼塊、非靜態同步方法、靜態同步方法)),現在用Lock對象實現:
package com.clzhang.sample.thread; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class LockThread1 implements Runnable { private Integer key = 0; // 鎖對象 private Lock lock = new ReentrantLock(); @Override public void run() { // 需要結果是key實現自增長,如果沒有同步塊,則可能會出現重復key值的現象 lock.lock(); try { key++; System.out.println(Thread.currentThread().getName() + ":" + key); try { Thread.sleep(10); } catch (InterruptedException e) { } }finally{ // 上述代碼實現功能與使用sychronized同步代碼塊一樣。 // sychronized同步代碼塊或同步方法在代碼執行完之后鎖自動釋放;而用Lock則需要手工釋放鎖。 // 為了保證鎖最終被釋放,釋放鎖代碼放在finally塊內。 lock.unlock(); } } public static void main(String[] args) { LockThread1 lt = new LockThread1(); for(int i=0; i<100; i++) { new Thread(lt, "Thread" + i).start(); } } }
部分輸出:
Thread86:95
Thread88:96
Thread90:97
Thread98:98
Thread92:99
Thread96:100
二:再來一段稍復雜的代碼
package com.clzhang.sample.thread; import java.util.*; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class LockThread2 implements Runnable { // 內部類 class Student { private int age = 0; public int getAge() { return age; } public void setAge(int age) { this.age = age; } } // 全局變量定義 private int count = 0; private Student student = new Student(); // 鎖對象 private Lock lock1 = new ReentrantLock(false); private Lock lock2 = new ReentrantLock(false); @Override public void run() { String currentThreadName = Thread.currentThread().getName(); System.out.println(currentThreadName + " is running!"); lock1.lock();// 使用重入鎖 System.out.println(currentThreadName + " got lock1@Step1!"); try { count++; Thread.sleep(3000); } catch (Exception e) { e.printStackTrace(); } finally { System.out.println(currentThreadName + " first:count=" + count + "\tage=" + this.student.getAge()); lock1.unlock(); }
lock2.lock();// 使用另外一個不同的重入鎖 System.out.println(currentThreadName + " got lock2@Step2!"); try { Random random = new Random(); int age = random.nextInt(100); this.student.setAge(age); Thread.sleep(3000); } catch (Exception ex) { ex.printStackTrace(); } finally { System.out.println(currentThreadName + " second:count=" + count + "\tage=" + this.student.getAge()); lock2.unlock(); } } public static void main(String[] args) { LockThread2 lt = new LockThread2(); for (int i = 1; i <= 3; i++) { Thread t = new Thread(lt, "Thread" + i); t.start(); } } }
輸出:
Thread1 is running!
Thread1 got lock1@Step1! // 線程1獲取鎖1
Thread3 is running!
Thread2 is running!
Thread1 first:count=1 age=0
Thread3 got lock1@Step1!
Thread1 got lock2@Step2! // 線程3、1分別獲取鎖1、2
Thread3 first:count=2 age=13
Thread1 second:count=2 age=13 // count值已經被線程3更改;age是自己設置的。
Thread3 got lock2@Step2!
Thread2 got lock1@Step1! // 線程3、2分別獲取鎖2、1
Thread2 first:count=3 age=34
Thread3 second:count=3 age=34 // count值已經被線程2更改;age是自己設置的。
Thread2 got lock2@Step2! // 線程2獲取鎖2
Thread2 second:count=3 age=40 // 沒人改count值了;age是自己設置的。
參考:http://wenku.baidu.com/view/bba6ec24482fb4daa48d4b06.html
三:再來一段讀寫的代碼
要求:寫入和寫入互斥,讀取和寫入互斥,讀取和讀取之間不互斥。
package com.clzhang.sample.thread; import java.util.*; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; public class LockThread3 implements Runnable { // 數據存放 private StringBuilder sb = new StringBuilder(); // 鎖對象 private ReadWriteLock rwl = new ReentrantReadWriteLock(); @Override public void run() { if (Thread.currentThread().getName().startsWith("Read")) { rwl.readLock().lock();// 取到讀鎖 try { System.out.println(Thread.currentThread().getName() + "正在讀取..."); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "結果:" + sb.toString()); } finally { rwl.readLock().unlock();// 釋放讀鎖 } } else if (Thread.currentThread().getName().startsWith("Write")) { rwl.writeLock().lock();// 取到寫鎖 try { System.out.println(Thread.currentThread().getName() + "正在寫入..."); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } String writeData = "" + new Random().nextInt(100); sb.append(writeData + ","); System.out.println(Thread.currentThread().getName() + "結果:" + writeData); } finally { rwl.writeLock().unlock();// 釋放寫鎖 } } else { // 啥也不做行不 } } public static void main(String[] args) { LockThread3 lt = new LockThread3(); for (int i=0; i<5; i++) { Thread t = new Thread(lt, "Write" + i); t.start(); } for (int i=0; i<5; i++) { Thread t = new Thread(lt, "Read" + i); t.start(); } } }
輸出:
Write1正在寫入...
Write1結果:25
Write0正在寫入...
Write0結果:5
Read0正在讀取...
Read2正在讀取...
Read4正在讀取...
Read2結果:25,5,
Read0結果:25,5,
Read4結果:25,5,
Write2正在寫入...
Write2結果:77
Write4正在寫入...
Write4結果:38
Read1正在讀取...
Read3正在讀取...
Read1結果:25,5,77,38,
Read3結果:25,5,77,38,
Write3正在寫入...
Write3結果:77