synchronized对象锁和类锁

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/yhl_jxy/article/details/82746903

 

一 对象锁和类锁简介

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

对象锁:

1)加在实例方法上;

2)加在实例方法的代码块上;

类锁:

1)加在静态方法上;

2)加在静态方法的代码块上;

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

二 类锁

先看下比较特别的类锁,首先创建一个类,有在静态方法上加锁的,也有在静态方法代码块上加锁的。

***TaskA

package com.lanhuigu.demo3.Synchronized.synchronized4;

/**
 * 用于测试类锁的实例
 * @author yihonglei
 * @date 2018/9/17 17:44
 */
public class TaskA {
    /**
     * 静态方法上加锁
     */
    public static void doSomething1() {
        synchronized (TaskA.class) {
            System.out.println(Thread.currentThread().getName() + ", begin");
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + ", end");
        }
    }

    /**
     * 静态方法上加锁
     */
    public static void doSomething2() {
        synchronized (TaskA.class) {
            System.out.println(Thread.currentThread().getName() + ", begin");
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + ", end");
        }
    }

    /**
     * 静态方法的代码块上加锁
     */
    synchronized public static void doSomething3() {
        System.out.println(Thread.currentThread().getName() + ", begin");
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + ", end");
    }
}

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

***ThreadA1

package com.lanhuigu.demo3.Synchronized.synchronized4;

/**
 * 创建线程A1
 * @author yihonglei
 * @date 2018/9/17 14:46
 */
public class ThreadA1 extends Thread{

    @Override
    public void run() {
        TaskA.doSomething1();
    }
}

***ThreadA2

package com.lanhuigu.demo3.Synchronized.synchronized4;

/**
 * 创建线程A1
 * @author yihonglei
 * @date 2018/9/17 14:46
 */
public class ThreadA2 extends Thread{

    @Override
    public void run() {
        TaskA.doSomething2();
    }
}

***ThreadA3

package com.lanhuigu.demo3.Synchronized.synchronized4;

/**
 * 创建线程A1
 * @author yihonglei
 * @date 2018/9/17 14:46
 */
public class ThreadA3 extends Thread{

    @Override
    public void run() {
        TaskA.doSomething3();
    }
}

最后,写一个测试类,调用3个线程,观察输出结果。

***RunTestA

package com.lanhuigu.demo3.Synchronized.synchronized4;

/**
 * 类锁:
 * 1)同步静态方法
 * 2)同步静态方法的代码块
 * 他们用的同一个锁,线程排队执行同步方法和代码块;
 * @author yihonglei
 * @date 2018/9/17 17:07
 */
public class RunTestA {
    public static void main(String[] args) {
        // 创建线程
        ThreadA1 threadA1 = new ThreadA1();
        threadA1.setName("ThreadA1");

        ThreadA2 threadA2 = new ThreadA2();
        threadA2.setName("ThreadA2");

        ThreadA3 threadA3 = new ThreadA3();
        threadA3.setName("ThreadA3");

        // 启动线程
        threadA1.start();
        threadA2.start();
        threadA3.start();
    }
}

运行结果:

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

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

这就是结果按顺序输出的原因,这也是类锁的特性,一个类,一个类锁,大家用一个,持有就是王者,

否则就排队等待。

三 对象锁

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

先看第一种,在实例方法上加锁。下面讨论多个线程持有多个对象,每一个线程控制自己的对象锁,

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

1、实例方法上加锁

***TaskB

package com.lanhuigu.demo3.Synchronized.synchronized4;

/**
 * 用于测试同步实例方法加,即对象锁;
 * @author yihonglei
 * @date 2018/9/17 17:59
 */
public class TaskB {
    /**
     * 实例方法上加锁
     */
    synchronized public void doSomething() {
        System.out.println(Thread.currentThread().getName() + ", begin");
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + ", end");
    }
}

***ThreadB

package com.lanhuigu.demo3.Synchronized.synchronized4;

/**
 * 创建线程B
 * @author yihonglei
 * @date 2018/9/17 14:46
 */
public class ThreadB extends Thread{
    private TaskB taskB;

    public ThreadB(TaskB taskB) {
        this.taskB = taskB;
    }

    @Override
    public void run() {
        taskB.doSomething();
    }
}

***RunTestB

package com.lanhuigu.demo3.Synchronized.synchronized4;

/**
 * 对象锁:
 * 1)加在实例方法上;
 * 2)多线程多对象,每一个线程拥有一个对象,jvm为每一个实例对象同步方法加锁,多线程之间互不影响,异步执行;
 * @author yihonglei
 * @date 2018/9/17 17:07
 */
public class RunTestB {
    public static void main(String[] args) {
        // 创建实例
        TaskB taskB1 = new TaskB();
        TaskB taskB2 = new TaskB();

        // 创建线程
        ThreadB threadB1 = new ThreadB(taskB1);
        threadB1.setName("threadB1");

        ThreadB threadB2 = new ThreadB(taskB2);
        threadB2.setName("threadB2");

        // 启动线程
        threadB1.start();
        threadB2.start();
    }
}

运行结果:

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

用的不是同一个锁,而是用自己持有对象的锁,线程异步执行,而不是排队等待执行。

2、实例方法的代码块上加锁

***TaskC

package com.lanhuigu.demo3.Synchronized.synchronized4;

/**
 * 用于测试同步实例方法的代码块,即对象锁;
 * @author yihonglei
 * @date 2018/9/17 17:59
 */
public class TaskC {
    /**
     * 实例方法代码块上加锁
     */
    public void doSomething() {
        synchronized (this) {
            System.out.println(Thread.currentThread().getName() + ", begin");
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + ", end");
        }
    }
}

***ThreadC

package com.lanhuigu.demo3.Synchronized.synchronized4;

/**
 * 创建线程C
 * @author yihonglei
 * @date 2018/9/17 14:46
 */
public class ThreadC extends Thread{
    private TaskC taskC;

    public ThreadC(TaskC taskC) {
        this.taskC = taskC;
    }

    @Override
    public void run() {
        taskC.doSomething();
    }
}

***RunTestC

package com.lanhuigu.demo3.Synchronized.synchronized4;

/**
 * 对象锁:
 * 1)加在实例方法的代码块;
 * 2)多线程多对象,每一个线程拥有一个对象,jvm为每一个实例对象同步方法加锁,多线程之间互不影响,异步执行;
 * @author yihonglei
 * @date 2018/9/17 17:07
 */
public class RunTestC {
    public static void main(String[] args) {
        // 创建实例
        TaskC taskC1 = new TaskC();
        TaskC taskC2 = new TaskC();

        // 创建线程
        ThreadC threadC1 = new ThreadC(taskC1);
        threadC1.setName("threadC1");

        ThreadC threadC2 = new ThreadC(taskC2);
        threadC2.setName("threadC2");

        // 启动线程
        threadC1.start();
        threadC2.start();
    }
}

运行结果:

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

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

猜你喜欢

转载自blog.csdn.net/yhl_jxy/article/details/82746903