首先,我们需要先了解什么是JAVA内置锁:
在JAVA中,任何对象都能够用作保证代码同步执行的锁,这个锁称为内置锁。当程序运行到被内置锁保护的代码时,就会获取锁。运行完被锁保护的代码时,就会释放锁。
JAVA的内置锁是线程间互斥的,也就是说,同一时间只有一个线程能获得锁。例如有A,B两个线程,A首先执行了被锁保护的代码段,那么A拿走了锁,当B将要执行被锁保护的代码段时,因为锁已经被A拿走了,所以B就会阻塞来等待A释放锁,A释放锁之后B获得锁,然后B才能执行被锁保护的代码段;如果A一直不释放锁,那么B就会永远的等待下去。
JAVA的类锁和对象锁的概念与内置锁相同,但是类锁和对象锁本身的使用方式和作用范围不同。
类锁:
作用范围:
本类的所用实例化对象。
使用方式:
1. synchronized关键字修饰静态方法。
2. 通过如下代码对代码段进行加锁:
synchronized(xxx.class){
//代码段
}
对象锁:
作用范围:
同一个对象(也就是被同一次new关键字实例化出来的对象)。
使用方式:
1. synchronized关键字修饰非静态方法。
2. 通过如下代码对代码段进行加锁:
synchronized(xxx){
//代码段
}
值得注意的是,类锁和对象锁本身就是两种锁,这两种锁之间不存在线程互斥关系。也就是某个类的一个对象,在两个线程中分别访问被该类的类锁和该类的对象锁保护的代码段时,这两个线程是异步执行的。
为了理解方便,我们这么假设:
锁就是公共厕所。
类就是人,类的对象就是不同的人,也就是男人和女人。
类锁的话,就是不管是类中的哪一个实例对象,都会锁住,那么类锁这个公共厕所就是建立在村东头的不区分男女的那种,而且只有一个马桶,不论男女哪个对象访问马桶,只要马桶没被别人使用,就可以马上拉屎。
对象锁的话,只会锁住相同的实例对象,不同的实例对象不会被锁住,那么对象锁这个公共厕所就是建立在村西头区分男女的那种厕所,男厕和女厕各有一个马桶,男人只能访问男厕的马桶,女人只能访问女厕的马桶,不然。。就很香艳了,JAVA是拒绝的。
而类锁和对象锁本身就是两个公共厕所,一个是建立在村东头的不分男女的厕所,一个是建立在村西头的区分男女的公共厕所,所以,任何一个人随便去这两个厕所中的其中一个都可以上厕所,所以类锁和对象锁互相不会被锁住。
下面,我们通过程序来看一下类锁和对象锁的实际应用:
首先,我们创建一个类TestTask,这个类中有类锁的方法,也有对象锁的方法:
class TestTask implements Runnable {
/**
* 类锁方法
*/
public synchronized static void classSynchronizedMehtod() {
System.out.println(Thread.currentThread().getName() + "开始时间:" + LocalDateTime.now());
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "结束时间:" + LocalDateTime.now());
}
/**
* 对象锁方法
*/
public synchronized void projectSynchronizedMehtod() {
System.out.println(Thread.currentThread().getName() + "开始时间:" + LocalDateTime.now());
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "结束时间:" + LocalDateTime.now());
}
private String lockType;
TestTask(){}
TestTask(String lockType) {
this.lockType = lockType;
}
@Override
public void run() {
// 锁的类型,如果是class则调用类锁方法,project则调用对象所方法
switch (lockType) {
case "class" :
classSynchronizedMehtod();
break;
case "project" :
projectSynchronizedMehtod();
break;
default:
System.out.println("传值错误,老哥");
break;
}
}
}
以及类TestTask2,这个类除了类名和TestTask1不同外,其他代码都一样。
public class TestTask2 implements Runnable {
// 代码与 TestTask1 相同
}
那么我们开始测试:
1. 同一个类的两个不同对象会不会被类锁影响从而同步执行:
public static void main(String[] args) {
TestTask classSynchronized1 = new TestTask("class");
TestTask classSynchronized2 = new TestTask("class");
Thread testTask1 = new Thread(classSynchronized1);
Thread testTask2 = new Thread(classSynchronized2);
testTask1.setName("这是第一个类锁任务");
testTask2.setName("这是第二个类锁任务");
testTask1.start();
testTask2.start();
}
运行结果:
结论:
classSynchronized1、classSynchronized2 是同一个类的两个不同对象,所以会在类锁的作用下同步执行,因为类锁会作用于这个类的所有实例对象。
2. 同一个类的相同对象会不会被类锁影响从而同步执行:
public static void main(String[] args) {
TestTask classSynchronized1 = new TestTask("class");
Thread testTask1 = new Thread(classSynchronized1);
Thread testTask2 = new Thread(classSynchronized1);
testTask1.setName("这是第一个类锁任务");
testTask2.setName("这是第二个类锁任务");
testTask1.start();
testTask2.start();
}
运行结果:
结论:
同一个类的两个相同对象,会在类锁的作用下同步执行,因为类锁会作用于这个类的所有实例对象。
3. 不同类的两个不同对象会不会被类锁影响从而同步执行:
public static void main(String[] args) {
TestTask classSynchronized1 = new TestTask("class");
TestTask2 classSynchronized2 = new TestTask2("class");
Thread testTask1 = new Thread(classSynchronized1);
Thread testTask2 = new Thread(classSynchronized2);
testTask1.setName("这是第一个类锁任务");
testTask2.setName("这是第二个类锁任务");
testTask1.start();
testTask2.start();
}
运行结果:
结论:
classSynchronized1、classSynchronized2 是不同类的两个不同对象,所以不会被类锁锁住,依然是异步执行的。
4. 同一个类的两个不同对象会不会被对象锁影响从而同步执行:
public static void main(String[] args) {
TestTask methodSynchronized1 = new TestTask("project");
TestTask methodSynchronized2 = new TestTask("project");
Thread testTask3 = new Thread(methodSynchronized1);
Thread testTask4 = new Thread(methodSynchronized2);
testTask3.setName("这是第一个对象锁任务");
testTask4.setName("这是第二个对象锁任务");
testTask3.start();
testTask4.start();
}
运行结果:
结论:
methodSynchronized1、methodSynchronized2是同一个类的两个不同的对象,不会被对象锁影响,依然是异步执行的,因为对象锁只会锁住相同的对象(也就是同一次new出来的对象)。
5. 同一个类的相同对象会不会被对象锁影响从而同步执行:
public static void main(String[] args) {
TestTask methodSynchronized1 = new TestTask("project");
Thread testTask3 = new Thread(methodSynchronized1);
Thread testTask4 = new Thread(methodSynchronized1);
testTask3.setName("这是第一个对象锁任务");
testTask4.setName("这是第二个对象锁任务");
testTask3.start();
testTask4.start();
}
运行结果:
结论:
同一个类的两个相同的对象,会被对象锁影响,是同步执行的,因为对象锁只会锁住相同的对象(也就是同一次new出来的对象)。
6. 不同类的两个不同对象会不会被对象锁影响从而同步执行:
public static void main(String[] args) {
TestTask methodSynchronized1 = new TestTask("project");
TestTask2 methodSynchronized2 = new TestTask2("project");
Thread testTask3 = new Thread(methodSynchronized1);
Thread testTask4 = new Thread(methodSynchronized2);
testTask3.setName("这是第一个对象锁任务");
testTask4.setName("这是第二个对象锁任务");
testTask3.start();
testTask4.start();
}
运行结果:
结论:
methodSynchronized1、methodSynchronized2是不同类的两个不同的对象,不会被对象锁影响,依然是异步执行的,因为对象锁只会锁住相同的对象(也就是同一次new出来的对象)。
7. 那么同一个类的同一个对象会不会被类锁和对象锁锁住呢:
public static void main(String[] args) {
TestTask synchronized1 = new TestTask();
Thread testTask1 = new Thread(() -> synchronized1.classSynchronizedMehtod());
Thread testTask2 = new Thread(() -> synchronized1.projectSynchronizedMehtod());
testTask1.setName("这是第一个类锁任务");
testTask2.setName("这是第一个对象锁任务");
testTask1.start();
testTask2.start();
}
运行结果:
结论:
synchronized1在两个线程中分别调用了被该类的类锁和该类的对象锁保护的方法,这两个线程是异步执行的,也就是说对于同一个对象的类锁和对象锁是两种不同的锁,不会线程互斥。
这里只是演示了用synchronized关键字修饰静态方法产生的类锁以及修饰非静态方法所产生的对象锁。
用synchronized直接包括代码段的两种方式:
类锁:
synchronized(xxx.class) {
// 代码段
}
对象锁:
synchronized(xxx) {
// 代码段
}
就不再演示了,结果都是一样的。