线程活跃性危险

原创 2017年01月03日 15:25:42

发生死锁的原因:
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();
        }
    }
}
版权声明:本文为博主原创文章,未经博主允许不得转载。

相关文章推荐

《Java并发编程实战》第十章 避免活跃性危险 读书笔记

一、死锁 所谓死锁: 是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。 百科百科 当两个以上的运算单元,双方都在等待对方停...

clang中的活跃性分析(续)

clang中活跃性分析续,这篇文章介绍了LiveVariables和RelaxedLiveVariables的区别,并给出了相关示例...

clang中的活跃性分析

引子clang和LLVM都实现了活跃性分析,只是两者的目的不同,一个用于后续静态代码分析,一个用于后续的代码生成以及代码优化。clang活跃性分析实现主要在LiveVariables.h和LiveVa...

2016书单总结--Java并发编程实战--安全性-活跃性示例

2016书单总结–Java并发编程实战–安全性-活跃性示例Java的内存模型支持一次编写,随处运行 每个线程拥有自己的虚拟机栈、局部变量、程序计数器,共享进程中堆上的共享变量,共享方法区(永久内存区...

活跃性(Liveness)

一个并发应用能够及时执行任务的特性称为活跃性,这一节讲述最常见的一种活跃性问题–死锁,并将简单的介绍另外两种活跃性问题,分别为饥饿和活锁。死锁(Deadlock)    死锁描述的是这样一种情景,当...
  • rjcs888
  • rjcs888
  • 2016年07月06日 22:23
  • 502

Java并发笔记三——活跃性和guarded blocks

活跃性和guarded blocks

并发编程实战学习笔记(七)——避免活跃性问题

锁顺序死锁定义试图以不同的顺序去获得相同的锁,就可能会产生死锁解决办法如果所有线程以固定的顺序来获得锁,那么在程序中就不会出现锁顺序死锁问题动态的锁顺序死锁原因锁顺序本身是动态的,无法通过相同的顺序来...

并发危险:解决多线程代码中的 11 个常见的问题

转自:http://msdn.microsoft.com/zh-cn/magazine/cc817398.aspx
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:线程活跃性危险
举报原因:
原因补充:

(最多只允许输入30个字)