synchronized对象锁和类锁

时间:2019-02-11
本文章向大家介绍synchronized对象锁和类锁,主要包括synchronized对象锁和类锁使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

一 对象锁和类锁简介

我们可以从synchronized加锁位置区分对象锁和类锁。

1、对象锁

普通同步方法,锁是当前实例对象。比如:

public synchronized void doLongTimeTaskC() {}

2、类锁

静态同步方法,锁是当前类的Class对象。比如:

public synchronized static void doLongTimeTaskA() {}

3、同步代码块上的对象锁或类锁

加在同步代码块上,锁是Synchonized括号里配置的对象,可以是实例对象,也可以是Class对象;

public void doLongTimeTaskD() {
    synchronized (this) {
    }
}

public static void doLongTimeTaskE() {
    synchronized (Task.class) {
    }
}

对象锁和类锁是两个完全不一样的锁,下面通过实例看看他们的区别。

二 对象锁

对象锁分两种情况说明,分别是在实例方法上加锁或在实例方法的代码块上加锁。

下面讨论多个线程持有多个对象,每一个线程控制自己的对象锁,

线程之间异步执行。简单说就是一个线程一个对象,谁也不影响谁。

Task

package com.lanhuigu.thread.synchronizeddemo;

/**
 * 可以从synchronized加锁位置区分对象锁和类锁:
 * 1、对象锁
 *
 * 普通同步方法,锁是当前实例对象。比如:
 *
 * public synchronized void doLongTimeTaskC() {}
 *
 * 2、类锁
 *
 * 静态同步方法,锁是当前类的Class对象。比如:
 *
 * public synchronized static void doLongTimeTaskA() {}
 *
 * 3、同步代码块
 *
 * 加在同步代码块上,锁是Synchonized括号里配置的对象,可以是实例对象,也可以是Class对象;
 *
 * public void doLongTimeTaskD() {
 *     synchronized (this) {
 *     }
 * }
 *
 * 或
 *
 * public static void doLongTimeTaskE() {
 *     synchronized (Task.class) {
 *     }
 * }
 */
public class Task {

    /**
     * 对象锁:普通同步方法,锁为当前实例对象。
     */
    public synchronized void doLongTimeTaskA() {
        System.out.println("name = " + Thread.currentThread().getName() + ", begain");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("name = " + Thread.currentThread().getName() + ", end");

    }

    /**
     * 对象锁:同步代码块,锁为代码块里面的实例对象。
     */
    public void doLongTimeTaskB() {
        synchronized (this) {
            System.out.println("name = " + Thread.currentThread().getName() + ", begain");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("name = " + Thread.currentThread().getName() + ", end");
        }
    }
}

ThreadA

package com.lanhuigu.thread.synchronizeddemo;

public class ThreadA extends Thread{
    private Task mTask;

    public ThreadA(Task tk){
        mTask = tk;
    }

    @Override
    public void run() {
        mTask.doLongTimeTaskA();
    }
}

ThreadB 

package com.lanhuigu.thread.synchronizeddemo;

public class ThreadB extends Thread{
    private Task mTask;

    public ThreadB(Task tk){
        mTask = tk;
    }

    @Override
    public void run() {
        mTask.doLongTimeTaskB();
    }
}

RunObjectTest

package com.lanhuigu.thread.synchronizeddemo;

/**
 * 对象锁测试
 */
public class RunObjectTest {
    public static void main(String[] args) {
        Task mTaskA = new Task();
        Task mTaskB = new Task();

        ThreadA ta1 = new ThreadA(mTaskA);
        ThreadA ta2 = new ThreadA(mTaskB);

        ThreadB tb1 = new ThreadB(mTaskA);
        ThreadB tb2 = new ThreadB(mTaskB);

        ta1.setName("A1");
        ta2.setName("A2");

        tb1.setName("B1");
        tb2.setName("B2");

        ta1.start();
        ta2.start();
        tb1.start();
        tb2.start();
    }
}

运行结果:

 

从运行结果可以看到A1,begin开始后,并没有接着输出A1,end,而是输出了A2,begin,说明两个线程

用的不是同一个锁,如果用的是同一把锁,A1,begin输出,必然需要等A1,end结束释放锁后,

A2获取锁才会接着输出A2,begin。

所以A1,A2分别用的是自己持有对象的锁,线程自己管自己的锁,互不影响,线程异步执行,而不是排队等待执行。

对于B1和B2也是同理。

总结:

多线程分别持有多个对象,每个线程异步执行对象的同步方法,因为JVM为每个对象创建了锁。

如果想让线程排队执行,让多个线程持有同一个对象,线程就会排队执行。

 

三 类锁

类锁,有在静态方法上加锁的,也有在静态方法代码块上加锁的。

Task

package com.lanhuigu.thread.synchronizeddemo;

/**
 * 可以从synchronized加锁位置区分对象锁和类锁:
 * 1、对象锁
 *
 * 普通同步方法,锁是当前实例对象。比如:
 *
 * public synchronized void doLongTimeTaskC() {}
 *
 * 2、类锁
 *
 * 静态同步方法,锁是当前类的Class对象。比如:
 *
 * public synchronized static void doLongTimeTaskA() {}
 *
 * 3、同步代码块
 *
 * 加在同步代码块上,锁是Synchonized括号里配置的对象,可以是实例对象,也可以是Class对象;
 *
 * public void doLongTimeTaskD() {
 *     synchronized (this) {
 *     }
 * }
 *
 * 或
 *
 * public static void doLongTimeTaskE() {
 *     synchronized (Task.class) {
 *     }
 * }
 */
public class Task {

    // 静态对象
    private static Object object = new Object();

    /**
     * 对象锁:普通同步方法,锁为当前实例对象。
     */
    public synchronized void doLongTimeTaskA() {
        System.out.println("name = " + Thread.currentThread().getName() + ", begin");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("name = " + Thread.currentThread().getName() + ", end");

    }

    /**
     * 对象锁:同步代码块,锁为代码块里面的实例对象。
     */
    public void doLongTimeTaskB() {
        synchronized (this) {
            System.out.println("name = " + Thread.currentThread().getName() + ", begin");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("name = " + Thread.currentThread().getName() + ", end");
        }
    }

    /**
     * 类锁:静态同步方法,锁为当前Class对象。
     */
    public synchronized static void doLongTimeTaskC() {
        System.out.println("name = " + Thread.currentThread().getName() + ", begin");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("name = " + Thread.currentThread().getName() + ", end");
    }

    /**
     * 类锁:静态同步方法,锁为当前Class对象。
     */
    public synchronized static void doLongTimeTaskD() {
        System.out.println("name = " + Thread.currentThread().getName() + ", begin");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("name = " + Thread.currentThread().getName() + ", end");
    }

    /**
     * 同步代码块:里面的对象可以是Class对象,也可以是实例对象。
     */
    public static void doLongTimeTaskE() {
        synchronized (Task.class) {// Class对象
        //synchronized (object) {// 实例对象
            System.out.println("name = " + Thread.currentThread().getName() + ", begin");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("name = " + Thread.currentThread().getName() + ", end");
        }
    }
}

然后创建3个线程,分别调用TaskA中的3个方法。

ThreadC

package com.lanhuigu.thread.synchronizeddemo;

public class ThreadC extends Thread{
    private Task mTask;

    public ThreadC(Task tk){
        mTask = tk;
    }

    @Override
    public void run() {
        mTask.doLongTimeTaskC();
    }
}

ThreadD

package com.lanhuigu.thread.synchronizeddemo;

public class ThreadD extends Thread{
    private Task mTask;

    public ThreadD(Task tk){
        mTask = tk;
    }

    @Override
    public void run() {
        mTask.doLongTimeTaskD();
    }
}

ThreadE

package com.lanhuigu.thread.synchronizeddemo;

public class ThreadE extends Thread{
    private Task mTask;

    public ThreadE(Task tk){
        mTask = tk;
    }

    @Override
    public void run() {
        mTask.doLongTimeTaskE();
    }
}

RunClassTest

package com.lanhuigu.thread.synchronizeddemo;

/**
 * 类锁测试
 */
public class RunClasstTest {
    public static void main(String[] args) {
        Task mTask = new Task();
        ThreadC tc = new ThreadC(mTask);
        ThreadD td = new ThreadD(mTask);
        ThreadE te = new ThreadE(mTask);

        tc.setName("C");// 静态同步方法
        td.setName("D");// 静态同步方法
        te.setName("E");// 静态方法同步代码块

        tc.start();
        td.start();
        te.start();
    }
}

运行结果:

 

从程序运行结果可以看到,结果按照某个线程begin,然后接着输出end,说明线程按顺序执行同步方法。

因为,三个线程持有的是Task的Class类锁,是同一个锁,所以线程需要排队等待执行,直到获取锁才能执行,

这就是结果按顺序输出的原因,这也是类锁的特性,多个线程持有一个类锁,排队执行,持有就是王者,

否则就排队等待。