首页 > 代码库 > java对象锁和类锁

java对象锁和类锁

参考 

http://www.cnblogs.com/yyyyy5101/archive/2011/07/20/2112157.html

http://www.cnblogs.com/kkcheng/archive/2011/02/25/1964521.html

http://my.oschina.net/billowworld/blog/120766

1.java对象锁 

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

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

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();    }}

同步到多个对象锁

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

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();    }}

Lock对象锁
除了使用synchronized外,还可以使用Lock对象来创建临界区。Resource3.java的演示效果同Resource1.java;Resource4.java的演示效果同Resource2.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();    }}
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();    }}

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之间同步。 

推荐使用 ReentrantLock 和 Atomic

总结:

java 的锁都是基于对象的,判断会不会产生竞争关系的关键在于一个对象只有一把锁,对象锁 非静态方法,非静态变量,非静态代码块 都是对象锁,如果是同一个对象会产生竞争关系,反之没有竞争关系,静态方法 静态变量 静态代码块,静态* 等,他们对应的

对象时 Class的实例 如果是同一个Class实例也会产生竞争关系。

举例:同一个线程,同一个类上的不同加锁方法,一个是静态方法加锁,一个非静态方法加锁,调用的时候一个是new实例,一个是直接调用静态方法,这两个方法是不会产生竞争的因为一个是实例对象的锁,一个是Class对象的锁  

关于静态变量和静态代码块,静态方法,非静态方法的存储 都是只有一份

JVM的内存分为两部分:stack和heap :


stack(栈) 是JVM的内存指令区。stack管理很简单,push一定长度字节的数据或者指令,stack指针压栈相应的字节位移;pop一定字节长度数据或者指令,stack指针弹栈。stack的速度很快,管理很简单,并且每次操作的数据或者指令字节长度是已知的。所以Java 基本数据类型,Java 指令代码,常量都保存在stack中。 

heap (堆) 是JVM的内存数据区。hea p 的管理很复杂,每次分配不定长的内存 空间,专门用来保存对象的实例。在heap 中分配一定的内存来保存对象实例,实际上也只是保存对象实例的属性值,属性的类型和对象本身的类型标记等,并不保存对象的方法(方法是指令,保存在stack中),在heap 中分配一定的内存保存对象实例和对象的序列化比较类似。而对象实例在heap 中分配好以后,需要在stack中保存一个4字节的heap 内存地址,用来定位该对象实例在heap 中的位置,便于找到该对象实例。 

由于stack的内存管理是顺序分配的,而且定长,不存在内存回收问题;而heap 则是随机分 配内存,不定长度,存在内存分配和回收的问题;因此在JVM中另有一个GC进程,定期扫描heap ,它根据stack中保存的4字节对象地址扫描heap ,定位heap 中这些对象,进行一些优化(例如合并空闲内存块什么的 ),并且假设heap 中没有扫描到的区域都是空闲的,统统refresh(实际上是把stack中丢失了对象地址的无用对象清除了),这就是垃圾收集的过程。 

我们首先要搞清楚的是什么是数据,什么是指令?然后要搞清楚对象的方法和对象的属性分别保存在哪里? 

为了便于描述,我简单的统称: 
1)方法本身是指令的操作码部分,保存在stack中; 
2)方法内部变量作为指令的操作数 部分,跟在指令的操作码之后,保存在stack中(实际上是简单类型保存在stack中,对象类型在stack中保存地址,在heap 中保存值); 
上述的指令操作码和指令操作数构成了完整的Java 指令。 
3)对象实例包括其属性值作为数据,保存在 数据区heap 中。 
非静态的对象属性作为对象实例的一部分保存在heap 中,而对象实例必须通过stack中保存的 地址指针才能访问到。因此能否访问到对象实例以及它的非静态属性值完全取决于能否获得对象 实例在stack中的地址指针。 

先分析一下非静态方法和静态方法的区别: 
非静态方法有一个和静态方法很重大的不同:非静态方法有一个隐含的传入参数,该参数是JVM给它的,和我们怎么写代码无关,这个 隐含的参数就是对象实例在stack中的地址指针 。因此非静态方法(在stack中的指令代 码)总是可以找到自己的专用数 据(在heap 中的对象属 性值)。当然非静态方法也必须获得该隐含参数,因此非静态方法在调用 前,必须先new一个对象实 例,获得stack中的地址指针,否则JVM将无法将隐含参数传给非静态方法。

而静态方法无此隐含参数,因此也不需要new对 象,只要class文件被ClassLoader load 进入JVM的stack,该静态方法即可被调用。 当然此时静态方法是存取不到heap 中的对象属性的。 

总结一下该过程:当一个class文件被ClassLoader load进 入JVM后,方法指令保存在stack中,此时heap 区没有数据。然后程序技术器开始执行指令,如果是静态方法,直接依次执行指令代码,当然此时指令代码是不能访问heap 数据区的;如果是非静态方法,由于隐含参数没有值,会报错。因此在非静态方法执行前,要先new对象,在heap 中分配数据,并把stack中的地址指针交给非 静态方法,这样程序技术器依次执行指令,而指令代码此时能够访问到heap 数据区了。 

再说一下静态属性和动态属性: 
前面提到对象实例以及动态属性都是保存在heap 中的,而heap 必须通过stack中的地址指针才 能够被指令(类的方法)访问到。因此可以推断出:静态属性是保存在stack中 的(基本类型保存在stack中 ,对象类型地址保存在stack,值保存在heap 中),而不同于动态属性保存在heap 中。正因为都是在stack中,而stack中指令和数据都是定长的,因此很容易算出偏移量,也因此不管什么指令(类的方法),都可以访问到类的静态属性。也正因为静态属性 被保存在stack中,所以具有了全局属性。 
----------------------------------------------- 

整理总结,欢迎拍砖:

 

 

1、class 没有 load 进 JVM 前,所有的 fields ,methods 均不在内存中

2、class 被 load 进 JVM 后

 a. 对于方法

  不论是 static 或 非 static 的,都一样的,总是跟 class 本身在一起的(即存放在内存中),永远不会被回收,除非 JVM 退出。他们的区别是,静态的方法不需要实例即可使用,而非静态的方法需要实例调用。

        那么,除了安全的考虑外,以于使用者来说,调用 static 方法更方便,但并不会对内存造成额外的开销。

     b. 对于属性

  静态属性是跟 class 相关的,所以总是当 class 被 load 进 JVM 时即存在于内存中,并不会被垃圾回收(所以,相比于实例属性,对于内存来说是有多余的开销的)。

  静态属性被回收的唯一情况是类本身被回收,这种情况的发生只有当某个应用单独使用了一种复杂的类装载器的时候才可能出现。

  实例属性是跟实例相关的,故只有类实例化后,才会放入内存,当实例被回收后,实例属性自然也不存在了。

-

 

3、静态属性保存在stack指令内 存区,动态属性保存在heap 数据内存区。

进行内存优化其实就是时间换空间或空间换时间,如何界定取决你的需求。

java对象锁和类锁