synchronized上鎖

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();
    }
}

運行結果:
在這裏插入圖片描述
上面這種鎖的不是對象,鎖的是類對象的這種鎖又稱爲全局鎖,相當於鎖住了代碼段,一次只有一個線程能執行該代碼段;

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章