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:\Users\Administrator>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 方法,不过,当然,用法不同。

java 锁!,布布扣,bubuko.com

时间: 2024-08-27 15:46:16

java 锁!的相关文章

java锁的种类以及辨析(转载)

java锁的种类以及辨析(一):自旋锁 锁作为并发共享数据,保证一致性的工具,在JAVA平台有多种实现(如 synchronized 和 ReentrantLock等等 ) .这些已经写好提供的锁为我们开发提供了便利,但是锁的具体性质以及类型却很少被提及.本系列文章将分析JAVA下常见的锁名称以及特性,为大家答疑解惑. 1.自旋锁 自旋锁是采用让当前线程不停地的在循环体内执行实现的,当循环的条件被其他线程改变时 才能进入临界区.如下 01 public class SpinLock { 02  

java锁和同步

Java 语言设计中的一大创新就是:第一个把跨平台线程模型和锁模型应用到语言中去,Java 语言包括了跨线程的关键字synchronized 和 volatile,使用关键字和java类库就能够简单的实现线程间的同步.在简化与平台无关的并发程序开发时,它没有使并发程序的编写工作变得繁琐,反而使它变得更容易了. 在这一章,我们详细介绍锁的技术和概念,java中提供了两种锁,一个是使用关键字的锁,还有一种类库提供的锁. synchronized关键字锁 synchronized关键字能够作为函数的修

java 锁2

并发,其实是多线程才有的场景... java 多线程? 锁? 现在看来,即使已经工作了4.5年,这仍然不是一个简单的问题. 其实java 本身有提供锁的机制. 比如 Object对象的 wait .notify 方法.synchronized 的原理不过是直接调用对应的对象的 wait方法罢了! 看tomcat 源码的时候,多线程的地方就是直接用到了 wait .notify等方法 —— 这些方法真高级, 一般哪里会用得着???!!! 1 synchronized 其实这个算是容易学的东西了,相

Java 锁的学习

个人学习整理,所有资料均来源于网络,非原创. 死锁的四个必要条件:互斥条件(Mutual exclusion):资源不能被共享,只能由一个进程使用.请求与保持条件(Hold and wait):已经得到资源的进程可以再次申请新的资源.一个进程因请求资源而阻塞时,对已获得的资源保持不放.非剥夺条件(No pre-emption):已经分配的资源不能从相应的进程中被强制地剥夺.循环等待条件(Circular wait):系统中若干进程组成环路,该环路中每个进程都在等待相邻进程正占用的资源. 不难看出

java锁Lock的应用

package jun.lock; public class BankCard { protected String cardId;//银行卡号 protected int balance = 8000;//余额 public String getCardId() { return cardId; } public void setCardId(String cardId) { this.cardId = cardId; } public int getBalance() { return ba

有关Java 锁原理

锁 锁是用来锁东西的,让别人打不开也看不到!在线程中,用这个“锁”隐喻来说明一个线程在“操作”一个目标(如一个变量)的时候,如果变量是被锁住的,那么其他线程就对这个目标既“操作”不了(挂起)也无法看到目标的内容!对Java并发包,锁的实现基本在java.util.concurrent.locks包中,说“基本”是因为,在java.util.concurrent中还有CountDownLatch(可以看成带计数器的锁),CyclicBarrier,Semaphore(类似于信号量,但是也可以看成一

Java锁(一)之内存模型

想要了解Java锁机制.引发的线程安全问题以及数据一致性问题,有必要了解内存模型,机理机制了解清楚了,这些问题也就应声而解了. 一.主内存和工作内存 Java内存模型分为主内存和工作内存,所有的变量都存储在主内存中.每条线程还有自己的工作内存,线程的工作内存中保存了被该线程使用到变量的主内存副本拷贝,线程对变量的所有操作都必须在工作内存中进行,而不能直接读写主内存中的变量.不同的线程之间也无法直接访问对方工作内存中的变量,线程间变量值的传递均需要主内存来完成. 二.线程.工作内存和主内存 下面是

java 锁3

Object 有提供 三个重构的wait.一个notify.一个notifAll . 都是线程或者说多线程,相关的. Wait()方法,使当前获得指定对象锁的线程阻塞并释放锁. ————我很好奇,为什么要阻塞,然后又释放呢?Notify.NotifyAll 使当前没有获得指定对象锁的线程唤醒. 方法调用一定要处于synchronized关键字包含的代码中,即锁控制的区域. package test; public class ObjectLock { public static void mai

java 锁4

关于锁的分类 及 锁的灵活使用: 参见 http://blog.csdn.net/qaz13177_58_/article/details/21543515  有几句话说得相当不错: 锁的分类 : 同步分为类级别和对象级别,分别对应着类锁和对象锁.类锁是每个类只有一个,如果static的方法被synchronized关键字修饰,则在这个方法被执行前必须获得类锁:对象锁类同. 唤醒的含义: 只要是在synchronied块中的代码,没有对象锁是寸步难行的.其实唤醒一个线程就是重新允许这个线程去获得