多个线程访问同一个数据时,容易出现线程安全问题。如:经典的银行取钱问题。
为阻止两条线程对同一个共享资源的并发访问引起的问题,
可以:
1、使用同步监视器的通用方法就是同步代码块。
Synchronized(obj){
//同步代码块
}
其中obj就是同步监视器,必须要获得对同步监视器的锁定才能执行同步代码块。
2、同步方法。
就是使用Synchronized修饰的方法,同步方法无需显示指定同步监视器,同步方法的同步监视器是this。
import java.util.concurrent.locks.ReentrantLock;
public class Account {
private String accountNo;
private double balance;
private final ReentrantLock lock = new ReentrantLock();//可重入锁
public Account(String accountNo, double balance) {
this.accountNo = accountNo;
this.balance = balance;
}
public void draw(double drawAmount) // public synchronized void draw(doubledrawAmount) 同步方法
{
lock.lock();
try {
if (balance >= drawAmount) {
try {
Thread.sleep(1);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
balance -= drawAmount;
System.out.println("余额为啊:" + balance);
} else {
System.out.println("余额不足啊。");
}
} finally {
lock.unlock();
}
}
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 int hashCode() {
return accountNo.hashCode();
}
public boolean equals(Object obj) {
if (obj != null && obj.getClass() == Account.class) {
Account target = (Account) obj;
return target.getAccountNo().equals(accountNo);
}
return false;
}
}
package ex1;
public class DrawThread extends Thread
{
private Account account;
private double drawAmount;
public DrawThread(String name, Account account, double drawAmount)
{
super(name);
this.account = account;
this.drawAmount = drawAmount;
}
//当多个线程同时修改一个共享数据时,涉及数据安全问题。
public void run()
{
account.draw(drawAmount);//直接调用account对象的draw方法执行,该方法用synchronized修饰,变成同步方法
/*synchronized(account)//使用account作为同步监视器的方法用
{
if (account.getBalance() >=drawAmount)
{
try
{
Thread.sleep(1);
} catch (InterruptedException ex)
{
ex.printStackTrace();
}
account.setBalance(account.getBalance() - drawAmount);
System.out.println("取钱成功。余额:" +account.getBalance());
} else
{
System.out.println("余额不足。");
}
}*/
}
}
3、同步锁(Lock)
Lock提供比Synchronized更广泛的锁机制,它是控制多个线程对共享资源进行访问的工具。锁对共享资源的独占访问。每次只能有一个线程加锁。常用的ReentrantLock(可重入锁)。
class X {
// 定义锁对象
private final ReentrantLock lock = new ReentrantLock();
// 定义需要保证线程安全的方法
public void m() {
// 加锁
lock.lock();
try {
// 需要保证安全的代码
}
// 使用finally块来保证释放锁
finally {
lock.unlock();
}
}
}
4、死锁
当两个线程相互等待对方释放同步监视器时就会发生死锁,一旦发生死锁程序不会发生任何异常和提示,只是出于阻塞状态,无法继续。