问题:
平时对锁的研究不是很深,只知道它的概念,有对象锁,类锁,方法锁这些,但是实际使用就分不清什么时候锁是生效的,这里略微总结下对象锁和类锁的使用。
一、类锁的使用
第一种情况:同一个方法上加类锁
首先应该明确的是java同一个类只会加载一次,所以在给一个方法中上类锁,无论有多少个线程new了多少个对象去调用这个方法,那么都是排队执行的。
public class SynTest {
public static void read (String threadName) {
synchronized (SynTest.class) { //类锁
System.out.printf("【%s】我是read方法!开始执行....%n", threadName);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.printf("【%s】我是read方法!执行结束!%n", threadName);
}
}
}
public class ConvertDemoApplication {
public static void main(String[] args) {
//模拟多个线程同时去调用read方法,观察是否时串行执行的
new Thread(() -> {
SynTest.read(Thread.currentThread().getName());
}, "aaa").start();
new Thread(() -> {
SynTest.read(Thread.currentThread().getName());
}, "bbb").start();
new Thread(() -> {
SynTest.read(Thread.currentThread().getName());
}, "ccc").start();
}
}
执行结果: 结论是串行执行的
【aaa】我是read方法!开始执行....
【aaa】我是read方法!执行结束!
【ccc】我是read方法!开始执行....
【ccc】我是read方法!执行结束!
【bbb】我是read方法!开始执行....
【bbb】我是read方法!执行结束!
第二种情况:同一个类中,不同方法中上类锁
同一个类中有多个方法都上了类锁,这时候就是互斥锁,多个线程调用不同的方法时,也需要排队执行,即调用A结束后才能调用B;
public class SynTest {
//同一个类中的read方法
public static void read (String name) {
synchronized (SynTest.class) {
System.out.printf("【%s】我是read方法!开始执行....%n", name);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.printf("【%s】我是read方法!执行结束!%n", name);
}
}
//同一个类中的write方法
public static void write (String name) {
synchronized (SynTest.class) { //类锁
System.out.printf("【%s】我是write方法!开始执行....%n", name);
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.printf("【%s】我是write方法!执行结束!%n", name);
}
}
}
public class ConvertDemoApplication {
private static int index = 0;
public static void main(String[] args) {
//下面是调用read方法
new Thread(() -> {
SynTest.read(Thread.currentThread().getName());
}, "aaa").start();
new Thread(() -> {
SynTest.read(Thread.currentThread().getName());
}, "bbb").start();
new Thread(() -> {
SynTest.read(Thread.currentThread().getName());
}, "ccc").start();
//下面是调用write方法
new Thread(() -> {
SynTest.write(Thread.currentThread().getName());
}, "AAA").start();
new Thread(() -> {
SynTest.write(Thread.currentThread().getName());
}, "BBB").start();
new Thread(() -> {
SynTest.write(Thread.currentThread().getName());
}, "CCC").start();
}
}
执行结果:即使调用不同方法,也实现了方法间的互斥。
【bbb】我是read方法!开始执行....
【bbb】我是read方法!执行结束!
【CCC】我是write方法!开始执行....
【CCC】我是write方法!执行结束!
【BBB】我是write方法!开始执行....
【BBB】我是write方法!执行结束!
【AAA】我是write方法!开始执行....
【AAA】我是write方法!执行结束!
【ccc】我是read方法!开始执行....
【ccc】我是read方法!执行结束!
【aaa】我是read方法!开始执行....
【aaa】我是read方法!执行结束!
二、对象锁
在方法内使用实例对象加锁
public class SynTest {
public void toDo (String name) {
synchronized (this) { //用的是对象锁
System.out.printf("【%s】我是toDo方法!开始执行....%n", name);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.printf("【%s】我是toDo方法!执行结束!%n", name);
}
}
}
public class ConvertDemoApplication {
public static void main(String[] args) throws InterruptedException {
//第一种,不同线程传入同一个对象
SynTest test1 = new SynTest();
new Thread(() -> {
test1.toDo(Thread.currentThread().getName());
}, "111").start();
new Thread(() -> {
test1.toDo(Thread.currentThread().getName());
}, "222").start();
new Thread(() -> {
test1.toDo(Thread.currentThread().getName());
}, "333").start();
Thread.sleep(5000);
//第二种 不同线程传入不同的对象
new Thread(() -> {
SynTest test = new SynTest();
test.toDo(Thread.currentThread().getName());
}, "444").start();
new Thread(() -> {
SynTest test = new SynTest();
test.toDo(Thread.currentThread().getName());
}, "555").start();
new Thread(() -> {
SynTest test = new SynTest();
test.toDo(Thread.currentThread().getName());
}, "666").start();
new Thread(() -> {
SynTest test = new SynTest();
test.toDo(Thread.currentThread().getName());
}, "RRR").start();
}
}
执行结果: 结论是对象锁使用相同对象能实现互斥,使用不同对象不能实现互斥。
【111】我是toDo方法!开始执行....
【111】我是toDo方法!执行结束!
【333】我是toDo方法!开始执行....
【333】我是toDo方法!执行结束!
【222】我是toDo方法!开始执行....
【222】我是toDo方法!执行结束!
【444】我是toDo方法!开始执行....
【555】我是toDo方法!开始执行....
【666】我是toDo方法!开始执行....
【444】我是toDo方法!执行结束!
【555】我是toDo方法!执行结束!
【666】我是toDo方法!执行结束!
四、方法锁
方法锁是将synchronized放在方法上,这个和实例对象锁用法一样,放在方法内是锁住部分代码,放在方法上是锁住方法所有的代码;
五、结论
- 看是不是互斥锁,只需要看锁住的是不是同一个对象,类对象只有一个,所以无论多少个线程调用,都是同一个对象,所以一定是互斥的,但是用的是实例对象的话,就要看不同线程传入的是不是同一个对象,如果是,则是互斥锁,如果不是同一个对象,则不是互斥锁。
- 在实际的项目中,由于spring管理的bean都是单例的,比如controller都是单例的,所以使用实例对象锁时就是互斥的,如果controller不是单例的,那么多线程环境下就不是互斥锁。