Solo  当前访客:1 开始使用

多线程使用(线程安全)


一、死锁演示与解决
1、锁有简单锁,和动态锁,简单锁保证锁的顺序即可,动态锁需要根据某个标准获取锁
2、普通死锁演示

public class NormalDeadLock {

    private static Object valueFirst = new Object();//第一个锁

    private static Object valueSecond = new Object();//第二个锁

    //先拿第一个锁,再拿第二个锁
    private static void fisrtToSecond() throws InterruptedException {
        String threadName = Thread.currentThread().getName();
        synchronized (valueFirst){
            System.out.println(threadName+" get valueSecond");
            Thread.sleep(100);
            synchronized (valueSecond){
                System.out.println(threadName+" get valueFirst");
            }
        }
    }

    //先拿第二个锁,再拿第一个锁
    private static void SecondToFisrt() throws InterruptedException {
        String threadName = Thread.currentThread().getName();
        synchronized (valueSecond){
            System.out.println(threadName+" get valueSecond");
            Thread.sleep(100);
            synchronized (valueFirst){
                System.out.println(threadName+" get valueFirst");
            }
        }
    }

    private static class TestThread extends Thread{

        private String name;

        public TestThread(String name) {
            this.name = name;
        }

        public void run(){
            Thread.currentThread().setName(name);
            try {
                SecondToFisrt();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        Thread.currentThread().setName("TestDeadLock");
        TestThread testThread = new TestThread("SubTestThread");
        testThread.start();
        try {
            fisrtToSecond();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

3、动态锁演示,参数传入顺序不同,锁的顺序也不同,System.identityHashCode(from);不管参数的传入顺序,总是先锁值小的

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;
    }
}
public interface ITransfer {

    void transfer(UserAccount from, UserAccount to, int amount) 
    		throws InterruptedException;
}
public class SafeOperate implements ITransfer {

    private static Object tieLock = new Object();

    @Override
    public void transfer(UserAccount from, UserAccount to, int amount)
            throws InterruptedException {

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

        if(fromHash<toHash){
            synchronized (from){
                System.out.println(Thread.currentThread().getName()+" get "+from.getName());
                Thread.sleep(100);
                synchronized (to){
                    System.out.println(Thread.currentThread().getName()+" get "+to.getName());
                    from.flyMoney(amount);
                    to.addMoney(amount);
                    System.out.println(from);
                    System.out.println(to);
                }
            }
        }else if(toHash<fromHash){
            synchronized (to){
                System.out.println(Thread.currentThread().getName()+" get"+to.getName());
                Thread.sleep(100);
                synchronized (from){
                    System.out.println(Thread.currentThread().getName()+" get"+from.getName());
                    from.flyMoney(amount);
                    to.addMoney(amount);
                    System.out.println(from);
                    System.out.println(to);
                }
            }
        }else{
            synchronized (tieLock){
                synchronized (from){
                    synchronized (to){
                        from.flyMoney(amount);
                        to.addMoney(amount);
                    }
                }
            }
        }
    }
}
    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();

    }
	}

二、CAS演示与问题
1、cas在计算机科学中,比较和交换(Conmpare And Swap)是用于实现多线程同步的原子指令。
比较并且交换: 三个运算符: 一个内存地址V,一个期望的旧值A,一个新值B,操作的时候如果这个地址上存放的值等于这个期望的值A,则将地址上的值赋为新值B,否则不做任何操作。

public class UseAtomicReference {

    public static AtomicReference<UserInfo> atomicUserRef = new
            AtomicReference<UserInfo>();//原子引用类型的实例
    public static void main(String[] args) {
        UserInfo user = new UserInfo("Mark", 15);//要修改的实体的实例
        atomicUserRef.set(user);//用原子引用类型包装
        UserInfo updateUser = new UserInfo("Bill", 17);//要变化成为的新实例
        atomicUserRef.compareAndSet(user, updateUser);
        System.out.println(atomicUserRef.get().getName());
        System.out.println(atomicUserRef.get().getAge());
        //System.out.println(user.getName());
        //System.out.println(user.getAge());
    }
    
    //定义一个实体类
    static class UserInfo {
        private String name;
        private int age;
        public UserInfo(String name, int age) {
            this.name = name;
            this.age = age;
        }
        public String getName() {
            return name;
        }
        public int getAge() {
            return age;
        }
    }

}

2、CAS中的ABA问题与解决
在CAS算法中,需要取出内存中某时刻的数据(由用户完成),在下一时刻比较并替换(由CPU完成,该操作是原子的)。这个时间差中,会导致数据的变化。

使用版本戳解决:AtomicStampedReference
状态戳可类比为时间戳,是一个整数值,每一次修改对象值的同时,也要修改状态戳,从而区分相同对象值的不同状态。当AtomicStampedReference设置对象值时,对象值以及状态戳都必须满足期望值,写入才会成功。

public class UseAtomicStampedReference {

    static AtomicStampedReference<String> asr =
            new AtomicStampedReference("mark", 0);

    public static void main(String[] args) throws InterruptedException {
        final int oldStamp = asr.getStamp();//拿初始版本0
        final String oldReference = asr.getReference();//初始值
        System.out.println(oldReference+"============"+oldStamp);
        Thread rightStampThread = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+":当前变量值:"
                        +oldReference + "-当前版本戳:" + oldStamp + "-"
                  + asr.compareAndSet(oldReference,
                        oldReference + "+Java", oldStamp, oldStamp + 1));
            }
        });

        Thread errorStampThread = new Thread(new Runnable() {
            @Override
            public void run() {
                String reference = asr.getReference();//变量的最新值
                System.out.println(Thread.currentThread().getName()+":当前变量值:"
                        +reference + "-当前版本戳:" + asr.getStamp() + "-"
                        + asr.compareAndSet(reference,
                        reference + "+C", oldStamp, oldStamp + 1));
            }
        });
        rightStampThread.start();
        rightStampThread.join();
        errorStampThread.start();
        errorStampThread.join();

        System.out.println(asr.getReference()+"============"+asr.getStamp());
    }
}

0 0