這一篇博文是【大數據技術●降龍十八掌】系列文章的其中一篇,點擊查看目錄:大數據技術●降龍十八掌
synchronized可以實現同步訪問,java.util.concurrent.locks包下的Lock也可以實現同步訪問。
synchronized是java中的一個關鍵字,是Java語言內置的特性。處於synchronized作用下的代碼塊或者方法代碼塊,是不允許兩個線程同時進入,不管是讀操作還是寫操作,都是不允許同時進行的,這在高併發的情況下對性能影響很大。而Lock更復雜,控制也更精細,另外Lock提供的功能更豐富。
1、Lock
Lock是一個接口,定義如下:
public interface Lock {
void lock();
void lockInterruptibly() throws InterruptedException;
boolean tryLock();
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
void unlock();
Condition newCondition();
}
方法 | 說明 |
---|---|
lock() | 獲取鎖,如果鎖被其他線程佔用了,就等待 |
tryLock() | 嘗試獲取鎖,如果獲取成功就返回true,如果獲取失敗,就返回false,這個不同於lock(),tryLock(),獲取不到鎖也會立即返回,而不是等待 |
unlock() | 釋放鎖,因爲鎖不會自動釋放,所以通常將unlock()方法放到finally裏,以保證肯定會被釋放掉 |
2、ReadWriteLock
ReadWriteLock是一個接口,它定義了讀鎖和寫鎖,可以使得多個線程可以同時讀。定義如下:
public interface ReadWriteLock {
Lock readLock();
Lock writeLock();
}
3、ReentrantReadWriteLock
ReentrantReadWriteLock是個類,它實現了ReadWriteLock接口。在應用讀鎖和寫鎖時候,如果一個線程佔用了寫鎖,那麼其他線程不能獲取寫鎖也不能佔用讀鎖,只有寫完其他線程才能讀和寫,當一個線程佔用了讀鎖,那麼其他線程可以獲取讀鎖,但是不能佔用寫鎖,也就是隻能讀不能寫。這樣就比synchronized提高了讀取時候的效率,因爲synchronized作用下,不管讀取還是寫入,都必須一個線程一個線程地進行。
下面是一個ReentrantReadWriteLock的應用實例,模擬多個客戶端對同一個銀行賬號進行操作,操作有:存錢、取錢、查詢餘額,保證存錢和取錢這兩個寫操作不能同時進行,但是在沒有寫入的時候,多個查詢餘額操作可以同時進行。
package lock;
import java.util.Date;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* Created by 鳴宇淳 on 2017/12/19.
* 多客戶端對銀行賬號進行存取查詢的例子
*/
public class MyLockDemo {
public static void main(String[] args) throws InterruptedException {
//創建銀行賬戶
Account account = new Account("6222022222212154578", 1000);
//創建一個鎖
ReadWriteLock lock = new ReentrantReadWriteLock(false);
//三個客戶端同時操作
Client client1 = new Client("存錢1", 1, account, 500, lock);
Client client2 = new Client("取錢2", 2, account, 800, lock);
Client client3 = new Client("查詢3", 3, account, 0, lock);
Client client4 = new Client("查詢4", 3, account, 0, lock);
Client client5 = new Client("存錢5", 1, account, 100, lock);
Thread t1 = new Thread(client1);
Thread t2 = new Thread(client2);
Thread t3 = new Thread(client3);
Thread t4 = new Thread(client4);
Thread t5 = new Thread(client5);
System.out.println(new Date() + "賬戶內起始金額爲1000\n---------------");
t1.start();
t2.start();
t3.start();
t4.start();
t5.start();
}
}
package lock;
import java.util.Date;
import java.util.concurrent.locks.ReadWriteLock;
/**
* Created by 鳴宇淳 on 2017/12/19.、
* 存取客戶端
*/
public class Client implements Runnable {
private String name; //客戶端操作人
private int saveOrWithdrawOrGet;//1.存錢 2.取錢 3.查詢餘額
private ReadWriteLock lock;//鎖
private Account account;//操作賬號
private int num;//存取金額
public Client(String name, int saveOrWithdrawOrGet, Account account, int num, ReadWriteLock lock) {
this.name = name;
this.saveOrWithdrawOrGet = saveOrWithdrawOrGet;
this.lock = lock;
this.account = account;
this.num = num;
}
public void run() {
if (this.saveOrWithdrawOrGet == 1) {
//存錢
try {
System.out.println(new Date() +this.name + "to....申請鎖存錢");
lock.writeLock().lock();
int r = account.saveMoney(this.num);
System.out.println(new Date() +this.name + "存錢" + this.num + ",餘額爲" + r);
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.writeLock().unlock();
}
} else if (this.saveOrWithdrawOrGet == 2) {
//取錢
try {
System.out.println(new Date() +this.name + "to....申請鎖取錢");
lock.writeLock().lock();
int r = account.withdrawMoney(num);
System.out.println(new Date() + this.name + "取錢" + this.num + ",餘額爲" + r);
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.writeLock().unlock();
}
} else if (this.saveOrWithdrawOrGet == 3) {
//查詢餘額
try {
System.out.println(new Date() +this.name + "to....申請鎖查詢");
lock.readLock().lock();
int r = account.getMoney();
System.out.println(new Date() +this.name + "查詢餘額,餘額爲" + r);
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.readLock().unlock();
}
}
}
}
package lock;
/**
* Created by 鳴宇淳 on 2017/12/19.
* 銀行賬戶
*/
public class Account {
private String cardcode;
private int money;
public Account(String cardcode, int money) {
this.cardcode = cardcode;
this.money = money;
}
//存錢
public int saveMoney(int num) {
this.money = this.money + num;
return this.money;
}
//取錢
public int withdrawMoney(int num) {
if (num <= this.money) {
this.money = this.money - num;
}
return this.money;
}
//查詢餘額
public int getMoney() {
return this.money;
}
}
運行以上代碼,輸入如下:
Thu Dec 28 11:09:22 CST 2017賬戶內起始金額爲1000
---------------
Thu Dec 28 11:09:22 CST 2017取錢2to....申請鎖取錢
Thu Dec 28 11:09:22 CST 2017存錢5to....申請鎖存錢
Thu Dec 28 11:09:22 CST 2017查詢3to....申請鎖查詢
Thu Dec 28 11:09:22 CST 2017查詢4to....申請鎖查詢
Thu Dec 28 11:09:22 CST 2017存錢1to....申請鎖存錢
Thu Dec 28 11:09:22 CST 2017存錢5存錢100,餘額爲1100
Thu Dec 28 11:09:24 CST 2017取錢2取錢800,餘額爲300
Thu Dec 28 11:09:26 CST 2017查詢3查詢餘額,餘額爲300
Thu Dec 28 11:09:26 CST 2017查詢4查詢餘額,餘額爲300
Thu Dec 28 11:09:28 CST 2017存錢1存錢500,餘額爲800
通過輸出可以看到,客戶端12345同時申請各自的鎖,存儲5客戶端11:09:22首先搶到了寫鎖,進行存錢操作後休眠了2秒,這個過程中,其他線程不管是申請寫鎖或者讀鎖,都不能得到鎖,2秒後11:09:24取錢2客戶端搶到了寫鎖,進程取錢操作後,休眠2秒,這個過程中同樣其他線程不能讀也不能寫,11:09:26查詢3客戶端得到讀鎖,然後也休眠2秒,這個過程,查詢4可以得到賭說,但是存錢1不能得到寫鎖,也就是隻能讀不能寫,只有當2秒後,查詢3和查詢4都釋放了讀鎖後,存錢1才能搶到寫鎖,進行存錢操作。