线程活跃性危险

原创 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并发编程实战-避免活跃性危险

1,死锁的避免与诊断   1.1,在使用细粒度锁的程序中,可以通过使用一种两阶段策略来检查代码中的死锁:     1.1.1,首先,找出在什么地方将获取多个锁,确保他们在整个程序中获取锁的顺序都保持一...
  • tidu2chengfo
  • tidu2chengfo
  • 2017年07月18日 21:50
  • 123

《java并发编程实战》第10章:避免活跃性危险

10.0 活跃性线程的活跃性到底是什么呢?我们得先知道线程带来的风险: 安全性问题(永远不会发生糟糕的事情) 活跃性问题(某件正确的事情最终会发生) 性能问题(正确的事情要尽快发生) 当某个操作无法继...
  • kingtouch1459
  • kingtouch1459
  • 2016年06月20日 15:34
  • 932

[Java Concurrency in Practice]第十章 避免活跃性危险

避免活跃性危险在安全性和活跃性之间通常存在着某种制衡。我们使用加锁机制来确保线程安全,但如果过度地使用加锁,则可能导致锁顺序死锁。同样,我们使用线程池和信号量来限制对资源的使用,但这些被限制的行为可能...
  • qilixiang012
  • qilixiang012
  • 2015年09月04日 01:18
  • 579

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

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

《Java并发编程实践》笔记5——线程活跃性问题及其解决方法

1.死锁的定义及发生条件: 死锁就像是两个人过独木桥,在桥中间碰到了,谁也不想让步,结果谁也无法通过。 线程A占有锁L时想要获得锁M,而线程B占有锁M并尝试获得锁L,两个线程将永远等待下去,这种情...
  • chjttony
  • chjttony
  • 2015年06月26日 16:41
  • 2928

《Java并发编程实战》读书笔记四:活跃性和性能,死锁和显示锁

博文目录一、活跃性危险:死锁 - 1.锁顺序死锁 - 2.动态锁顺序死锁 - 3.协作对象之间的死锁 - 4.死锁的避免与分析 - 5.其它活跃性危险二、性能和可伸缩性 - 1.使用线程的...
  • jeffleo
  • jeffleo
  • 2016年12月27日 11:45
  • 335

chapter10 避免活跃性危险

1、死锁    JVM在解决死锁方面没有数据库那样强大,无法自动检测和处理死锁。往往只能通过中止并重启才能彻底恢复。    1)顺序死锁        public class LeftRig...
  • novenbersky
  • novenbersky
  • 2013年06月03日 07:21
  • 281

重温《并发编程实战》---避免活跃性危险

1.一些程序是包含死锁风险的,但是并不会立即显示出来,一般死锁发生在高负载的情况下。     2,死锁类别: a.)静态锁顺序死锁:如果两个线程试以不同的顺序获得相同的锁,就会产生锁顺序死锁。 如果每...
  • Mypromise_TFS
  • Mypromise_TFS
  • 2017年05月26日 18:43
  • 126

线程活跃性与性能

@ThreadSafe public class CatchedFactorizer implements Servlet { private BigInteger lastNumber; ...
  • neutrojan
  • neutrojan
  • 2013年02月18日 15:47
  • 922

并发编程实战 - 避免活跃性危险【死锁场景】

顺序死锁与资源死锁 - 我们使用加锁机制来确保线程安全,但是如果过度的使用加锁,则可能导致锁顺序死锁。同样,我们使用线程池和信号量来限制对资源的使用,但这些限制的行为可能导致资源死锁。 在数据库系统...
  • json_it
  • json_it
  • 2018年01月26日 00:42
  • 33
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:线程活跃性危险
举报原因:
原因补充:

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