本篇我們來講解synchronized關鍵字的四種加鎖使用方式,並對其進行比較Github項目地址。
synchronized對方法加鎖
public class SynchronizedTest {
static class Thread1 extends Thread {
Utils mUtils;
Thread1(Utils utils) {
mUtils = utils;
}
@Override
public void run() {
super.run();
mUtils.method1();
}
}
static class Thread2 extends Thread {
Utils mUtils;
Thread2(Utils utils) {
mUtils = utils;
}
@Override
public void run() {
super.run();
mUtils.method2();
}
}
static class Utils {
synchronized void method1() {
System.out.println("method1 exec before -- threadName:" + Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("method1 exec after-- threadName:" + Thread.currentThread().getName());
}
synchronized void method2() {
System.out.println("method2 exec before -- threadName:" + Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("method2 exec after-- threadName:" + Thread.currentThread().getName());
}
}
public static void main(String[] args) {
Utils utils = new Utils();
Utils utils2 = new Utils();
Thread thread1 = new Thread1(utils);
Thread thread2 = new Thread2(utils2);
thread1.start();
thread2.start();
}
}
//執行結果
method1 exec before -- threadName:Thread-0
method2 exec before -- threadName:Thread-1
method1 exec after-- threadName:Thread-0
method2 exec after-- threadName:Thread-1
- 我們看到如果對方法整體加鎖的話,線程中如果不是同一個Utils對象的話,那麼線程0和線程1其實是並行的,並沒有達到同步的目的。如果我們僅僅修改運行代碼如下,使得傳入的對象爲同一個對象的話,那麼就是同步的效果,如下看到是線程1執行完畢,然後線程2纔開始執行:
public static void main(String[] args) {
Utils utils = new Utils();
Thread thread1 = new Thread1(utils);
Thread thread2 = new Thread2(utils);
thread1.start();
thread2.start();
}
//執行結果
method1 exec before -- threadName:Thread-0
method1 exec after-- threadName:Thread-0
method2 exec before -- threadName:Thread-1
method2 exec after-- threadName:Thread-1
使用synchronized(this)
public class SynchronizedTest2 {
static class Thread1 extends Thread {
Utils mUtils;
Thread1(Utils utils) {
mUtils = utils;
}
@Override
public void run() {
super.run();
mUtils.method1();
}
}
static class Thread2 extends Thread {
Utils mUtils;
Thread2(Utils utils) {
mUtils = utils;
}
@Override
public void run() {
super.run();
mUtils.method2();
}
}
static class Utils {
void method1() {
synchronized (Utils.this) {
System.out.println("method1 exec before -- threadName:" + Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("method1 exec after-- threadName:" + Thread.currentThread().getName());
}
}
void method2() {
synchronized (Utils.this) {
System.out.println("method2 exec before -- threadName:" + Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("method2 exec after-- threadName:" + Thread.currentThread().getName());
}
}
}
public static void main(String[] args) {
Utils utils = new Utils();
Thread thread1 = new Thread1(utils);
Thread thread2 = new Thread2(utils);
thread1.start();
thread2.start();
}
}
//執行結果
method1 exec before -- threadName:Thread-0
method1 exec after-- threadName:Thread-0
method2 exec before -- threadName:Thread-1
method2 exec after-- threadName:Thread-1
- synchronized(this)代碼塊加鎖,傳入同一個對象是同步,如果傳入的不是同一個對象,如下所示,則是異步的(線程0和線程1併發執行)。
public static void main(String[] args) {
Utils utils = new Utils();
Utils utils2 = new Utils();
Thread thread1 = new Thread1(utils);
Thread thread2 = new Thread2(utils2);
thread1.start();
thread2.start();
}
//執行結果
method1 exec before -- threadName:Thread-0
method2 exec before -- threadName:Thread-1
method1 exec after-- threadName:Thread-0
method2 exec after-- threadName:Thread-1
使用synchronized(xxx.class)
public class SynchronizedTest3 {
static class Thread1 extends Thread {
Utils mUtils;
Thread1(Utils utils) {
mUtils = utils;
}
@Override
public void run() {
super.run();
mUtils.method1();
}
}
static class Thread2 extends Thread {
Utils mUtils;
Thread2(Utils utils) {
mUtils = utils;
}
@Override
public void run() {
super.run();
mUtils.method2();
}
}
static class Utils {
void method1() {
synchronized (Utils.class) {
System.out.println("method1 exec before -- threadName:" + Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("method1 exec after-- threadName:" + Thread.currentThread().getName());
}
}
void method2() {
synchronized (Utils.class) {
System.out.println("method2 exec before -- threadName:" + Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("method2 exec after-- threadName:" + Thread.currentThread().getName());
}
}
}
public static void main(String[] args) {
Utils utils = new Utils();
Thread thread1 = new Thread1(utils);
Thread thread2 = new Thread2(utils);
thread1.start();
thread2.start();
}
}
//執行結果
method1 exec before -- threadName:Thread-0
method1 exec after-- threadName:Thread-0
method2 exec before -- threadName:Thread-1
method2 exec after-- threadName:Thread-1
- synchronized(xxx.class)加鎖方式, 不管線程中傳入的是不是同一個對象,都是同步,上面是同一個對象是同步的效果,下面看傳入的不是同一個對象的話,效果也是一樣的同步。
public static void main(String[] args) {
Utils utils = new Utils();
Utils utils2 = new Utils();
Thread thread1 = new Thread1(utils);
Thread thread2 = new Thread2(utils2);
thread1.start();
thread2.start();
}
//執行結果
method1 exec before -- threadName:Thread-0
method1 exec after-- threadName:Thread-0
method2 exec before -- threadName:Thread-1
method2 exec after-- threadName:Thread-1
靜態方法前加synchronized
因爲靜態方法可以直接用類名調用,所以就沒有實例化對象,從測試來看也是同步的效果。
public class SynchronizedTest4 {
static class Thread1 extends Thread {
@Override
public void run() {
super.run();
Utils.method1();
}
}
static class Thread2 extends Thread {
@Override
public void run() {
super.run();
Utils.method2();
}
}
static class Utils {
synchronized static void method1() {
System.out.println("method1 exec before -- threadName:" + Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("method1 exec after-- threadName:" + Thread.currentThread().getName());
}
synchronized static void method2() {
System.out.println("method2 exec before -- threadName:" + Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("method2 exec after-- threadName:" + Thread.currentThread().getName());
}
}
//synchronized(xxx.class)加鎖, 不管線程中傳入的是不是同一個對象,都是同步
public static void main(String[] args) {
new Thread1().start();
new Thread2().start();
}
}
//執行結果
method1 exec before -- threadName:Thread-0
method1 exec after-- threadName:Thread-0
method2 exec before -- threadName:Thread-1
method2 exec after-- threadName:Thread-1
總結:
synchronized加鎖方式 | 同步還是異步 |
---|---|
synchronized void methodName() | 線程中傳入的是同一個對象同步,否則異步 |
synchronized(this) | 線程中傳入的是同一個對象同步,否則異步 |
synchronized(xxx.class) | 不管線程中傳入的是不是同一個對象,都是同步 |
synchronized static void methodName() | 同步 |