b2科目四模拟试题多少题驾考考爆了怎么补救
b2科目四模拟试题多少题 驾考考爆了怎么补救

16、Java并发性和多线程

电脑杂谈  发布时间:2019-06-27 21:20:18  来源:网络整理

java线程死锁例子_java线程死锁怎么解决_java多线程死锁

当某个线程请求一个由其他线程持有的锁时,发出请求的线程就会阻塞(线程阻塞在锁池等待队列中),直到获得锁为止。死锁:两个或更多线程阻塞着等待其它处于死锁状态的线程所持有的锁。假如线程a持有一个锁,线程b请求这个锁,由于这个锁已经被持有,所以b会放入阻塞对类中。

例如,如果线程1锁住了A,然后尝试对B进行加锁,同时线程2已经锁住了B,接着尝试对A进行加锁,这时死锁就发生了。线程1永远得不到B,线程2也永远得不到A,并且它们永远也不会知道发生了这样的事情。为了得到彼此的对象(A和B),它们将永远阻塞下去。这种情况就是一个死锁。

该情况如下:

Thread 1  locks A, waits for B
Thread 2  locks B, waits for A

java多线程死锁_java线程死锁例子_java线程死锁怎么解决

这里有一个TreeNode类的例子,它调用了不同实例的synchronized方法:

public class TreeNode {
    TreeNode parent   = null;  
    List children = new ArrayList();
    public synchronized void addChild(TreeNode child){
        if(!this.children.contains(child)) {
            this.children.add(child);
            child.setParentOnly(this);
        }
    }
  
    public synchronized void addChildOnly(TreeNode child){
        if(!this.children.contains(child){
            this.children.add(child);
        }
    }
  
    public synchronized void setParent(TreeNode parent){
        this.parent = parent;
        parent.addChildOnly(this);
    }
    public synchronized void setParentOnly(TreeNode parent){
        this.parent = parent;
    }
}

如果线程1调用parent.addChild(child)方法的同时有另外一个线程2调用child.setParent(parent)方法,两个线程中的parent表示的是同一个对象,child亦然,此时就会发生死锁。下面的伪代码说明了这个过程:

Thread 1: parent.addChild(child); //locks parent
          --> child.setParentOnly(parent);
Thread 2: child.setParent(parent); //locks child
          --> parent.addChildOnly()

java线程死锁怎么解决_java线程死锁例子_java多线程死锁

首先线程1调用parent.addChild(child)。因为addChild()是同步的,所以线程1会对parent对象加锁以不让其它线程访问该对象。

为每一个共享资源创建一个lock对象,当你需要访问该资源时,调用acquire方法来获取锁对象(如果其它线程已经获得了该锁,则当前线程需等待其被释放),待资源访问完后,再调用release方法释放锁:。 // 调用 wait 方法释放对象上的锁并阻止该线程(线程状态为 waitsleepjoin) // 该线程进入到同步对象的等待队列,直到其它线程调用 pulse 使该线程进入到就绪队列中 // 线程进入到就绪队列中才有条件争夺同步对象的所有权 // 如果没有其它线程调用 pulse/pulseall 方法,该线程不可能被执行 monitor.wait(monitor)。具体的实现为当一个线程访问同步块时会在对象头的mark word中存储锁的偏向线程id,后续该线程访问该锁时,就可以简单的检查下mark word是否为偏向锁并且其偏向锁是否指向当前线程。

(3)线程2的run)(方法中同步代码块先获取lock2的对象锁,接着获取lock1的对象锁,当然这时lock1的对象锁已经被线程1锁持有,线程2肯定是要等待线程1释放lock1的对象锁的。 //因为上面尝试先获取l1失败,说明有别的线程在持有l1,那么这次先尝试获取锁l1(只有前面的线程释放了,才可能获取到)。如果调用pthread_mutex_unlock()时有多个线程被mutex对象阻塞,则互斥锁变为可用时调度策略可确定获取该互斥锁的线程。

注意:像上文描述的,这两个线程需要同时调用parent.addChild(child)和child.setParent(parent)方法java多线程死锁,并且是同一个parent对象和同一个child对象,才有可能发生死锁。上面的代码可能运行一段时间才会出现死锁。

java多线程死锁_java线程死锁例子_java线程死锁怎么解决

还有一个需要注意的一个问题就是,如果一个线程获取到了锁并进入的临界区,此时如果因为异常而终止整个线程,如果这个线程在终止的时候无法处理这个被锁住的资源,那其他线程都会因此被阻塞,也就是所谓的死锁。所以每次进入这个 block 时,都会去加一次锁,而从第二次开始,由于锁已经被使用了且没有解锁,所以它需要等待锁被解除,这样就导致了死锁,线程被阻塞住了。还需要注意的就是,锁一般是不可重入的,也就是说一个线程无法获取一个锁两次,第二次试图获取锁的时候这个线程就会被阻塞,这样也就造成了死锁。

更复杂的死锁

死锁可能不止包含2个线程,这让检测死锁变得更加困难。下面是4个线程发生死锁的例子:

Thread 1  locks A, waits for B
Thread 2  locks B, waits for C
Thread 3  locks C, waits for D
Thread 4  locks D, waits for A

java线程死锁例子_java多线程死锁_java线程死锁怎么解决

线程1等待线程2,线程2等待线程3,线程3等待线程4,线程4等待线程1。

的死锁

更加复杂的死锁场景发生在事务中。一个事务可能由多条SQL更新请求组成。当在一个事务中更新一条记录,这条记录就会被锁住避免其他事务的更新请求,直到第一个事务结束。同一个事务中每一个更新请求都可能会锁住一些记录。

当多个事务同时需要对一些相同的记录做更新操作时,就很有可能发生死锁,例如:

Transaction 1, request 1, locks record 1 for update
Transaction 2, request 1, locks record 2 for update
Transaction 1, request 2, tries to lock record 2 for update.
Transaction 2, request 2, tries to lock record 1 for update.

当检查到存在冲突的事务,我们就将一个锁模式为lock_x | lock_gap|lock_x | lock_gap 加入到请求队列中(调用函数lock_rec_enqueue_waiting),这里也会负责去检查死锁。第一个非常好理解,也是最常见的死锁,每个事务执行两条sqljava多线程死锁,分别持有了一把锁,然后加另一把锁,产生死锁。死锁主要发生在有多个依赖锁存在时,会在一个线程试图以与另一个线程相反顺序锁住互斥量时发生.如何避免死锁是使用互斥量应该格。


本文来自电脑杂谈,转载请注明本文网址:
http://www.pc-fly.com/a/jisuanjixue/article-108876-1.html

    相关阅读
      发表评论  请自觉遵守互联网相关的政策法规,严禁发布、暴力、反动的言论

      热点图片
      拼命载入中...