Java的类锁、对象锁和方法锁
在Java中,对于synchronized关键字,大家看到的第一反应就是这个关键字是进行同步操作的,即得名“同步锁”。
-
当用它来修饰方法和代码块时,默认当前的对象为锁的对象,即对象锁。
-
当用来修饰类和静态方法时,默认当前的类为锁的对象
1.对象锁
修饰在方法上时,多个线程调用同一对象的同步方法时会阻塞,调用不同对象的同步方法时不会阻塞。
在多线程环境下,调用不同对象的同步方法:
public class SynchronizedDemo {
public synchronized void synTest(){
int i = 5;
while (i-- > 0){
System.out.println(Thread.currentThread().getName() + " : " + i);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
SynchronizedDemo demo1 = new SynchronizedDemo();
SynchronizedDemo demo2 = new SynchronizedDemo();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
demo1.obj3();
}
});
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
demo2.obj3();
}
});
t1.start();
t2.start();
}
}
Output:
Thread-0 : 4
Thread-1 : 4
Thread-0 : 3
Thread-1 : 3
Thread-0 : 2
Thread-1 : 2
Thread-0 : 1
Thread-1 : 1
Thread-0 : 0
Thread-1 : 0
在多线程环境下,调用同一对象的同步方法:
public class SynchronizedDemo {
public synchronized void synTest(){
int i = 5;
while (i-- > 0){
System.out.println(Thread.currentThread().getName() + " : " + i);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
SynchronizedDemo demo1 = new SynchronizedDemo();
SynchronizedDemo demo2 = new SynchronizedDemo();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
demo1.synTest();
}
});
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
demo1.synTest();
}
});
t1.start();
t2.start();
}
}
Output:
Thread-0 : 4
Thread-0 : 3
Thread-0 : 2
Thread-0 : 1
Thread-0 : 0
Thread-1 : 4
Thread-1 : 3
Thread-1 : 2
Thread-1 : 1
Thread-1 : 0
在多线程环境下,调用不同对象通过this修饰的局部代码块
public class SynchronizedDemo {
public void synTest(){
synchronized (this){
int i = 5;
while (i-- > 0){
System.out.println(Thread.currentThread().getName() + " : " + i);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
SynchronizedDemo demo1 = new SynchronizedDemo();
SynchronizedDemo demo2 = new SynchronizedDemo();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
demo1.synTest();
}
});
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
demo2.synTest();
}
});
t1.start();
t2.start();
}
}
Output:
Thread-0 : 4
Thread-1 : 4
Thread-0 : 3
Thread-1 : 3
Thread-0 : 2
Thread-1 : 2
Thread-0 : 1
Thread-1 : 1
Thread-0 : 0
Thread-1 : 0
对于this修饰的其实指的就是类的实例,所以它也属于对象锁,并不是类锁。
在多线程环境下,调用不同对象通过其他实例类修饰的局部代码块
public class SynchronizedDemo {
public void synTest(){
String str = new String("lock");
synchronized (str){
int i = 5;
while (i-- > 0){
System.out.println(Thread.currentThread().getName() + " : " + i);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
SynchronizedDemo demo1 = new SynchronizedDemo();
SynchronizedDemo demo2 = new SynchronizedDemo();
Thread t1 = new Thread(() -> {
demo1.synTest();
});
Thread t2 = new Thread(() -> {
demo2.synTest();
});
t1.start();
t2.start();
}
}
Output:
Thread-0 : 4
Thread-1 : 4
Thread-1 : 3
Thread-0 : 3
Thread-1 : 2
Thread-0 : 2
Thread-1 : 1
Thread-0 : 1
Thread-1 : 0
Thread-0 : 0
我们可以看到,我们通过每次调用时实例一个String来进行同步代码块,但是并没有发生阻塞,因为每次生成的是一个实例String,锁的是String,每次都是不一样的,所以不会发生阻塞。
可以通过上述的运行结果可以得到一下结论:
在多线程环境下:
- 调用不同对象的同步方法,不会发生阻塞
- 调用相同对象的同步方法,会发生阻塞
- 调用不同对象通过this修饰的局部代码块,不会发生阻塞
- 调用不同对象通过其他实例类修饰的同步代码块,不会发生阻塞
2.类锁
在多线程环境下,多次调用类的静态同步方法:
public class SynchronizedDemo {
public static synchronized void synTest(){
int i = 5;
while (i-- > 0){
System.out.println(Thread.currentThread().getName() + " : " + i);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
SynchronizedDemo.synTest();
}
});
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
SynchronizedDemo.synTest();
}
});
t1.start();
t2.start();
}
Output:
Thread-0 : 4
Thread-0 : 3
Thread-0 : 2
Thread-0 : 1
Thread-0 : 0
Thread-1 : 4
Thread-1 : 3
Thread-1 : 2
Thread-1 : 1
Thread-1 : 0
在多线程环境下,多次调用被类锁的代码块:
public class SynchronizedDemo {
public void synTest(){
synchronized (SynchronizedDemo.class){
int i = 5;
while (i-- > 0){
System.out.println(Thread.currentThread().getName() + " : " + i);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
SynchronizedDemo demo1 = new SynchronizedDemo();
SynchronizedDemo demo2 = new SynchronizedDemo();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
demo1.synTest();
}
});
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
demo2.synTest();
}
});
t1.start();
t2.start();
}
}
Output:
Thread-1 : 4
Thread-1 : 3
Thread-1 : 2
Thread-1 : 1
Thread-1 : 0
Thread-0 : 4
Thread-0 : 3
Thread-0 : 2
Thread-0 : 1
Thread-0 : 0
对于对象SynchronizedDemo.class
,实际上就是SynchronizedDemo
这个类,也就是对类进行加锁。
可以通过上述的运行结果可以得到一下结论:
在多线程环境下:
- 多次调用静态的同步方法,会进行阻塞
- 不同对象调用被类锁的同步代码块,会进行阻塞
3.类锁和对象锁同时存在
在多线程环境下,同时调用同一对象的类锁和对象锁
public class SynchronizedDemo {
public static synchronized void synTestStatic() {
int i = 5;
while (i-- > 0) {
System.out.println(Thread.currentThread().getName() + " : " + i);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public synchronized void synTest() {
int i = 5;
while (i-- > 0) {
System.out.println(Thread.currentThread().getName() + " : " + i);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
SynchronizedDemo demo1 = new SynchronizedDemo();
Thread t1 = new Thread(() -> {
demo1.synTest();
});
Thread t2 = new Thread(() -> {
SynchronizedDemo.synTestStatic();
});
t1.start();
t2.start();
}
}
Output:
Thread-1 : 4
Thread-0 : 4
Thread-1 : 3
Thread-0 : 3
Thread-1 : 2
Thread-0 : 2
Thread-1 : 1
Thread-0 : 1
Thread-1 : 0
Thread-0 : 0
我们可以到看到,在多线程环境下,类锁和对象锁同时存在的情况下,多线程访问时不会阻塞,因为他们不是同一个锁。
可以通过上述的运行结果可以得到一下结论:
在多线程环境下:
- 类锁和对象锁同时存在的情况下,不会发生阻塞