关闭

线程活跃性危险

36人阅读 评论(0) 收藏 举报
分类:

发生死锁的原因:
1.1 锁顺序死锁:

synchronized(left){
    synchronized(right){
    ...
    }
}

synchronized(right){
    synchronized(left){
    ...
    }
}

1.2 动态的锁顺序死锁 (获取锁的顺序由参数决定):

func(A,B){
    synchronized(A){
        synchronized(B){
        }
    }
}

1.3 顺序死锁的解决方法
规定获取锁的顺序

2.在协作对象间发生死锁
在持有锁的情况下调用了某个外部方法,需要警惕死锁

2.1开放调用
即调用某个方法时不需要持有锁

public void setLocation(Point location){
    synchronized(this){
        this.location = location;
    }
    //notifyAvailable是需要加锁的
    dispatcher.notifyAvailable(this);
}

3.资源死锁
一般出现在资源池为空(资源池一般由信号量实现)的阻塞行为
Ex: A持有数据库D1连接,等待D2连接;B持有D2连接,等待D1连接
线程饥饿死锁
Ex: 单线程Executor中一个任务提交另一个任务并等待执行完成

死锁的避免
1.使用定时锁:显示使用Lock类中tryLock代替内置锁

与上文毫无关系的
Semaphore用法

Semaphore a = new Semaphore(0);
a.acquire();    //由于a中没有许可,所以会被阻塞
a.release();    //a中的可用许可变为1
a.release();    //a中的可用许可变为2

Semaphore acquire与release并没有先后使用关系。单纯的,acquire 执行 -1,release 执行 +1

并发程序单元测试
1. 单元测试 —— 测试阻塞状态

//中断状态单元测试
/**BoundedBuffer是一个基于信号量的有界缓存。《并行编程实战》P205
**/
class BoundedBufferTest extends TestCase{
    public void testTakeBlocksWhenEmpty(){
        final BoundedBuffer<Integer> bb = new BoundedBuffer<Integer>(10);
        Thread taker = new Thread(){
            public void run(){
                try{
                    //按道理会被阻塞
                    int unused = bb.take();
                    //如果执行到这里算错误
                    fail();
                }catch(InterruptedException e){
                    //检测到主线程传来的中断
                    System.out.println("I am interrupted");
                }
            }
        };
    try{
        taker.start();
        //等待一段时间
        Thread.sleep(LOCKUP_DETECT_TIMEOUT);
        //中断线程
        taker.interrupted();
        //为防止意外,比如线程无法响应中断,或者。。。自己恢复了中断
        taker.join(LOCKUP_DETECT_TIMEOUT);
        assertFalse(taker.isAlive());
    }catch(Exception unexpected){
        fail();
    }
    }
}

LOCK
1. synchronized 与 ReentrantLock 之间抉择:
ReentrantLock可以作为一种高级工具,这些功能包括:可定时的,可轮询的与可中断的锁获取操作,公平队列,以及非快结构的锁。否则还是应该优先使用synchronized

2.轮询+定时锁
使用轮询锁时,它会释放已获得的锁,然后尝试重新获取所有锁。

public boolean transferMoney(Account fromAcct,Account toAcct,long timeout,TimeUnit unit){
long fixedDelay;    //在重新获取锁时,采用 固定时间+随机时间,从而降低发生活锁的可能性
long randMod;
long stopTime = System.nanoTime + unit.toNanos(timeout);    //定时时间

while(true){
    if(fromAcct.lock.tryLock()){
        try{
            if(toAcct.locl.tryLock()){
                try{
                    ...
                    return true;  //即使已经返回true,finally仍然会执行。
                }finally{
                    toAcct.lock.unlock();
                }
            }
        }finally{
            fromAcct.lock.unlock();
        }
    }
    if(System.nanoTime > stopTime)
        return false;   //超时取消
    //过一定的时间之后再获取锁
    TimeUnit.NANOSECONDS.sleep(fixedDelay + randMod);
}
}

3.lock 自带的定时锁

if(!lock.tryLock(timeout,TimeUnit.NANOSECONDS)){
        return false; //`在规定时间内未获取锁
}

4.可中断的锁
lock.lockInterruptibly(); 该方法获取锁时,如果处于阻塞状态时,线程被中断,就会响应中断。
而lock.lock(); 在阻塞状态时,即使线程被中断,仍然会等待获取锁,直到获取锁之后再响应中断。

5.读写锁
ReentrantReadWriteLock 特性:

  1. 可重入的加锁定义
  2. 写线程可降级为读线程,但读线程不能升级为写线程
  3. 同时只能有一个写线程,但允许多个读线程。
//用读写锁封装的map
public class ReadWriteMap<K,V>{
    private final Map<K,V> map;
    private final ReadWriteLock lock = new ReentrantReadWriteLock();
    private final Lock r = lock.readLock();
    private final Lock w = lock.writeLock();

    public ReadWriteMap(Map<K,V> map){
        this.map = map;
    }

    public V put(K key,V value){
        w.lock(); //写线程加锁
        try{
            return map.put(key,value);
        }finally{
            w.unlock();
        }
    }

    public V get(Object key){
        r.lock();
        try{
            return map.get(key);
        }finally{
            r.unlock();
        }
    }
}
0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:677次
    • 积分:103
    • 等级:
    • 排名:千里之外
    • 原创:10篇
    • 转载:0篇
    • 译文:0篇
    • 评论:0条
    文章分类