多线程同步

先来看Bank.java的代码:

package com.example.lib.threads;

import java.lang.reflect.Array;
import java.util.Arrays;
import java.util.concurrent.locks.ReentrantLock;

public class Bank {
    private ReentrantLock bankLock = new ReentrantLock();
    private final double[] accounts;
    public Bank(int n,double initialBalance) {
        accounts = new double[n];
        Arrays.fill(accounts,initialBalance);
    }

    public void transfer(int from,int to,double amount) {
        //bankLock.lock();
        //if (accounts[from] < amount) return;
        try {
            System.out.println(Thread.currentThread());
            accounts[from] -= amount;
            System.out.printf(" %10.2f from %d to %d; ", amount, from, to);
            accounts[to] += amount;
            System.out.printf("Total banlance : %10.2f%n", getTotalBanlance());
        }
        finally {
            //bankLock.unlock();
        }
    }

    public double getTotalBanlance() {
        double sum = 0;
        for (double a : accounts) {
            sum += a;
        }
        return sum;
    }

    public int size() {
        return accounts.length;
    }
}

在上述代码中,没有加锁去保护线程的访问。
在以下输出中:

package com.example.lib.threads;

import sun.rmi.runtime.Log;

public class BankTest {
    public static final int NACCOUNTS = 100;
    public static final double INITIAL_BANLANCE = 1000;
    public static final int MAX_AMOUNT = 1000;
    public static final int DELAY =10;

    public static void main(String[] args) {
        Bank bank = new Bank(NACCOUNTS,INITIAL_BANLANCE);
        for (int i = 0;i < MAX_AMOUNT;i++) {
            int fromAccount = i;
            Runnable r = () -> {
                try {
                    while (true) {
                        int toAccount = (int) (bank.size() * Math.random());
                        System.out.println("toAccount = " + toAccount);
                        double amount = MAX_AMOUNT * Math.random();
                        System.out.println("amount = " + amount);
                        //Log.getLog("w",String.valueOf(amount));
                        bank.transfer(fromAccount,toAccount,amount);
                        Thread.sleep((int) (DELAY * Math.random()));
                    }
                } catch (InterruptedException e) {
            }
        };
            Thread thread = new Thread(r);
            thread.start();
        }
    }
}

输出结果:

     772.04 from 66 to 99; Total banlance :   99014.39
toAccount = 73
amount = 350.2780310321255
Thread[Thread-80,5,main]
     350.28 from 80 to 73; Total banlance :   99014.39
     985.61 from 0 to 97; Total banlance :  100000.00
toAccount = 0
toAccount = 46
toAccount = 3
amount = 139.6185107884077
Thread[Thread-5,5,main]
     139.62 from 5 to 3; Total banlance :  100000.00
toAccount = 28

可以看到,banlance有时是错的。
所以需要加锁:

package com.example.lib.threads;

import java.lang.reflect.Array;
import java.util.Arrays;
import java.util.concurrent.locks.ReentrantLock;

public class Bank {
    private ReentrantLock bankLock = new ReentrantLock();
    private final double[] accounts;
    public Bank(int n,double initialBalance) {
        accounts = new double[n];
        Arrays.fill(accounts,initialBalance);
    }

    public void transfer(int from,int to,double amount) {
        bankLock.lock();
        //if (accounts[from] < amount) return;
        try {
            System.out.println(Thread.currentThread());
            accounts[from] -= amount;
            System.out.printf(" %10.2f from %d to %d; ", amount, from, to);
            accounts[to] += amount;
            System.out.printf("Total banlance : %10.2f%n", getTotalBanlance());
        }
        finally {
            bankLock.unlock();
        }
    }

    public double getTotalBanlance() {
        double sum = 0;
        for (double a : accounts) {
            sum += a;
        }
        return sum;
    }

    public int size() {
        return accounts.length;
    }
}

加锁的方法也很简单:
private ReentrantLock bankLock = new ReentrantLock();//new一个锁
然后在需要保护的操作前注明:bankLock.lock();
等相关操作完成后,再 bankLock.unlock();即可。
需要主要的是:
lock操作需要在try中使用;而unlock操作需要在finally中完成。

上一篇:1035 Password


下一篇:1035. Password (20)-PAT甲级真题