Java併發編程 - 鎖Lock 取款機場景實例

Lock是一個接口,ReentrantLock是它的實現類,下面經過「取款機案例」來剖析它的4個經常使用方法。java

1.爸爸媽媽同時在ATM上登陸取款(不加任何鎖)

public class Bank {
    private static double money = 10000;

    public void login(Thread currentUserThread) {
        System.out.println(Thread.currentThread().getName() + "  登陸進入銀行" + "  當前銀行餘額  : " + money);

    }

    public void logout() {
        System.out.println(Thread.currentThread().getName() + "  退出銀行");
    }

    public double withdraw(double withdrawMoney) {
        if (this.money < withdrawMoney) {
            System.out.println(Thread.currentThread().getName() + " 當前銀行餘額  : " + this.money + " 餘額不夠");
            return 0;
        }
        this.money -= withdrawMoney;
        System.out.println(Thread.currentThread().getName() + "  取款  : " + withdrawMoney + "  當前銀行餘額  : " + this.money);
        return withdrawMoney;
    }
}
public class TestLock {

    public static void main(String[] args) {
        final Bank bank = new Bank();

        //啓動爸爸線程
        Thread fatherThread = new Thread("爸爸") {
            public void run() {
                try {
                    //爸爸登陸
                    bank.login(Thread.currentThread());
                    //過2秒取10000
                    Thread.sleep(2000);
                    bank.withdraw(10000);

                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        fatherThread.start();

        // 啓動媽媽線程
        Thread motherThread = new Thread("媽媽") {
            public void run() {
                try {
                    //媽媽登陸
                    bank.login(Thread.currentThread());
                    //過5秒取 10000
                    Thread.sleep(5000);
                    bank.withdraw(10000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        motherThread.start();
    }
}

結果:this

媽媽  登陸進入銀行  當前銀行餘額  : 10000.0
爸爸  登陸進入銀行  當前銀行餘額  : 10000.0
爸爸  取款  : 10000.0  當前銀行餘額  : 0.0
媽媽 當前銀行餘額  : 0.0 餘額不夠

媽媽登陸顯示銀行餘額爲10000,可是當她取錢時卻顯示「餘額不足」,產生了數據不一致。spa

2.同一時刻,爸爸或媽媽線程只能有一個可以登陸銀行取款(獲取lock()),另一個線程須要等待, 直到unlock()釋放鎖

public class Bank {
    private static double money = 10000;
    private Lock lock = new ReentrantLock();

    public void login(Thread currentUserThread) {
        lock.lock();//登陸加鎖
        System.out.println(Thread.currentThread().getName() + "  登陸進入銀行" + "  當前銀行餘額  : " + money);

    }

    public void logout() {
        lock.unlock();//退出釋放鎖
        System.out.println(Thread.currentThread().getName() + "  退出銀行");
    }

    public double withdraw(double withdrawMoney) {
        if (this.money < withdrawMoney) {
            System.out.println(Thread.currentThread().getName() + " 當前銀行餘額  : " + this.money + " 餘額不夠");
            return 0;
        }
        this.money -= withdrawMoney;
        System.out.println(Thread.currentThread().getName() + "  取款  : " + withdrawMoney + "  當前銀行餘額  : " + this.money);
        return withdrawMoney;
    }
}
public class TestLock {

    public static void main(String[] args) {
        final Bank bank = new Bank();

        //啓動爸爸線程
        Thread fatherThread = new Thread("爸爸") {
            public void run() {
                try {
                    //爸爸登陸
                    bank.login(Thread.currentThread());
                    //過2秒取10000
                    Thread.sleep(2000);
                    bank.withdraw(10000);
                    //爸爸退出
                    bank.logout();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        fatherThread.start();

        // 啓動媽媽線程
        Thread motherThread = new Thread("媽媽") {
            public void run() {
                try {
                    //媽媽登陸
                    bank.login(Thread.currentThread());
                    //過5秒取 10000
                    Thread.sleep(5000);
                    bank.withdraw(10000);
                    //媽媽退出
                    bank.logout();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        motherThread.start();
    }
}

結果:線程

爸爸  登陸進入銀行  當前銀行餘額  : 10000.0
爸爸  取款  : 10000.0  當前銀行餘額  : 0.0
爸爸  退出銀行
媽媽  登陸進入銀行  當前銀行餘額  : 0.0
媽媽 當前銀行餘額  : 0.0 餘額不夠
媽媽  退出銀行

lock() 和unlock()成對出現,在login(Thread currentUserThread) 登陸方法中調用 lock() ,在 logout()退出方法中調用了unlock()。
也就是說,Lock類的鎖機制容許在不一樣的方法中加鎖和解鎖,而synchronized關鍵字只能在同一個方法中加鎖和解鎖。code

3. 經過tryLock()判斷是否能夠得到鎖, 能得到鎖返回true,不然返回false

public class Bank {
    private static double money = 10000;
    private Lock lock = new ReentrantLock();

    public void login(Thread currentUserThread) {
        //判斷是否已經有線程登陸
        if (!lock.tryLock()) {
            System.out.println(Thread.currentThread().getName() + " 有人已經登陸進入銀行  請稍等");
        } else {
            System.out.println(Thread.currentThread().getName() + "  登陸進入銀行" + "  當前銀行餘額: " + money);
        }

    }

    public void logout() {
        lock.unlock();//退出釋放鎖
        System.out.println(Thread.currentThread().getName() + "  退出銀行");
    }

    public double withdraw(double withdrawMoney) {
        if (this.money < withdrawMoney) {
            System.out.println(Thread.currentThread().getName() + " 當前銀行餘額: " + this.money + " 餘額不夠");
            return 0;
        }
        this.money -= withdrawMoney;
        System.out.println(Thread.currentThread().getName() + "  取款: " + withdrawMoney + "  當前銀行餘額: " + this.money);
        return withdrawMoney;
    }
}
public class TestLock {

    public static void main(String[] args) {
        final Bank bank = new Bank();

        //啓動爸爸線程
        Thread fatherThread = new Thread("爸爸") {
            public void run() {
                try {
                    //爸爸登陸
                    bank.login(Thread.currentThread());
                    //過2秒取10000
                    Thread.sleep(2000);
                    bank.withdraw(10000);
                    //爸爸退出
                    bank.logout();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        fatherThread.start();

        // 啓動媽媽線程
        Thread motherThread = new Thread("媽媽") {
            public void run() {
                //媽媽登陸
                bank.login(Thread.currentThread());
            }
        };
        motherThread.start();
    }
}

結果:blog

媽媽 有人已經登陸進入銀行  請稍等
爸爸  登陸進入銀行  當前銀行餘額: 10000.0
爸爸  取款: 10000.0  當前銀行餘額: 0.0
爸爸  退出銀行

4. 經過tryLock(long time,TimeUnit timeUnit)設置一段時間後從新進入 

public class Bank {
    private static double money = 10000;
    private Lock lock = new ReentrantLock();

    public void login(Thread currentUserThread) {
        //若是登陸不成功,10秒後再從新嘗試得到鎖
        try {
            if (!lock.tryLock(10, TimeUnit.SECONDS)) {
                System.out.println(Thread.currentThread().getName() + " 有人已經登陸進入銀行,請稍等");
            } else {
                System.out.println(Thread.currentThread().getName() + "  登陸進入銀行" + "  當前銀行餘額: " + money);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }

    public void logout() {
        lock.unlock();//退出釋放鎖
        System.out.println(Thread.currentThread().getName() + "  退出銀行");
    }

    public double withdraw(double withdrawMoney) {
        if (this.money < withdrawMoney) {
            System.out.println(Thread.currentThread().getName() + " 當前銀行餘額: " + this.money + " 餘額不夠");
            return 0;
        }
        this.money -= withdrawMoney;
        System.out.println(Thread.currentThread().getName() + "  取款: " + withdrawMoney + "  當前銀行餘額: " + this.money);
        return withdrawMoney;
    }
}
public class TestLock {

    public static void main(String[] args) {
        final Bank bank = new Bank();

        //啓動爸爸線程
        Thread fatherThread = new Thread("爸爸") {
            public void run() {
                try {
                    //爸爸登陸
                    bank.login(Thread.currentThread());
                    //過2秒取10000
                    Thread.sleep(2000);
                    bank.withdraw(10000);
                    //爸爸退出
                    bank.logout();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        fatherThread.start();

        // 啓動媽媽線程
        Thread motherThread = new Thread("媽媽") {
            public void run() {
                //媽媽登陸
                bank.login(Thread.currentThread());
            }
        };
        motherThread.start();
    }
}

結果媽媽10秒後從新嘗試獲得鎖登陸進入了銀行 :接口

爸爸  登陸進入銀行  當前銀行餘額: 10000.0
爸爸  取款: 10000.0  當前銀行餘額: 0.0
爸爸  退出銀行
媽媽  登陸進入銀行  當前銀行餘額: 0.0

5.若是爸爸線程調用 lock() 得到鎖之後,沒有unlock()釋放鎖,媽媽線程將會一直等待產生死鎖,即便調用 interrupt()中斷也沒有做用。 

public class Bank {
    private static double money = 10000;
    private Lock lock = new ReentrantLock();

    public void login(Thread currentUserThread) {
        lock.lock();
        System.out.println(Thread.currentThread().getName() + " 登陸進入銀行" + "  當前銀行餘額  : " + money);
    }

    public void logout() {
        lock.unlock();//退出釋放鎖
        System.out.println(Thread.currentThread().getName() + "  退出銀行");
    }

    public double withdraw(double withdrawMoney) {
        if (this.money < withdrawMoney) {
            System.out.println(Thread.currentThread().getName() + " 當前銀行餘額: " + this.money + " 餘額不夠");
            return 0;
        }
        this.money -= withdrawMoney;
        System.out.println(Thread.currentThread().getName() + "  取款: " + withdrawMoney + "  當前銀行餘額: " + this.money);
        return withdrawMoney;
    }
}
public class TestLock {

    public static void main(String[] args) {
        final Bank bank = new Bank();

        //啓動爸爸線程
        Thread fatherThread = new Thread("爸爸") {
            public void run() {
                try {
                    //爸爸登陸
                    bank.login(Thread.currentThread());
                    //過2秒取10000
                    Thread.sleep(2000);
                    bank.withdraw(10000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        fatherThread.start();

        // 啓動媽媽線程
        Thread motherThread = new Thread("媽媽") {
            public void run() {
                try {
                    Thread.sleep(1000);
                    bank.login(Thread.currentThread());
                } catch (InterruptedException e) {
                    System.out.println(Thread.currentThread().getName()+"  登陸超時被中斷");
                }
            }
        };
        motherThread.start();
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        motherThread.interrupt();
    }
}

結果:get

媽媽線程一直等待爸爸線程釋放鎖,結果形成死鎖。媽媽線程調用motherThread.interrupt(); 中斷也不起作用。it

6. 在登陸得到鎖時,調用lockInterruptibly(),容許中斷等待的線程,使motherThread.interrupt()可用。

public class Bank {
    private static double money = 10000;
    private Lock lock = new ReentrantLock();

    public void login(Thread currentUserThread) throws InterruptedException {
        lock.lockInterruptibly();//運行等待線程被中斷
        System.out.println(Thread.currentThread().getName() + " 登陸進入銀行" + "  當前銀行餘額  : " + money);
    }

    public void logout() {
        lock.unlock();//退出釋放鎖
        System.out.println(Thread.currentThread().getName() + "  退出銀行");
    }

    public double withdraw(double withdrawMoney) {
        if (this.money < withdrawMoney) {
            System.out.println(Thread.currentThread().getName() + " 當前銀行餘額: " + this.money + " 餘額不夠");
            return 0;
        }
        this.money -= withdrawMoney;
        System.out.println(Thread.currentThread().getName() + "  取款: " + withdrawMoney + "  當前銀行餘額: " + this.money);
        return withdrawMoney;
    }
}
public class TestLock {

    public static void main(String[] args) {
        final Bank bank = new Bank();

        //啓動爸爸線程
        Thread fatherThread = new Thread("爸爸") {
            public void run() {
                try {
                    //爸爸登陸
                    bank.login(Thread.currentThread());
                    //過2秒取10000
                    Thread.sleep(2000);
                    bank.withdraw(10000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        fatherThread.start();

        // 啓動 媽媽 線程
        Thread motherThread = new Thread("媽媽") {
            public void run() {
                try {
                    Thread.sleep(1000);
                    bank.login(Thread.currentThread());
                } catch (InterruptedException e) {
                    System.out.println(Thread.currentThread().getName()+"  登陸超時被中斷");
                }
            }
        };
        motherThread.start();
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        motherThread.interrupt();
    }
}

結果:io

爸爸 登陸進入銀行  當前銀行餘額  : 10000.0
媽媽  登陸超時被中斷
爸爸  取款: 10000.0  當前銀行餘額: 0.0

 

 

轉自:http://www.verejava.com/?id=17236703718463

相關文章
相關標籤/搜索