一 对象锁和类锁简介
我们可以从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为每个对象创建了锁。
如果想让线程排队执行,让多个线程持有同一个对象,线程就会排队执行。