什么是活锁?
多线程环境下,线程在尝试拿锁的机制中,发生多个线程之间互相谦让,不断发生拿锁,释放锁的过程。
模拟银行转账的业务:
实体类:
package com.caojiulu;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
*@author caojiulu
*
*类说明:用户账户的实体类
*/
public class UserAccount {
//private int id;
private final String name;//账户名称
private int money;//账户余额
//显示锁
private final Lock lock = new ReentrantLock();
public Lock getLock() {
return lock;
}
public UserAccount(String name, int amount) {
this.name = name;
this.money = amount;
}
public String getName() {
return name;
}
public int getAmount() {
return money;
}
@Override
public String toString() {
return "UserAccount{" +
"name='" + name + '\'' +
", money=" + money +
'}';
}
//转入资金
public void addMoney(int amount){
money = money + amount;
}
//转出资金
public void flyMoney(int amount){
money = money - amount;
}
}
定义接口:
package com.caojiulu.service;
import com.caojiulu.UserAccount;
/**
*@author caojiulu
*
*类说明:银行转账动作接口
*/
public interface ITransfer {
/**
*
* @param from 转出账户
* @param to 转入账户
* @param amount 转账金额
* @throws InterruptedException
*/
void transfer(UserAccount from, UserAccount to, int amount)
throws InterruptedException;
}
接口实现类:
package com.caojiulu.service;
import java.util.Random;
import com.caojiulu.UserAccount;
import com.caojiulu.SleepTools;
/**
*@author caojiulu
*
*类说明:测试活锁
*/
public class SafeOperateToo implements ITransfer {
@Override
public void transfer(UserAccount from, UserAccount to, int amount)
throws InterruptedException {
Random r = new Random();
while(true) {
if(from.getLock().tryLock()) {
try {
System.out.println(Thread.currentThread().getName()
+" get "+from.getName());
if(to.getLock().tryLock()) {
try {
System.out.println(Thread.currentThread().getName()
+" get "+to.getName());
//两把锁都拿到了
from.flyMoney(amount);
to.addMoney(amount);
break;
}finally {
to.getLock().unlock();
}
}
}finally {
from.getLock().unlock();
}
}
//SleepTools.ms(r.nextInt(10)); //这里注释掉了
}
}
}
测试类:
package com.caojiulu
import com.caojiulu.service.ITransfer;
import com.caojiulu.SafeOperateToo;
/**
*@author caojiulu
*
*类说明:模拟支付公司转账的动作
*/
public class PayCompany {
/*执行转账动作的线程*/
private static class TransferThread extends Thread{
private String name;//线程名字
private UserAccount from;
private UserAccount to;
private int amount;
private ITransfer transfer; //实际的转账动作
public TransferThread(String name, UserAccount from, UserAccount to,
int amount, ITransfer transfer) {
this.name = name;
this.from = from;
this.to = to;
this.amount = amount;
this.transfer = transfer;
}
public void run(){
Thread.currentThread().setName(name);
try {
transfer.transfer(from,to,amount);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
PayCompany payCompany = new PayCompany();
UserAccount zhangsan = new UserAccount("zhangsan",20000);
UserAccount lisi = new UserAccount("lisi",20000);
ITransfer transfer = new SafeOperateToo();
TransferThread zhangsanToLisi = new TransferThread("zhangsanToLisi"
,zhangsan,lisi,2000,transfer);
TransferThread lisiToZhangsan = new TransferThread("lisiToZhangsan"
,lisi,zhangsan,4000,transfer);
zhangsanToLisi.start();
lisiToZhangsan.start();
}
}
运行结果:
这就是因为//SleepTools.ms(r.nextInt(10));发生多个线程之间互相谦让,不断发生拿锁,释放锁的过程。
解决办法:
每个线程休眠随机数,错开拿锁的时间。
线程饥饿:
非公平锁的抢占机制就会可能导致饥饿,线程优先级比较低的时候容易产生,当T1线程占用了资源A,T2,T3,T4。。。都在等待锁的释放,当锁释放的时候,T3抢到了资源,之后又释放锁,T4抢到了资源。。。T2 总是抢不到。