多线程安全(一)

多线程安全

类的线程安全定义

类在多线程环境下运行,始终保持正确的行为,就是线程安全的。

怎么才能做到类的线程安全?

  • 栈封闭

简单的说,就是把变量定义在方法里面,因为方法是线程私有的。

  • 无状态类

线程不安全主要是因为资源竞争,如果类没有属性,就不会有线程安全的问题。

  • 不发布修改属性的方法

定义类属性的时候私有化,并且不发布修改属性的方法,在类外部就不能修改属性,就可以避免线程安全的问题

  • 加锁

synchronized内置锁,或者lock。

  • cas或者线程安全的容器

用cas类,例如AtomicInteger;或者concurrent包的容器。

  • ThreadLocal

threadlocal是保存线程变量副本,每个线程都会保存变量副本。

死锁

资源数量大于1,请求资源的线程大于等于资源数,核心原因就是:请求资源的顺序不一致。

  • 检测死锁的方法
  1. jvm命令
    jps -l : 查看运行的应用id
    jstack #ID: 查看运行应用的线程以及子线程信息,会打印线程持有的资源信息以及请求的资源信息,如果有死锁会检测到。

2.jconsole监控工具或者jprofile

  • 简单的
    同时创建两个线程,一个调用a方法,一个调用b方法,就会发生死锁。
public void a(){
    Synchronized(A){
        Synchronized(B){
            
        }
    }
}

public void b(){
    Synchronized(B){
        Synchronized(A){
            
        }
    }
}
  • 动态的
    转账问题
  1. 线程不安全的代码示例
/**
 * @Auther: allen
 * @Date: 2019/8/22 21:00
 * @Description: 用户类
 */
public class UserAccount {

    //姓名
    private String name;

    //账户余额
    private int account;

    /**
     * 账户锁
     */
    private final Lock lock = new ReentrantLock();

    public UserAccount(String name, int account) {
        this.name = name;
        this.account = account;
    }

    /**
     * 增加账户月
     * @param money
     */
    public void addAccount(int money){
        account = account + money;
    }

    /**
     * 减少账户余额
     * @param money
     * @throws Exception
     */
    public void subAccount(int money) throws Exception{
        if( money > account ){
            throw new Exception("账户余额不足!");
        }
        account = account - money;
    }

    public String getName() {
        return name;
    }

    public int getAccount() {
        return account;
    }

    public Lock getLock() {
        return lock;
    }
}

/**
 * @Auther: allen
 * @Date: 2019/8/22 21:04
 * @Description: 银行转账动作接口
 */
public interface ITransfer {
    /**
     *
     * @param from 转出账户
     * @param to 转入账户
     * @param account 转账金额
     * @throws InterruptedException
     */
    void transfer(UserAccount from, UserAccount to, int account) throws InterruptedException;

}

/**
 * @Auther: allen
 * @Date: 2019/8/22 21:07
 * @Description: 银行转账动作接口实现类
 */
public class TransferAccount implements ITransfer{

    public void transfer(UserAccount from, UserAccount to, int account) throws InterruptedException {
        try {
            from.getLock().lock();
            System.out.println(Thread.currentThread().getName() + " get " + from.getName());
            try {
                to.getLock().lock();
                System.out.println(Thread.currentThread().getName() + " get " + to.getName());

                //处理转账业务
                from.subAccount(account);
                to.addAccount(account);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                to.getLock().unlock();
            }
        } finally {
            from.getLock().unlock();
        }
    }
}

/**
 * @Auther: allen
 * @Date: 2019/8/22 21:16
 * @Description: 测试类:new出两个线程,一个张三转给李四2000,一个张三转给李四2000
 */
public class PayAccountTask {

    private static class PayAccountThread extends Thread{

        //线程名称
        private String name;

        //转出账户
        private UserAccount from;

        //转入账户
        private UserAccount to;

        //转账金额
        private int account;

        //交易操作实现类
        private ITransfer iTransfer;

        public PayAccountThread(String name, UserAccount from, UserAccount to, int account, ITransfer iTransfer) {
            this.name = name;
            this.from = from;
            this.to = to;
            this.account = account;
            this.iTransfer = iTransfer;
        }

        @Override
        public void run() {
            //设置线程名称
            Thread.currentThread().setName(name);

            try {
                System.out.println(from + " start transfer " + account + " $ to " + to);
                //执行转账操作
                iTransfer.transfer(from, to, account);

                System.out.println(from + " transfer finish.");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }


    public static void main(String[] args) {
        UserAccount zhangsan = new UserAccount("zhangsan", 5000);
        UserAccount lisi = new UserAccount("lisi",5000);

        ITransfer transfer = new TransferAccount();

        PayAccountThread zhangsanToLisi = new PayAccountThread("zhangsanToLisi",
                zhangsan,lisi,2000,transfer);
        PayAccountThread lisiToZhangsan = new PayAccountThread("lisiToZhangsan",
                lisi,zhangsan,2000,transfer);

        zhangsanToLisi.start();
        lisiToZhangsan.start();
    }
}

运行结果:

  1. 线程安全的实现
  • 比较对象hash值,先获取hash值大的,保证锁获取顺序
/**
 * @Auther: allen
 * @Date: 2019/8/22 21:36
 * @Description:
 */
public class SafeTransferOperate implements ITransfer {

    //加时锁
    private final Lock tieLock = new ReentrantLock();

    public void transfer(UserAccount from, UserAccount to, int account) throws InterruptedException {

        int fromHash = System.identityHashCode(from);
        int toHash = System.identityHashCode(to);

        //先锁hash值大的,保证获取锁的顺序始终一致
        if(fromHash > toHash){
            try {
                from.getLock().lock();
                System.out.println(Thread.currentThread().getName() + " get " + from.getName());
                try {
                    to.getLock().lock();
                    System.out.println(Thread.currentThread().getName() + " get " + to.getName());

                    //处理转账业务
                    from.subAccount(account);
                    to.addAccount(account);
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    to.getLock().unlock();
                }
            } finally {
                from.getLock().unlock();
            }
        } else if( fromHash < toHash ){
            try {
                to.getLock().lock();
                System.out.println(Thread.currentThread().getName() + " get " + to.getName());
                try {
                    from.getLock().lock();
                    System.out.println(Thread.currentThread().getName() + " get " + from.getName());

                    //处理转账业务
                    from.subAccount(account);
                    to.addAccount(account);
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    to.getLock().unlock();
                }
            } finally {
                from.getLock().unlock();
            }
        }else {
            try {
                tieLock.lock();
                try {
                    from.getLock().lock();
                    try {
                        to.getLock().lock();

                        from.subAccount(account);
                        to.addAccount(account);
                    } catch (Exception e) {
                        e.printStackTrace();
                    } finally {
                        to.getLock().unlock();
                    }
                } finally {
                    from.getLock().unlock();
                }
            } finally {
                tieLock.unlock();
            }
        }
    }
}

执行结果:
[外链图片转存失败(img-02orSVOD-1566482813596)(https://i.loli.net/2019/08/22/ZyimVCjhDwPa78e.png)]

  • 显示锁,一直tryLock
public class SafeOperateLock 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));
    	}
    }
}

其他安全问题

  • 活锁
    尝试拿锁的机制中,发生多个线程之间互相谦让,不断发生拿锁,释放锁的过程。
    解决办法:每个线程休眠随机数,错开拿锁的时间。

  • 线程饥饿
    低优先级的线程,总是拿不到执行时间

性能和思考

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值