【十八掌●基本功篇】第一掌:Java之多線程--鎖

這一篇博文是【大數據技術●降龍十八掌】系列文章的其中一篇,點擊查看目錄:這裏寫圖片描述大數據技術●降龍十八掌


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才能搶到寫鎖,進行存錢操作。

發佈了74 篇原創文章 · 獲贊 77 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章