zoukankan      html  css  js  c++  java
  • java 锁!

    问题:如何实现死锁。

    关键:

    1 两个线程ta、tb

    2 两个对象a、b

    3 ta拥有a的锁,同时在这个锁定的过程中,需要b的锁;tb拥有b的锁,同时在这个锁定的过程中,需要a的锁;

     关键的实现难点是3, —— 所以说,死锁也不是那么容易出现的吧。。

    实现方式synchronized、Lock 等等

    死锁例子1  采用了不同类的两个对象。 原理是: 两个线程尝试进入同一个需要对象锁的方法

    package basic.thread;
    
    public class DL {
    
        
        public static void main(String[] args) {
            dla a = new dla();
            a.start();
            
            dlb b = new dlb();
            b.start();
        }
    }
    
    
    class dla extends Thread{
        
        @Override
        public void run() {
            System.out.println("dla-------------");
            Res.getInstace().aa();
        }
    }
    
    class dlb extends Thread{
        
        @Override
        public void run() {
            System.out.println("dlb-----------");
            Res2.getInstace().bb();
            //super.run();
        }
    }
    
    class Res {
        private static Res res = new Res();;
        
        public static Res getInstace() {
            return res;
        }
        
        private Res() {
            
        }
        public synchronized void aa() {
            System.out.println("Res.aa()");
            try {
                Thread.sleep(1000);
                Res2.getInstace().bb();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    
    
    class Res2 {
        private static Res2 res = new Res2();;
        
        public static Res2 getInstace() {
            return res;
        }
        
        private Res2() {
            
        }
    
        public synchronized void bb() {
            System.out.println("Res2.bb()");
            try {
                Thread.sleep(1000);
                Res.getInstace().aa();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    死锁例子2 原理是: 两个线程尝试进入同一个需要对象锁的方法。 这就说明的,只要是一个线程ta正在执行synchronized的方法ma(没有返回,这一点相当关键),下次别的线程进入同一个类的同一方法也好,别的synchronized方法也好,都是需要等待ta在ma的返回,就是要等ta在ma执行完了后才行,简单说就是不能进入。。—— 说起来好拗口。。。

    package basic.thread;
    
    public class DL {
    
        
        public static void main(String[] args) {
            dla a = new dla();
            a.start();
            
            dlb b = new dlb();
            b.start();
        }
    }
    
    
    class dla extends Thread{
        
        @Override
        public void run() {
            System.out.println("dla-------------");
            Res.getInstace().aa();
        }
    }
    
    class dlb extends Thread{
        
        @Override
        public void run() {
            System.out.println("dlb-----------");
            Res2.getInstace().bb();
            //super.run();
        }
    }
    
    class Res {
        private static Res res = new Res();;
        
        public static Res getInstace() {
            return res;
        }
        
        private Res() {
            
        }
        public synchronized void aa() {
            System.out.println("Res.aa()");
                try {
                    Thread.sleep(1000);
                    Res2.getInstace().bbb();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
        }
        
    
        public synchronized void aaa() {
            System.out.println("Res.aaa()");
        }
    }
    
    
    class Res2 {
        private static Res2 res = new Res2();;
        
        public static Res2 getInstace() {
            return res;
        }
        
        private Res2() {
            
        }
    
        public synchronized void bb() {
                System.out.println("Res2.bb() + " + Thread.currentThread().getName());
                try {
                    Thread.sleep(1000);
                    Res.getInstace().aaa();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
        }
    
        public synchronized void bbb() {
    
            System.out.println("Res2.bbb()");
        }
    }

    死锁例子3   采用了同一个类的两个对象。(自旋锁? 这个例子相当不好,因为可能出现死循环! 看来还是两个不同类来实现死锁比较容易)

    package basic.thread;
    
    public class DL {    
        public static void main(String[] args) {
            Res res = new Res();
            Res another = new Res();
            res.setAnother(another);
            another.setAnother(res);
            dlb b1 = new dlb(res);
            dlb b2 = new dlb(another);
            b1.start();
            b2.start();
        }
    }
    
    class dlb extends Thread{
        Res res;
        public dlb(Res res) {
            this.res = res;
        }
        @Override
        public void run() {
            System.out.println("dlb-----------");
            res.aa();
        }
    }
    
    class Res {
        
        Res another;
        public void setAnother(Res another) {
            this.another = another;
        }
        
        public synchronized void aa() {
            System.out.println("Res.aa()");
            try {
                Thread.sleep(1000);
                another.aa();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

     lock trylock

    上例中把 synchronized 改成lock,则发生死锁,一样的效果。但是改成trylock ,则不会发生死锁! 

    package basic.thread;
    
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    public class DL {
        
        public static void main(String[] args) {
            Res res = new Res();
            Res another = new Res();
            res.setAnother(another);
            another.setAnother(res);
            dlb b1 = new dlb(res);
            dlb b2 = new dlb(another);
            b1.start();
            b2.start();
        }
    }
    
    
    class dlb extends Thread{
        Res res;
        public dlb(Res res) {
            this.res = res;
        }
        @Override
        public void run() {
            System.out.println("dlb-----------");
            res.aa();
        }
    }
    
    class Res {
        
        Res another;
        public void setAnother(Res another) {
            this.another = another;
        }
        ReentrantLock lock = new ReentrantLock();
        
        public void aa() {
            lock.lock();
            System.out.println("Res.aa()");
            try {
                Thread.sleep(1000);
                another.aa();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            lock.unlock();
        }
    
    }

    lock时候堆栈为:

    "Thread-1" prio=6 tid=0x022eec00 nid=0x1e74 waiting on condition [0x0487f000..0x0487fae8]
       java.lang.Thread.State: WAITING (parking)
            at sun.misc.Unsafe.park(Native Method)
            - parking to wait for  <0x22e75958> (a java.util.concurrent.locks.ReentrantLock$NonfairSync)
            at java.util.concurrent.locks.LockSupport.park(LockSupport.java:158)
            at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:7
    47)
            at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireQueued(AbstractQueuedSynchronizer.java:778)
            at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(AbstractQueuedSynchronizer.java:1114)
            at java.util.concurrent.locks.ReentrantLock$NonfairSync.lock(ReentrantLock.java:186)
            at java.util.concurrent.locks.ReentrantLock.lock(ReentrantLock.java:262)
            at basic.thread.Res.aa(DL.java:57)
            at basic.thread.Res.aa(DL.java:61)
            at basic.thread.dlb.run(DL.java:44)
    
    "Thread-0" prio=6 tid=0x022ee400 nid=0x2c50 waiting on condition [0x047ef000..0x047efb68]
       java.lang.Thread.State: WAITING (parking)
            at sun.misc.Unsafe.park(Native Method)
            - parking to wait for  <0x22e75a50> (a java.util.concurrent.locks.ReentrantLock$NonfairSync)
            at java.util.concurrent.locks.LockSupport.park(LockSupport.java:158)
            at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:7
    47)
            at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireQueued(AbstractQueuedSynchronizer.java:778)
            at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(AbstractQueuedSynchronizer.java:1114)
            at java.util.concurrent.locks.ReentrantLock$NonfairSync.lock(ReentrantLock.java:186)
            at java.util.concurrent.locks.ReentrantLock.lock(ReentrantLock.java:262)
            at basic.thread.Res.aa(DL.java:57)
            at basic.thread.Res.aa(DL.java:61)
            at basic.thread.dlb.run(DL.java:44)

    ----

    Found one Java-level deadlock:
    =============================
    "Thread-1":
    waiting for ownable synchronizer 0x22e75958, (a java.util.concurrent.locks.ReentrantLock$NonfairSync), —— 注意这里的NonfairSync,非公平同步锁。
    which is held by "Thread-0"
    "Thread-0":
    waiting for ownable synchronizer 0x22e75a50, (a java.util.concurrent.locks.ReentrantLock$NonfairSync),
    which is held by "Thread-1"

    这个时候的死锁,其实跟synchronized时候的锁是不同的,但是效果是一样的。

    打印:

    dlb-----------
    Res.aa()
    dlb-----------
    Res.aa()

    之后就一直‘卡住’了

    tryLock的时候,打印堆栈发现:

    C:UsersAdministrator>jstack 16640
    2014-07-03 15:48:34
    Full thread dump Java HotSpot(TM) Client VM (11.2-b01 mixed mode, sharing):
    
    "DestroyJavaVM" prio=6 tid=0x01edc800 nid=0x3a54 waiting on condition [0x00000000..0x01eafd20]
       java.lang.Thread.State: RUNNABLE
    
    "Thread-1" prio=6 tid=0x01f93800 nid=0x35a8 waiting on condition [0x0480f000..0x0480fce8]
       java.lang.Thread.State: TIMED_WAITING (sleeping)
            at java.lang.Thread.sleep(Native Method)
            at basic.thread.Res.aa(DL.java:60)
            at basic.thread.Res.aa(DL.java:61)
            at basic.thread.Res.aa(DL.java:61)
            at basic.thread.Res.aa(DL.java:61)
            at basic.thread.Res.aa(DL.java:61)
            at basic.thread.Res.aa(DL.java:61)
            at basic.thread.Res.aa(DL.java:61)
            at basic.thread.Res.aa(DL.java:61)
            at basic.thread.Res.aa(DL.java:61)
            at basic.thread.Res.aa(DL.java:61)
            at basic.thread.Res.aa(DL.java:61)
            at basic.thread.dlb.run(DL.java:44)
    
    "Thread-0" prio=6 tid=0x01f90c00 nid=0x2abc waiting on condition [0x0477f000..0x0477fd68]
       java.lang.Thread.State: TIMED_WAITING (sleeping)
            at java.lang.Thread.sleep(Native Method)
            at basic.thread.Res.aa(DL.java:60)
            at basic.thread.Res.aa(DL.java:61)
            at basic.thread.Res.aa(DL.java:61)
            at basic.thread.Res.aa(DL.java:61)
            at basic.thread.Res.aa(DL.java:61)
            at basic.thread.Res.aa(DL.java:61)
            at basic.thread.Res.aa(DL.java:61)
            at basic.thread.Res.aa(DL.java:61)
            at basic.thread.Res.aa(DL.java:61)
            at basic.thread.Res.aa(DL.java:61)
            at basic.thread.Res.aa(DL.java:61)
            at basic.thread.dlb.run(DL.java:44)
    
    "Low Memory Detector" daemon prio=6 tid=0x01f68000 nid=0x48d0 runnable [0x00000000..0x00000000]
       java.lang.Thread.State: RUNNABLE
    
    "CompilerThread0" daemon prio=10 tid=0x01f63c00 nid=0x3a0c waiting on condition [0x00000000..0x045cf714]
       java.lang.Thread.State: RUNNABLE
    
    "Attach Listener" daemon prio=10 tid=0x01f95c00 nid=0x3728 waiting on condition [0x00000000..0x00000000]
       java.lang.Thread.State: RUNNABLE
    
    "Signal Dispatcher" daemon prio=10 tid=0x01f95000 nid=0x365c runnable [0x00000000..0x00000000]
       java.lang.Thread.State: RUNNABLE
    
    "Finalizer" daemon prio=8 tid=0x01f1b800 nid=0x258c in Object.wait() [0x01cdf000..0x01cdfc68]
       java.lang.Thread.State: WAITING (on object monitor)
            at java.lang.Object.wait(Native Method)
            - waiting on <0x22960b28> (a java.lang.ref.ReferenceQueue$Lock)
            at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:116)
            - locked <0x22960b28> (a java.lang.ref.ReferenceQueue$Lock)
            at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:132)
            at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:159)
    
    "Reference Handler" daemon prio=10 tid=0x01f17000 nid=0x4584 in Object.wait() [0x003cf000..0x003cfce8]
       java.lang.Thread.State: WAITING (on object monitor)
            at java.lang.Object.wait(Native Method)
            - waiting on <0x22960a30> (a java.lang.ref.Reference$Lock)
            at java.lang.Object.wait(Object.java:485)
            at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:116)
            - locked <0x22960a30> (a java.lang.ref.Reference$Lock)
    
    "VM Thread" prio=10 tid=0x01f15800 nid=0x4388 runnable
    
    "VM Periodic Task Thread" prio=10 tid=0x01f7f000 nid=0x4154 waiting on condition
    
    JNI global references: 596


    ———— 这个时候并没有发生死锁,但是,有两个线程确确实实是阻塞了。 就效果来说一样的,但是原理不同。
    而且后台打印不同:
    
    

    dlb-----------
    Res.aa()
    dlb-----------
    Res.aa()
    Res.aa()
    Res.aa()
    Res.aa()
    Res.aa()
    Res.aa()
    Res.aa()
    Res.aa()

    ... 
    一直有打印,每隔一秒钟一次———— 死循环!!!!!
    。。
    说明,线程有一个轮询机制,一直在等待另一个线程释放锁,但是它又不释放,如是就一直“卡”在了那里。 —— 卡,其实就相当于了‘锁’!

     好吧, 其实上面的例子有问题,因为,里面出现了一个死循环。。 而且,没有判断trylock 的返回值。 

    使用这个例子吧,但是,惊人的发现每次的结果不一样!: 有时候很快就两个end了,有时候一个end,有时候根本没有end——每次的tryLock都能返回true ,太奇怪了吧,如上例一样一直打印

    package basic.thread;
    
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    public class DL {
    
        
        public static void main(String[] args) {
            Res res = new Res();
            Res another = new Res();
            res.setAnother(another);
            another.setAnother(res);
            dlb b1 = new dlb(res);
            dlb b2 = new dlb(another);
            b1.start();
            b2.start();
        }
    }
    
    class dlb extends Thread{
        Res res;
        public dlb(Res res) {
            this.res = res;
        }
        @Override
        public void run() {
            System.out.println("dlb-----------");
            res.aa();
        }
    }
    
    class Res {
        
        Res another;
        public void setAnother(Res another) {
            this.another = another;
        }
        ReentrantLock lock = new ReentrantLock();
        
        public void aa() {
            boolean boo = lock.tryLock();
            if(boo){
                System.out.println("Res.aa()aaaaaaaa");
                try {
                    Thread.sleep(1000);
                    another.aa();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                lock.unlock();
            } else {
                System.out.println("Res.aa() end !! ");
            }
        }
    }

    好吧,其实上面的例子都不太好。 

    总结:

    1 一般使用lock即可。

    2 使用tryLock的话,需要判断其返回值,否则就达不到锁的效果。 应该如下,注意 unlock 必须要放在if里面

            boolean tryLock = lock.tryLock();
            System.out.println(" trylock : " + tryLock);
            if(tryLock) {
                   do sth.
                lock.unlock(); ---- unlock 必须要放在if里面,否则如果tryLock为false的话,报错java.lang.IllegalMonitorStateException。 因为如果false,则lock 的holder 并非当前线程。。。  很明显的道理吧!
            }    

    3 tryLock方法无阻塞,立即返回(true/false), 所以,使用tryLock是不会造成死锁的!!! 但是,正因为如此,可能到不到我们想要的目的,比如,有时候时候我就是想锁定某个资源,否则就不能继续操作。 ———— 所以说tryLock适合无阻塞的场景

     4 java-tryLock可轮询可中断可定时的锁 —— 好奇怪的名字。。

    参考http://aguang520.iteye.com/blog/815575 http://zfzaizheli.iteye.com/blog/1394823 (表示看不懂。。)等

     5  注意到 ReentrantLock 的 lock() 返回值为void、 而tryLock返回boolean。

    6 synchronized不能和lock等混用。 synchronized的用法见我另外一篇博客

    7 ReentrantLock 是接口Lock的实现,其功能、底层原理远比synchronized复杂,

    8 synchronized 可以加在方法前, 代码块前。 而且可以是静态的!

    9 ReentrantReadWriteLock 的用法一般是: 

      ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
      ReadLock readLock = lock.readLock(); —— 用来对get,即查询操作加锁 
      WriteLock writeLock = lock.writeLock();—— 用来对add/update/delete,即新增修改删除操作加锁

    —— 问题是,有必要分的这么细吗? 直接一个ReentrantLock 不就行了吗?

    参见 http://www.cnblogs.com/sunwei2012/archive/2010/10/09/1846358.html

     http://www.cnblogs.com/GnagWang/archive/2011/02/27/1966606.html ———— 写得非常非常非常详细。。

    http://www.cnblogs.com/violinn/archive/2013/05/21/multiThread1.html  非常非常非常详细而且全面的关于java锁、同步、多线程的介绍

    另外注意区别FileLock 类中也有 lock 、tryLock 方法,不过,当然,用法不同。

  • 相关阅读:
    为什么要使用MQ消息中间件?
    趣图:这是招聘超神级别的程序员?
    基于redis的分布式锁的分析与实践
    我的java问题排查工具单
    趣图:看到这些窗户,我想插上几根网线
    SpringBoot+MyBatis+MySQL读写分离(实例)
    IntelliJ IDEA 常用快捷键
    ex3 多分类和神经网络
    9、神经网络的学习
    神经网络--反向传播详细推导过程
  • 原文地址:https://www.cnblogs.com/FlyAway2013/p/3840689.html
Copyright © 2011-2022 走看看