- 刚学到这里的时候很困惑;只是简单知道:1、synchronized 加到 static 方法前面是给class 加锁,即类锁;2、而synchronized 加到非静态方法前面是给对象上锁,即对象锁。
- 下面我就从代码的角度演示一下二者不同的现象。
1、对象锁和类锁是不同的锁,所以多个线程同时执行这2个不同锁的方法时,是异步的。
- 创建一个类Task2,在其中定义三个方法 doLongTimeTaskA和doLongTimeTaskB是类锁,
- 而doLongTimeTaskC是对象锁。
- 再创建3个线程,每个线程中调用调用该类对象锁和类锁的方法。
- 在main函数中执行abc三个线程的代码。依次调用。
public class Task2 {
public synchronized static void doLongTimeTaskA() {
System.out.println("name = " + Thread.currentThread().getName() + ", begain ,"+ new Date());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("name = " + Thread.currentThread().getName() + ", end ,"+ new Date());
}
public synchronized static void doLongTimeTaskB() {
System.out.println("name = " + Thread.currentThread().getName() + ", begain ,"+ new Date());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("name = " + Thread.currentThread().getName() + ", end ,"+ new Date());
}
public synchronized void doLongTimeTaskC() {
System.out.println("name = " + Thread.currentThread().getName() + ", begain ,"+ new Date());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("name = " + Thread.currentThread().getName() + ", end ,"+ new Date());
}
static class ThreadA extends Thread{
private Task2 mTask2;
public ThreadA(Task2 tk){
mTask2 = tk;
}
@Override
public void run() {
mTask2.doLongTimeTaskA();
}
}
static class ThreadB extends Thread{
private Task2 mTask2;
public ThreadB(Task2 tk){
mTask2 = tk;
}
@Override
public void run() {
mTask2.doLongTimeTaskB();
}
}
static class ThreadC extends Thread{
private Task2 mTask2;
public ThreadC(Task2 tk){
mTask2 = tk;
}
@Override
public void run() {
mTask2.doLongTimeTaskC();
}
}
public static void main(String[] args) {
Task2 mTask2 = new Task2();
ThreadA ta = new ThreadA(mTask2);
ThreadB tb = new ThreadB(mTask2);
ThreadC tc = new ThreadC(mTask2);
ta.setName("A");
tb.setName("B");
tc.setName("C");
ta.start();
tb.start();
tc.start();
}
}
执行的结果如下:
name = A, begain ,Sat Jun 11 09:45:49 CST 2022
name = C, begain ,Sat Jun 11 09:45:49 CST 2022
name = C, end ,Sat Jun 11 09:45:50 CST 2022
name = A, end ,Sat Jun 11 09:45:50 CST 2022
name = B, begain ,Sat Jun 11 09:45:50 CST 2022
name = B, end ,Sat Jun 11 09:45:51 CST 2022
大家在跑的时候可以多跑几个例子,因为并不是每次跑该程序的结果都是一样的
多跑几次结合数据我们可以发现由于doLongTimeTaskA方法和doLongTimeTaskB方法都是用static修饰的synchronize类锁,即同一个锁,所以 A和B是按顺序执行(类的层面上应该是整个进程唯一的),即同步的。
而doLongTimeTaskC是对象锁,和A/B不是同一种锁,所以C和A、B是 异步执行的。(A、B、C代指上面的3中方法,A、B为类锁, C为对象锁即没有static修饰的方法)。
2、我们知道对象锁要想保持同步执行,那么锁住的必须是同一个对象。下面就修改下上面的代码来证明:
- 在Task.java中修改ThreadA 和 ThreadB 中run代码,使ABC三个线程都调用doLongTimeTaskC()方法:
@Override
public void run() {
mTask2.doLongTimeTaskC();
}
- 此时的main方法如下:
public static void main(String[] args) {
Task2 mTask2 = new Task2();
Task2 mTask3 = new Task2();
Task2 mTask4 = new Task2();
ThreadA ta = new ThreadA(mTask2);
ThreadB tb = new ThreadB(mTask3);
ThreadC tc = new ThreadC(mTask4);
ta.setName("A");
tb.setName("B");
tc.setName("C");
ta.start();
tb.start();
tc.start();
}
- 输出结果如下:
- name = C, begain ,Sat Jun 11 10:25:20 CST 2022
- name = B, begain ,Sat Jun 11 10:25:20 CST 2022
- name = A, begain ,Sat Jun 11 10:25:20 CST 2022
- name = B, end ,Sat Jun 11 10:25:25 CST 2022
- name = C, end ,Sat Jun 11 10:25:25 CST 2022
- name = A, end ,Sat Jun 11 10:25:25 CST 2022
- 从结果来看,前三行先打印,表示三个线程同时异步执行,各自线程sleep后再打印后面的语句。
- 从代码中看,这三个线程拿到的对象并不是一个对象,各自线程有各自的对象,而方法C又是对象锁。
- 这个锁生效的前提是三个线程拿到的是同一个 Task2 对象。才能保证代码的同步执行。
public static void main(String[] args) {
Task2 mTask2 = new Task2();
ThreadA ta = new ThreadA(mTask2);
ThreadB tb = new ThreadB(mTask2);
ThreadC tc = new ThreadC(mTask2);
ta.setName("A");
tb.setName("B");
tc.setName("C");
ta.start();
tb.start();
tc.start();
}
- 可以看到线程A执行完自己run方法中调用的方法时,再去执行线程B,线程C 。
- 第二种方法如果想要保证这三个线程中run方法中调用的方法同步执行的话,就将线程ABC三个线程中run方法中调用的doLongTimeTaskC改为doLongTimeTaskA或者doLongTimeTaskB,因为A,B是类锁,只要是这个类,就只能有一把锁。类锁对所有的该类对象都能起作用。
- 不像对象,每个对象一把锁。
- 两种方法实现三个线程同步。
3、总结
- 1、如果多线程同时访问同一类的 类锁(synchronized 修饰的静态方法)以及对象锁(synchronized 修饰的非静态方法)这两个方法执行是异步的,原因:类锁和对象锁是两种不同的锁。
- 2、类锁对该类的所有对象都能起作用,而对象锁不能;对象锁只能对该对象起作用。
版权声明:本文为qq_35604488原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。