多線程編程-- part 3 多線程同步->synchronized關鍵字
多線程同時訪問一個資源,可以會產生不可預料的結果,所以為這個資源加鎖,訪問資源的第一個線程為其加鎖後,其他線程便不能在使用那個資源,直到鎖被解除。
舉個例子:
存款1000元,能取出800的時候我就取800,當我同時用兩個線程調用這個取錢操作時,有時可以取出1600元
static class HelloRunable implements Runnable{ private int money = 1000; //取出800元 int getMoney() { System.out.println("開始" + money); if(money > 800) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } money -= 800; System.out.println("我取了800元" + money); } System.out.println("結束" + money); return money; } @Override public void run() { money = getMoney(); } } public static void main(String[] args) { HelloRunable helloRunable = new HelloRunable(); Thread t = new Thread(helloRunable); Thread t1 = new Thread(helloRunable); t.start(); t1.start(); }
synchronized:
所以我們引入了同步機制,在取錢方法前面加入了synchronized關鍵字,該方法稱為同步方法,輸出結果為
java中每個對象都有一個鎖,或者叫做監視器(monitor),當一個線程訪問某個對象的synchronized方法時,將該對象上鎖,其他任何線程都無法再去訪問該對象的synchronized方法,直到之前的那個線程執行完方法之後,才會將對象鎖釋放,其他線程才可以在用該對象的synchronized方法。 註:這是給對象上鎖,如果是不同的對象則該對象之間沒有限制關系。
如果一個對象有多個synchronized方法,某一時刻某個線程已經進入到了synchronized方法,那麽在該方法沒有執行完前,其他線程是無法訪問該對象的任何synchronized方法的。
例如:
public static void main(String[] args) { Example example = new Example(); Thread t1 = new Thread1(example); Thread t2 = new Thread2(example); t1.start(); t2.start(); } static class Example { public synchronized void execute() { for (int i = 0; i < 20; ++i) { try { Thread.sleep((long) Math.random() * 1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Hello: " + i); } } public void execute2() { for (int i = 0; i < 20; ++i) { try { Thread.sleep((long) Math.random() * 1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("World: " + i); } } } static class Thread1 extends Thread { private Example example; public Thread1(Example example) { this.example = example; } @Override public void run() { example.execute(); } } static class Thread2 extends Thread { private Example example; public Thread2(Example example) { this.example = example; } @Override public void run() { example.execute2(); } }
當加上synchronized後,當同步方法未執行完時另一個同步方法不會執行
靜態的同步方法
當一個synchronized關鍵字修飾的方法同時被static修飾,非靜態的同步方法會將對象上鎖,但是靜態方法不屬於對象,而是屬於類,他會將這個方法所在的類的Class對象上鎖。
public static void main(String[] args) { Example example = new Example(); Thread t1 = new Thread1(example); example = new Example(); Thread t2 = new Thread2(example); t1.start(); t2.start(); } static class Example { public static synchronized void execute() { for (int i = 0; i < 20; ++i) { try { Thread.sleep((long) Math.random() * 1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Hello: " + i); } } public static synchronized void execute2() { for (int i = 0; i < 20; ++i) { try { Thread.sleep((long) Math.random() * 1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("World: " + i); } } } static class Thread1 extends Thread { private Example example; public Thread1(Example example) { this.example = example; } @Override public void run() { example.execute(); } } static class Thread2 extends Thread { private Example example; public Thread2(Example example) { this.example = example; } @Override public void run() { example.execute2(); } }
不加靜態方法方法:
當某個synchronized方法是static的,那麽當線程訪問該方法時,他鎖的並不是synchronized方法所在的對象,而是synchronized方法所在類的對應的class對象(java中,一個類無論有多少對象,這些對象會對應唯一的class對象,因此當線程分別訪問一個類的兩個對象的的兩個static synchronized方法時,他們的執行順序也是有順序的,也就是說一個線程先去執行,執行完釋放鎖,另一個線程再去執行)
synchronized代碼塊
synchronized(object) {
}
表示的是在執行過程中會將object對象上鎖(這個對象可以使任意類的對象,也可以使用this),可以自行規定上鎖對象
public class test { public static void main(String[] args) { Example example = new Example(); Thread t1 = new Thread1(example); Thread t2 = new Thread2(example); t1.start(); t2.start(); } } class Example { private Object object = new Object(); public void execute() { System.out.println("我開始了"); synchronized (object) { for (int i = 0; i < 20; ++i) { try { Thread.sleep((long) Math.random() * 1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Hello: " + i); } } } public void execute2() { System.out.println("我也開始了"); synchronized (object) { for (int i = 0; i < 20; ++i) { try { Thread.sleep((long) Math.random() * 1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("World: " + i); } } } } class Thread1 extends Thread { private Example example; public Thread1(Example example) { this.example = example; } @Override public void run() { example.execute(); } } class Thread2 extends Thread { private Example example; public Thread2(Example example) { this.example = example; } @Override public void run() { example.execute2(); } }
可以看到除了synchronized代碼塊裏的方法,其他是不會同步執行的。
synchronized方法是一種粗粒度的並發控制,某一時刻,只能有一個線程執行synchronized方法。
synchronized塊則是細粒度的並發控制,只會將塊中的代碼同步,方法內的其他代碼是可以被多個線程同時訪問到的
多線程編程-- part 3 多線程同步->synchronized關鍵字