[转]java的类锁和对象锁

  对象锁:java的所有对象都含有1个互斥锁,这个锁由JVM自动获取和释放。线程进入synchronized方法的时候获取该对象的锁,当然如果已经有线程获取了这个对象的锁,那么当前线程会等待;synchronized方法正常返回或者抛异常而终止,JVM会自动释放对象锁。这里也体现了用synchronized来加锁的1个好处,方法抛异常的时候,锁仍然可以由JVM来自动释放。
  类锁:对象锁是用来控制实例方法之间的同步,类锁是用来控制静态方法(或静态变量互斥体)之间的同步。其实类锁只是一个概念上的东西,并不是真实存在的,它只是用来帮助我们理解锁定实例方法和静态方法的区别的。我们都知道,java类可能会有很多个对象,但是只有1个Class对象,也就是说类的不同实例之间共享该类的Class对象。Class对象其实也仅仅是1个java对象,只不过有点特殊而已。由于每个java对象都有1个互斥锁,而类的静态方法是需要Class对象。所以所谓的类锁,不过是Class对象的锁而已。获取类的Class对象有好几种,最简单的就是MyClass.class的方式。
  为什么需要加锁呢?肯定是因为存在不同线程对共享对象的并发访问,没有数据共享就不需要锁。下面这个类,是我们使用java的synchronized方式进行控制的方法,会在我们后面的线程中调用。

package net.aty.lock.target;
 
public class TargetMethod {
    // 对象锁:形式1
    public synchronized void objLockMethod1() {
        System.out.println("in...objLockMethod1");
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("out...objLockMethod1");
    }
 
    // 对象锁:形式2
    public void objLockMethod2()
    {
        synchronized (this) {
            System.out.println("in...objLockMethod2");
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("out...objLockMethod2");
        }
 
    }
 
    // 类锁:形式1
    public static synchronized void classLock1() {
        System.out.println("classLock1------in");
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("classLock1------out");
    }
 
    // 类锁:形式2
    public void classLock2() {
        synchronized (TargetMethod.class)
        {
            System.out.println("classLock2------in");
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("classLock2------out");
        }
    }
}

1、我们先来做第一个测试,该测试很简单,说明:如果线程不存在数据共享,锁就不会有效果,也就没有必要加锁。

package net.aty.lock.thread.first;
import net.aty.lock.target.TargetMethod;

public class DemoThread1 extends Thread {
    private TargetMethod target = null;
 
    public DemoThread1(TargetMethod target) {
        this.target = target;
    }
 
    @Override
    public void run() {
        target.objLockMethod1();
    }
}
    
package net.aty.lock.thread.first;
import net.aty.lock.target.TargetMethod;

public class DemoThread2 extends Thread {
    private TargetMethod target = null;
 
    public DemoThread2(TargetMethod target) {
        this.target = target;
    }
 
    @Override
    public void run() {
        target.objLockMethod2();
    }
}

package net.aty.lock.thread.first;
import net.aty.lock.target.TargetMethod;
 
public class Test {
 
    public static void main(String[] args) throws Exception {
        test2();
    }
 
    public static void test1() throws Exception {
        TargetMethod target1 = new TargetMethod();
        TargetMethod target2 = new TargetMethod();
 
        // 线程1运行后,睡眠500ms
        Thread t1 = new DemoThread1(target1);
        t1.start();
 
        // 主线程睡眠100ms后,恢复执行,此时线程1仍然处于睡眠状态
        Thread.sleep(100);
        System.out.println("main thread runnig....");
 
        // 线程2开始运行
        Thread t2 = new DemoThread2(target2);
        t2.start();
    }
     
    public static void test2() throws Exception {
        TargetMethod shared = new TargetMethod();
 
        Thread t1 = new DemoThread1(shared);
        t1.start();
 
        Thread.sleep(100);
        System.out.println("main thread runnig....");
 
        Thread t2 = new DemoThread2(shared);
        t2.start();
    }
}

2、第二组测试,验证所谓的”类锁”的确可以达到控制静态方法同步的效果

package net.aty.lock.thread.second;
import net.aty.lock.target.TargetMethod;

public class DemoThread3 extends Thread {
 
    public DemoThread3() {
    }
 
    @Override
    public void run() {
        TargetMethod.classLock1();
    }
}
    
package net.aty.lock.thread.second;
import net.aty.lock.target.TargetMethod;
 
public class DemoThread4 extends Thread {
    private TargetMethod target = null;
 
    public DemoThread4(TargetMethod target) {
        this.target = target;
    }

    @Override
    public void run() {
        target.classLock2();
    }
}

package net.aty.lock.thread.second;
import net.aty.lock.target.TargetMethod;

public class Test {
 
    public static void main(String[] args) throws Exception {
        // 线程3运行后,睡眠500ms
        Thread t1 = new DemoThread3();
        t1.start();
 
        // 主线程睡眠100ms后,恢复执行,此时线程1仍然处于睡眠状态
        Thread.sleep(100);
        System.out.println("main thread runnig....");
 
        // 线程4开始运行
        Thread t2 = new DemoThread4(new TargetMethod());
        t2.start();
    }
}

执行结果如下:通过分析,可以知道的确实现了static方法之间的同步访问
classLock1——in
main thread runnig….
classLock1——out
classLock2——in
classLock2——out

3、最后我们来测试下对象锁和类锁的区别和联系。线程5会访问同步的实例方法,线程6访问同步的静态方法。

package net.aty.lock.thread.third;
import net.aty.lock.target.TargetMethod;
 
public class DemoThread5 extends Thread {
    private TargetMethod target = null;
 
    public DemoThread5(TargetMethod target) {
        this.target = target;
    }
 
    @Override
    public void run() {
        target.objLockMethod1();
    }
}

package net.aty.lock.thread.third;
import net.aty.lock.target.TargetMethod;
 
public class DemoThread6 extends Thread {
    public DemoThread6() {
    }
 
    @Override
    public void run() {
        TargetMethod.classLock1();
    }
}

package net.aty.lock.thread.third;
import net.aty.lock.target.TargetMethod;
 
public class Test {
 
    public static void main(String[] args) throws Exception {
        test2();
    }
 
    public static void test1() throws Exception {
        // 线程5开始运行
        Thread t1 = new DemoThread5(new TargetMethod());
        t1.start();
 
        // 主线程睡眠100ms后,恢复执行,此时线程1仍然处于睡眠状态
        Thread.sleep(100);
        System.out.println("main thread runnig....");
 
        // 线程6运行后,睡眠500ms
        Thread t2 = new DemoThread6();
        t2.start();
    }
 
    public static void test2() throws Exception {
        // 线程6开始运行
        Thread t2 = new DemoThread6();
        t2.start();
 
        // 主线程睡眠100ms后,恢复执行,此时线程1仍然处于睡眠状态
        Thread.sleep(100);
        System.out.println("main thread runnig....");
 
        // 线程5
        Thread t1 = new DemoThread5(new TargetMethod());
        t1.start();
    }
}

执行结果如下:
classLock1——in
main thread runnig….
in…objLockMethod1
classLock1——out
out…objLockMethod1

可以看出,类锁和对象锁不是同1个东西,一个是类的Class对象的锁,1个是类的实例的锁。也就是说:1个线程访问静态synchronized的时候,允许另一个线程访问对象的实例synchronized方法。反过来也是成立的,因为他们需要的锁是不同的。

原址:http://www.2cto.com/kf/201404/289815.html

    原文作者:java锁
    原文地址: https://www.cnblogs.com/atai/articles/5990461.html
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞