synchronized的作用是:給對象加上鎖,就像是把一個籠子鎖上,這個籠子的鑰匙只有一把,一次只有一個人能拿到這把鑰匙開鎖;你只有拿到鎖的鑰匙,你才能打開籠子,否則你就只能等待,直到拿到鑰匙;
來一段代碼來看看加與不加synchronized的區別:
不加synchronized:
public class Test1 {
public static void main(String[] args) {
MyRunnable runnable = new MyRunnable();
Thread thread1 = new Thread(runnable, "線程1");
Thread thread2 = new Thread(runnable, "線程2");
thread1.start();
thread2.start();
}
}
class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "執行開始");
try {
Thread.sleep(1000);//讓線程休眠一下,等待1s後再執行,不會釋放對象鎖;
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "執行結束");
}
}
運行結果:
從結果可以看出來,線程1與線程2是同時進入到了run方法體中,因爲線程2還沒有執行完run方法,線程1就開始執行了:線程2的“線程執行結束”還沒有打印出來,線程1“線程1執行開始”就已經開始了;
加上synchronized:
public class Test2 {
public static void main(String[] args) {
MyRunnable runnable = new MyRunnable();
Thread thread1 = new Thread(runnable, "線程1");
Thread thread2 = new Thread(runnable, "線程2");
thread1.start();
thread2.start();
}
}
class MyRunnable implements Runnable {
@Override
public void run() {
synchronized (this) {
System.out.println(Thread.currentThread().getName() + "執行開始");
try {
Thread.sleep(1000);//讓線程休眠一下
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "執行結束");
}
}
}
運行結果:
從結果可以看出:線程1是等待線程2運行完畢後,纔開始執行的;
synchronized (this) 就是鎖住了某個對象,一次只有一個線程能拿到這個對象的鎖,進入代碼塊執行代碼,這個this就是 MyRunnable runnable = new MyRunnable()實例化的對象runnable ;
這種現象又稱爲同步處理;synchronized就可以實現同步處理;
同步指的是所有的線程不是一起進入到方法中執行,而是按照順序一個一個進來。
使用synchronized關鍵字處理同步現象有兩種模式:同步代碼塊、同步方法
用法如下:
1.同步代碼塊:
synchronized(對象)
(1) 對象 可以是:任意類的對象
如果鎖的是一個對象,即競爭一個對象,則可以使用 synchronized(this)
如:上面的例子
也可以鎖非this對象:synchronized(非this對象)
如:
public class Test2 {
public static void main(String[] args) {
MyRunnable runnable = new MyRunnable("hello");
Thread thread1 = new Thread(runnable, "線程1");
Thread thread2 = new Thread(runnable, "線程2");
thread1.start();
thread2.start();
}
}
class MyRunnable implements Runnable {
String str=new String();
public MyRunnable(String str) {
this.str = str;
}
@Override
public void run() {
synchronized (str) {//鎖str對象
System.out.println(Thread.currentThread().getName() + "執行開始");
try {
Thread.sleep(1000);//讓線程休眠一下
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "執行結束");
}
}
}
運行結果爲:
可見達到了同樣的效果,一次只有一個線程進入代碼塊執行,等待執行完成之後,才釋放對象鎖,其他線程再拿到對象鎖,纔去執行;
(2)對象 也可以是:類.class(類的反射)(只有一個)
如果此時有多個對象,但還是使用 synchronized(this)只鎖當前對象的話,恐怕會達不到同步處理的效果; 因爲 synchronized(this)只能鎖住一個對象;
例子:
public class Test2 {
public static void main(String[] args) {
MyRunnable runnable1 = new MyRunnable();
MyRunnable runnable2 = new MyRunnable();//多個對象
Thread thread1 = new Thread(runnable1, "線程1");
Thread thread2 = new Thread(runnable2, "線程2");
thread1.start();
thread2.start();
}
}
class MyRunnable implements Runnable {
@Override
public void run() {
synchronized (this) {
System.out.println(Thread.currentThread().getName() + "執行開始");
try {
Thread.sleep(1000);//讓線程休眠一下
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "執行結束");
}
}
}
運行j結果:
從運行結果可以看出:線程1與線程2是同時進入到代碼塊中執行的,此時synchronized(this)並沒有起作用;
如果鎖的是多個對象,這多個對象是同一個類的實例化對象,我們知道一個類的class對象只有一個,那麼鎖這個class對象不就可以了嗎,則可以用synchronized(類.class)
如:
public class Test2 {
public static void main(String[] args) {
MyRunnable runnable1 = new MyRunnable();
MyRunnable runnable2 = new MyRunnable();//多個對象
Thread thread1 = new Thread(runnable1, "線程1");
Thread thread2 = new Thread(runnable2, "線程2");
thread1.start();
thread2.start();
}
}
class MyRunnable implements Runnable {
@Override
public void run() {
synchronized (MyRunnable.class) {//類.class,一個類只有一個class對象
System.out.println(Thread.currentThread().getName() + "執行開始");
try {
Thread.sleep(1000);//讓線程休眠一下
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "執行結束");
}
}
}`
運行結果:
2.同步方法:
(1)一種是:修飾類成員方法 鎖的是當前對象this,實現了和synchronized(this)同樣的效果
如:
public class Test2 {
public static void main(String[] args) {
MyRunnable runnable = new MyRunnable();
Thread thread1 = new Thread(runnable, "線程1");
Thread thread2 = new Thread(runnable, "線程2");
thread1.start();
thread2.start();
}
}
class MyRunnable implements Runnable {
@Override
public synchronized void run() {//用synchronizd修飾方法,實現了和synchronized(this)同樣的效果
System.out.println(Thread.currentThread().getName() + "執行開始");
try {
Thread.sleep(1000);//讓線程休眠一下
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "執行結束");
}
}
運行結果:
如圖,也實現了同步處理,線程1完全執行完之後,線程2纔開始執行;
(2)一種是:修飾類方法(static) 鎖的是當前類的反射對象(一個類的反射對象只有一個) ,實現了與synchronized(類.class)同樣的效果;
如:
public class Test3 {
public static void main(String[] args) {
MyRunnable runnable1 = new MyRunnable();
MyRunnable runnable2 = new MyRunnable();
Thread thread1 = new Thread(runnable1, "線程1");
Thread thread2 = new Thread(runnable2, "線程2");
thread1.start();
thread2.start();
}
}
class MyRunnable implements Runnable{
public synchronized static void test(){//鎖的是當前MyRunnable 類的反射對象,
System.out.println(Thread.currentThread().getName() + "執行開始");
try {
Thread.sleep(1000);//讓線程休眠一下
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "執行結束");
}
@Override
public void run() {
MyRunnable.test();
}
}
運行結果:
上面這種鎖的不是對象,鎖的是類對象的這種鎖又稱爲全局鎖,相當於鎖住了代碼段,一次只有一個線程能執行該代碼段;