Java多线程—-java 对象锁 – ymeng

在并发环境下,解决共享资源冲突问题时,可以考虑使用锁机制。

1. 对象锁

所有对象都自动含有单一的锁。 JVM负责跟踪对象被加锁的次数。如果一个对象被解锁,其计数变为0。在任务(线程)第一次给对象加锁的时候,计数变为1。每当这个相同的任务(线程)在此对象上获得锁时,计数会递增。 只有首先获得锁的任务(线程)才能继续获取该对象上的多个锁。 每当任务离开一个synchronized方法,计数递减,当计数为0的时候,锁被完全释放,此时别的任务就可以使用此资源。

2. 类锁

对于同步静态方法/静态变量互斥体,由于一个class不论被实例化多少次,其中的静态方法和静态变量在内存中都只由一份。所以,一旦一个静态的方法被申明为synchronized。此类所有的实例化对象在调用此方法,共用同一把锁,我们称之为类锁。一旦一个静态变量被作为synchronized block的互斥体。进入此同步区域时,都要先获得此静态变量的对象锁。

由上述同步静态方法引申出一个概念,那就是类锁。其实系统中并不存在什么类锁。当一个同步静态方法被调用时,系统获取的其实就是代表该类的类对象的对象锁 可以尝试用以下方式获取类锁

synchronized (xxx.class) {...}synchronized (Class.forName("xxx")) {...}

若要同时获取两种锁,同时获取类锁和对象锁是允许的,并不会产生任何问题,但使用类锁时一定要注意,一旦产生类锁的嵌套获取的话,就会产生死锁,因为每个class在内存中都只能生成一个Class实例对象。3. synchronized同步块3.1. 同步到单一对象锁

当使用同步块时,如果方法下的同步块都同步到一个对象上的锁,则所有的任务(线程)只能互斥的进入这些同步块。 Resource1.java演示了三个线程(包括main线程)试图进入某个类的三个不同的方法的同步块中,虽然这些同步块处在不同的方法中,但由于是同步到同一个对象(当前对象 synchronized (this)),所以对它们的方法依然是互斥的。Resource1.java

package com.zj.lock;import java.util.concurrent.TimeUnit; public class Resource1 {    public void f() {       // other operations should not be locked...       System.out.println(Thread.currentThread().getName()              + ":not synchronized in f()");       synchronized (this) {           for (int i = 0; i < 5; i++) {              System.out.println(Thread.currentThread().getName()                     + ":synchronized in f()");              try {                  TimeUnit.SECONDS.sleep(3);              } catch (InterruptedException e) {                  e.printStackTrace();              }           }       }    }     public void g() {       // other operations should not be locked...       System.out.println(Thread.currentThread().getName()              + ":not synchronized in g()");       synchronized (this) {           for (int i = 0; i < 5; i++) {              System.out.println(Thread.currentThread().getName()                     + ":synchronized in g()");              try {                  TimeUnit.SECONDS.sleep(3);              } catch (InterruptedException e) {                  e.printStackTrace();              }           }       }    }     public void h() {       // other operations should not be locked...       System.out.println(Thread.currentThread().getName()              + ":not synchronized in h()");       synchronized (this) {           for (int i = 0; i < 5; i++) {              System.out.println(Thread.currentThread().getName()                     + ":synchronized in h()");              try {                  TimeUnit.SECONDS.sleep(3);              } catch (InterruptedException e) {                  e.printStackTrace();              }           }       }    }     public static void main(String[] args) {       final Resource1 rs = new Resource1();        new Thread() {           public void run() {              rs.f();           }       }.start();        new Thread() {           public void run() {              rs.g();           }       }.start();        rs.h();    }}

结果:Thread-0:not synchronized in f()Thread-0:synchronized in f()main:not synchronized in h()Thread-1:not synchronized in g()Thread-0:synchronized in f()Thread-0:synchronized in f()Thread-0:synchronized in f()Thread-0:synchronized in f()Thread-1:synchronized in g()Thread-1:synchronized in g()Thread-1:synchronized in g()Thread-1:synchronized in g()Thread-1:synchronized in g()main:synchronized in h()main:synchronized in h()main:synchronized in h()main:synchronized in h()main:synchronized in h()3.2. 同步到多个对象锁

Resource1.java演示了三个线程(包括main线程)试图进入某个类的三个不同的方法的同步块中,这些同步块处在不同的方法中,并且是同步到三个不同的对象(synchronized (this),synchronized(syncObject1),synchronized (syncObject2)),所以对它们的方法中的临界资源访问是独立的。Resource2.java

package com.zj.lock;import java.util.concurrent.TimeUnit; public class Resource2 {    private Object syncObject1 = new Object();    private Object syncObject2 = new Object();     public void f() {       // other operations should not be locked...       System.out.println(Thread.currentThread().getName()              + ":not synchronized in f()");       synchronized (this) {           for (int i = 0; i < 5; i++) {              System.out.println(Thread.currentThread().getName()                     + ":synchronized in f()");              try {                  TimeUnit.SECONDS.sleep(3);              } catch (InterruptedException e) {                  e.printStackTrace();              }           }       }    }     public void g() {       // other operations should not be locked...       System.out.println(Thread.currentThread().getName()              + ":not synchronized in g()");       synchronized (syncObject1) {           for (int i = 0; i < 5; i++) {              System.out.println(Thread.currentThread().getName()                     + ":synchronized in g()");              try {                  TimeUnit.SECONDS.sleep(3);              } catch (InterruptedException e) {                  e.printStackTrace();              }           }       }    }     public void h() {       // other operations should not be locked...       System.out.println(Thread.currentThread().getName()              + ":not synchronized in h()");       synchronized (syncObject2) {           for (int i = 0; i < 5; i++) {              System.out.println(Thread.currentThread().getName()                     + ":synchronized in h()");              try {                  TimeUnit.SECONDS.sleep(3);              } catch (InterruptedException e) {                  e.printStackTrace();              }           }       }    }     public static void main(String[] args) {       final Resource2 rs = new Resource2();        new Thread() {           public void run() {              rs.f();           }       }.start();        new Thread() {           public void run() {              rs.g();           }       }.start();        rs.h();    }}

结果:Thread-0:not synchronized in f()Thread-0:synchronized in f()main:not synchronized in h()main:synchronized in h()Thread-1:not synchronized in g()Thread-1:synchronized in g()Thread-0:synchronized in f()main:synchronized in h()Thread-1:synchronized in g()Thread-0:synchronized in f()main:synchronized in h()Thread-1:synchronized in g()Thread-0:synchronized in f()main:synchronized in h()Thread-1:synchronized in g()Thread-0:synchronized in f()main:synchronized in h()Thread-1:synchronized in g()4. Lock对象锁

除了使用synchronized外,还可以使用Lock对象来创建临界区。Resource3.java的演示效果同Resource1.java;Resource4.java的演示效果同Resource2.java。Resource3.java

package com.zj.lock;import java.util.concurrent.TimeUnit;import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock; public class Resource3 {    private Lock lock = new ReentrantLock();     public void f() {       // other operations should not be locked...       System.out.println(Thread.currentThread().getName()              + ":not synchronized in f()");       lock.lock();       try {           for (int i = 0; i < 5; i++) {              System.out.println(Thread.currentThread().getName()                     + ":synchronized in f()");              try {                  TimeUnit.SECONDS.sleep(3);              } catch (InterruptedException e) {                  e.printStackTrace();              }           }       } finally {           lock.unlock();       }    }     public void g() {       // other operations should not be locked...       System.out.println(Thread.currentThread().getName()              + ":not synchronized in g()");       lock.lock();       try {           for (int i = 0; i < 5; i++) {              System.out.println(Thread.currentThread().getName()                     + ":synchronized in g()");              try {                  TimeUnit.SECONDS.sleep(3);              } catch (InterruptedException e) {                  e.printStackTrace();              }           }       } finally {           lock.unlock();       }    }     public void h() {       // other operations should not be locked...       System.out.println(Thread.currentThread().getName()              + ":not synchronized in h()");       lock.lock();       try {           for (int i = 0; i < 5; i++) {              System.out.println(Thread.currentThread().getName()                     + ":synchronized in h()");              try {                  TimeUnit.SECONDS.sleep(3);              } catch (InterruptedException e) {                  e.printStackTrace();              }           }       } finally {           lock.unlock();       }    }     public static void main(String[] args) {       final Resource3 rs = new Resource3();        new Thread() {           public void run() {              rs.f();           }       }.start();        new Thread() {           public void run() {              rs.g();           }       }.start();        rs.h();    }

结果:Thread-0:not synchronized in f()Thread-0:synchronized in f()main:not synchronized in h()Thread-1:not synchronized in g()Thread-0:synchronized in f()Thread-0:synchronized in f()Thread-0:synchronized in f()Thread-0:synchronized in f()main:synchronized in h()main:synchronized in h()main:synchronized in h()main:synchronized in h()main:synchronized in h()Thread-1:synchronized in g()Thread-1:synchronized in g()Thread-1:synchronized in g()Thread-1:synchronized in g()Thread-1:synchronized in g()

Resource4.java

package com.zj.lock;import java.util.concurrent.TimeUnit;import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock; public class Resource4 {    private Lock lock1 = new ReentrantLock();    private Lock lock2 = new ReentrantLock();    private Lock lock3 = new ReentrantLock();     public void f() {       // other operations should not be locked...       System.out.println(Thread.currentThread().getName()              + ":not synchronized in f()");       lock1.lock();       try {           for (int i = 0; i < 5; i++) {              System.out.println(Thread.currentThread().getName()                     + ":synchronized in f()");              try {                  TimeUnit.SECONDS.sleep(3);              } catch (InterruptedException e) {                  e.printStackTrace();              }           }       } finally {           lock1.unlock();       }    }     public void g() {       // other operations should not be locked...       System.out.println(Thread.currentThread().getName()              + ":not synchronized in g()");       lock2.lock();       try {           for (int i = 0; i < 5; i++) {              System.out.println(Thread.currentThread().getName()                     + ":synchronized in g()");              try {                  TimeUnit.SECONDS.sleep(3);              } catch (InterruptedException e) {                  e.printStackTrace();              }           }       } finally {           lock2.unlock();       }    }     public void h() {       // other operations should not be locked...       System.out.println(Thread.currentThread().getName()              + ":not synchronized in h()");       lock3.lock();       try {           for (int i = 0; i < 5; i++) {              System.out.println(Thread.currentThread().getName()                     + ":synchronized in h()");              try {                  TimeUnit.SECONDS.sleep(3);              } catch (InterruptedException e) {                  e.printStackTrace();              }           }       } finally {           lock3.unlock();       }    }     public static void main(String[] args) {       final Resource4 rs = new Resource4();        new Thread() {           public void run() {              rs.f();           }       }.start();        new Thread() {           public void run() {              rs.g();           }       }.start();        rs.h();    }}

结果:Thread-0:not synchronized in f()Thread-0:synchronized in f()main:not synchronized in h()main:synchronized in h()Thread-1:not synchronized in g()Thread-1:synchronized in g()Thread-0:synchronized in f()main:synchronized in h()Thread-1:synchronized in g()Thread-0:synchronized in f()main:synchronized in h()Thread-1:synchronized in g()Thread-0:synchronized in f()main:synchronized in h()Thread-1:synchronized in g()Thread-0:synchronized in f()main:synchronized in h()

Thread-1:synchronized in g()

另外,ReentrantLock可定时和可轮询的锁获取模式由tryLock方法实现。

public boolean tryLock(); //等同于tryLock(0, TimeUnit.SECONDS),不停询问是否可获取锁public boolean tryLock(long timeout,                       TimeUnit unit)                throws InterruptedException    //timeout - 等待锁的时间,unit - timeout 参数的时间单位 

5. synchronized和lock的区别:

Lock 的锁定是通过代码实现的,而 synchronized 是在 JVM 层面上实现的。

synchronized 在锁定时如果方法块抛出异常,JVM 会自动将锁释放掉,不会因为出了异常没有释放锁造成线程死锁。但是 Lock 的话就享受不到 JVM 带来自动的功能,出现异常时必须在 finally 将锁释放掉,否则将会引起死锁。

在资源竞争不是很激烈的情况下,偶尔会有同步的情形下,synchronized是很合适的。原因在于,编译程序通常会尽可能的进行优化synchronize,另外可读性非常好,不管用没用过5.0多线程包的程序员都能理解。

ReentrantLock:

ReentrantLock提供了多样化的同步,比如有时间限制的同步,可以被Interrupt的同步(synchronized的同步是不能Interrupt的)等。在资源竞争不激烈的情形下,性能稍微比synchronized差点点。但是当同步非常激烈的时候,synchronized的性能一下子能下降好几十倍。而ReentrantLock确还能维持常态。

Atomic:

和上面的类似,不激烈情况下,性能比synchronized略逊,而激烈的时候,也能维持常态。激烈的时候,Atomic的性能会优于ReentrantLock一倍左右。但是其有一个缺点,就是只能同步一个值,一段代码中只能出现一个Atomic的变量,多于一个同步无效。因为他不能在多个Atomic之间同步。

关于synchronized和lock的详细区别请看http://www.ibm.com/developerworks/cn/java/j-jtp10264/index.html

同生天地间,为何我不能。

Java多线程—-java 对象锁 – ymeng

相关文章:

你感兴趣的文章:

标签云: