码迷,mamicode.com
首页 > 编程语言 > 详细

线程安全

时间:2018-06-14 18:03:39      阅读:176      评论:0      收藏:0      [点我收藏+]

标签:sleep   获取   例子   first   etl   final   AC   性能   oid   

1.什么是线程安全

一个实例或这一个方法在多线程使用中不会出现任何问题。

2.产生线程不安全的原因

多个线程访问同一相同资源,并且有线程执行了写操作,可能会出现线程安全问题。

2.怎样做到类线程安全

无状态 :没有成员变量的类,也就不存在共享同一资源了。
让类不可变:所有成员变量定义为final
volatile :最轻量,保证类的可见性 但是不能保证原子性。(当这个共享资源改变时会通知其他 读取最新的)
CAS :乐观锁
加锁:悲观锁
ThreadLocal 让每个线程工作内存里都有一个变量拷贝

3.线程安全问题

死锁:多个线程(m) 竞争多个资源(n) n<=m 互不相让

    
        如:线程1 线程2 同时需要资源A B, 1->A , 2->B。现在1需要拿B,但是B被2拿到并锁定,所以1等待2释放B,同理2和A也是这样,互相等待,谁也不放,导致死锁
        
    a.简单顺序锁:
        两个线程代码层级互相拿对方需要的不同资源 
        

简单顺序锁事例

public void getFirst() {
    try {
        synchronized (first) {
            System.out.println(Thread.currentThread().getName()+"getFirst first");
            Thread.sleep(1000);
            synchronized (second) {
                System.out.println(Thread.currentThread().getName()+"getFirst second");
            }
        }
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

public void getSecond() {
    try {
        synchronized (second) {
            System.out.println(Thread.currentThread().getName()+"getFirst second");
            Thread.sleep(1000);
            synchronized (first) {
                System.out.println(Thread.currentThread().getName()+"getFirst first");
            }
        }
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}
    b.动态顺序锁:
        参数传递 导致死锁
        
        
        

动态顺序锁 由于参数传递顺序不同导致死锁

 public void transfer(UserAccounts from, UserAccounts to, int amount) {
        try {
            String threadName = Thread.currentThread().getName();
            synchronized (from) {
                System.out.println(threadName + " get " + from.getName());
                Thread.sleep(100);
                synchronized (to) {
                    System.out.println(threadName + " get " + to.getName());
                    from.flyMoney(amount);
                    to.flyMoney(amount);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
        
        
        

解决死锁的办法:

1.对于简单顺序死锁,可以改变拿锁顺序让他们一致即可。
2.对于动态锁,通过一种方式给他们指定顺序,如判断hashcode来设定拿锁顺序。

private Object tieLock = new Object();//加时锁

public void transfer(UserAccounts from, UserAccounts to, int amount) {
    int fromHash = System.identityHashCode(from);//调用object hash因为哈希方法能被改写
    int toHash = System.identityHashCode(to);
    //始终让小的先加锁 如果相同 则再定义一个锁变量,看谁先获取到这个变量 谁先执行操作
    if (fromHash < toHash) {
        synchronizedSafe(from, to, from, to, amount);
    } else if (toHash < fromHash) {
        synchronizedSafe(to, from, from, to, amount);
    } else {
        synchronized (tieLock) {
            synchronizedSafe(from, to, from, to, amount);
        }
    }
}

 public void synchronizedSafe(UserAccounts first, UserAccounts sencond, UserAccounts from, UserAccounts to, int amount) {
    try {
        String threadName = Thread.currentThread().getName();
        synchronized (first) {
            System.out.println(threadName + " get " + first.getName());
            Thread.sleep(100);
            synchronized (sencond) {
                System.out.println(threadName + " get " + sencond.getName());
                from.flyMoney(amount);
                to.addMoney(amount);
                System.out.println(from);
                System.out.println(to);
            }
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
}
3.采用尝试拿锁方式。 在共享对象中建立一个锁,程序中判断拿到锁再拿下一把锁,如果没有拿到就释放当前锁


//在共享变量的UserAccounts 类中定义一个锁
private final Lock  lock = new ReentrantLock();

//加锁逻辑
public void transfer(UserAccounts from, UserAccounts to, int amount) {
    String threadName = Thread.currentThread().getName();
    Random r = new Random();
    while (true) {
        if (from.getLock().tryLock()) {//先拿from的锁 如果拿到尝试拿to的锁
            System.out.println(threadName + " get " + from.getName());
            try {
                if (to.getLock().tryLock()) {
                //拿到to的锁后执行操作
                    try {
                        System.out.println(threadName + " get " + to.getName());
                        from.flyMoney(amount);
                        to.addMoney(amount);
                        System.out.println(from);
                        System.out.println(to);
                        break;
                    } finally {
                    //发生异常释放锁
                        to.getLock().unlock();
                    }
                }
            } finally {
                from.getLock().unlock(); //to的锁拿不到就释放掉from的锁
            }
        }
    }
    try {
    //随机休眠 拿锁时间过短可能造成活锁
        Thread.sleep(r.nextInt(10));
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}
活锁: 上述例子不加sleep可能造成活锁,线程1 拿A 线程2 拿B ,1尝试拿B,拿不到,释放掉A,2拿A拿不到,释放掉B.循环上述操作。

活锁和死锁最大的区别是,是否释放资源,死锁会一直等待,而活锁会释放掉。

线程饥饿:由于线程优先级太低导致一直无法执行该线程
性能:

线程安全

标签:sleep   获取   例子   first   etl   final   AC   性能   oid   

原文地址:https://www.cnblogs.com/xingluo/p/9183540.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!