(1)同步代码块

为了避免并发造成的问题,java设计了同步机制,推荐对于可能被并发访问的共享资源充当同步监视器,比如上面程序可以使用account作为同步监视器,通过这种方式可以保证同一时刻只有一个线程访问修改共享资源的代码区(临界区)。

public class DrawThread extends Thread {
    private Account account;
    // The amount of money that client want to draw
    private double drawAmount;

    public DrawThread(String name, Account account, double drawAmount) {
        super(name);
        this.account = account;
        this.drawAmount = drawAmount;
    }

    @Override
    public void run() {
        synchronized (account) {
            if (account.getBalance() >= drawAmount) {
                System.out.println(getName() + " draw money:" + drawAmount);
                try {
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                account.setBalance(account.getBalance() - drawAmount);
                System.out.println("The blance is:" + account.getBalance());
            } else {
                System.out.println(getName() + "The blance is not enough!");
            }
        }
    }
}

(2)同步方法

Java语法中也设计了同步方法,同步方法不需要指定同步监视器,其监视器默认为this,即调用该方法的对象。下面代码通过同步方法使Account类的draw()线程安全。

public class Account {
    private String accountNo;
    private double balance;

    public Account() {
    }

    public Account(String accountNo, double balance) {
        this.accountNo = accountNo;
        this.balance = balance;
    }

    public String getAccountNo() {
        return accountNo;
    }

    public void setAccountNo(String accountNo) {
        this.accountNo = accountNo;
    }

    public double getBalance() {
        return balance;
    }

    public void setBalance(double balance) {
        this.balance = balance;
    }

    public synchronized void draw(double drawAmount) {
        if (getBalance() >= drawAmount) {
            System.out.println(Thread.currentThread().getName() + " draw money:" + drawAmount);
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            setBalance(getBalance() - drawAmount);
            System.out.println("The blance is:" + getBalance());
        } else {
            System.out.println(Thread.currentThread().getName() + "The blance is not enough!");
        }
    }
    @Override
    public int hashCode() {
        return accountNo.hashCode();
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == this)
            return true;
        if (obj != null && obj.getClass() == Account.class) {
            Account target = (Account) obj;
            return target.getAccountNo().equals(accountNo);
        }
        return false;
    }
}

public class DrawThread extends Thread {
    private Account account;
    // The amount of money that client want to draw
    private double drawAmount;

    public DrawThread(String name, Account account, double drawAmount) {
        super(name);
        this.account = account;
        this.drawAmount = drawAmount;
    }

    @Override
    public void run() {
        account.draw(drawAmount);
    }
}

可变类的线程安全是通过牺牲其性能换取的,为了尽可能保证其性能,应该只对竞争资源进行同步,同时如果可变类有多线程、单线程两种运行环境,应该提供两种运行版本。