java中的Synchronized關鍵字理解

  • Synchronized是java的關鍵字,被java語音原生支持
  • 是最基本的互斥同步手段
  • 是併發編程中的元老級角色,是併發編程的必學內容

首先引入oracle對Synchronized的解釋:java

同步方法支持一種簡單的策略來防止線程干擾和內存一致性錯誤:若是一個對象對多個線程可見,則對該對象變量的全部讀取或寫入都是經過同步方法完成的。編程

簡單的來講就是:在多線程同時執行一段程序時,可以保證在同一時刻最多隻有一個線程執行該段程序代碼,以達到保證併發安全的效果安全

首先看看在不使用併發的後果 一個i++ 10000次結果會是多少呢多線程

public class Add implements Runnable {
    static Add instance = new Add();
    
    static int i = 0;
    
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(instance);
        Thread t2 = new Thread(instance);
        t1.start();
        t2.start();
        t1.join();  //等待線程執行完再執行後面的代碼
        t2.join();
        System.out.println(i);
    }
    
    @Override
    public void run() {
        for (int j = 0; j < 10000; j++) {
            i++;
        }
    }
}

複製代碼

上面的栗子每次運行的結果都不同,這是由於:兩個線程同時i++, 最後結果會比預計的少(i++是三個操做,讀取i, i++, 將i寫入內存),因此在多線程的狀況下每一步執行完都有可能被打斷,因此i值有可能沒寫進內存就執行另外一個線程的i++操做了,這種狀況咱們稱爲線程不安全。併發


Synchronized的兩個用法oracle

一、對象鎖:

    a、方法鎖(默認鎖對象爲this當前實例對象)

    b、同步代碼塊鎖(本身指定鎖對象)

二、類鎖:

    概念:Java類可能有不少個對象,但只有一個Class對象,鎖類時Class對象的鎖(鎖類只能在同一時刻被一個對象擁有)

    形式:

        a、使用關鍵字synchronized修飾靜態的方法

        b、synchronized(*.class)代碼塊
複製代碼

方法鎖的栗子:ide

public class SimpleExample implements Runnable {
    static SimpleExample instance1 = new SimpleExample();
    
    @Override
    public void run() {
        method1();
        method2();
    }
    
    public synchronized void method1() {
    	System.out.println(Thread.currentThread().getName() + " method1 start");
    	try {
          Thread.sleep(1000);
      } catch(InterruptedException e) {
          e.printStackTrace();
      }
      System.out.println(Thread.currentThread().getName() + " method1 end");
    }

    public synchronized void method2() {
    	System.out.println(Thread.currentThread().getName() + " method2 start");
    	try {
          Thread.sleep(1000);
      } catch(InterruptedException e) {
          e.printStackTrace();
      }
      System.out.println(Thread.currentThread().getName() + " method2 end");
    }
    
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(instance1);
        Thread t2 = new Thread(instance1);
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println("finished");
    }
}

/** 結果: Thread-0 method1 start Thread-0 method1 end Thread-0 method2 start Thread-0 method2 end Thread-1 method1 start Thread-1 method1 end Thread-1 method2 start Thread-1 method2 end */

複製代碼

對上述的結果有點不理解,(給兩個方法都加上鎖,當第一個線程執行完第一個方法以後,第一個方法不是會釋放掉,那麼第二個線程應該會有可能在第一個線程執行第二個方法以前去執行第一個方法,但結果老是第一個線程執行完再到第二個線程)本身的理解是這樣的:應該兩個方法上的synchronized關鍵字都是加在this上的,就像同步代碼塊那樣子,若是加在同一個對象上,那麼線程會依次執行。ps:但願高手能指教...this


使用關鍵字synchronized修飾靜態的方法,這樣能夠在一些全局方法上作到同步spa

public class SimpleExample implements Runnable {
    static SimpleExample instance1 = new SimpleExample();
    static SimpleExample instance2 = new SimpleExample();
    
    @Override
    public void run() {
        method1();
    }
    
    public static synchronized void method1() { //若是把static去掉,線程就不會是同步的
    	System.out.println(Thread.currentThread().getName() + " method1 start");
    	try {
          Thread.sleep(500);
      } catch(InterruptedException e) {
          e.printStackTrace();
      }
      System.out.println(Thread.currentThread().getName() + " method1 end");
    }
    
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(instance1);
        Thread t2 = new Thread(instance2);
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println("finished");
    }
}

/** 結果: Thread-0 method1 start Thread-0 method1 end Thread-1 method1 start Thread-1 method1 end */
複製代碼

總結

  • 一把鎖只能同時被一個線程獲取,沒有拿到鎖的線程必須等待
  • 每一個實例都對應有本身的一把鎖,不一樣實例之間互不影響;例外:鎖對象是×。class以及synchronied修飾的是static方法的時候,全部對象共用一把鎖
  • 不管是方法正常執行完畢或者方法拋出異常,都會釋放鎖
相關文章
相關標籤/搜索