線程安全性(一) 原子性 基于鎖的實現(xiàn) - synchronized 修飾代碼塊

鎖概述

鎖是Java中除了AtomicXXX類之外,另一種實現(xiàn)原子性的方式,典型的兩種基于鎖實現(xiàn)原子性的方式是synchronized關鍵字和基于Lock接口的實現(xiàn)類;

synchronized關鍵字加鎖
  • 依賴于JVM的鎖實現(xiàn),用synchronized關鍵字加鎖;
  • synchronized關鍵字加的鎖是不可中斷鎖,適合競爭不激烈,可讀性好;
  • synchronized關鍵字加的鎖在競爭激烈時性能下降的非常快;
Lock接口的實現(xiàn)類
  • 依賴于特殊的CPU指令,比如JDK中的java.util.concurrent.locks.Lock接口,比較有代表性的實現(xiàn)類有ReentrantLock;
  • Lock接口的實現(xiàn)類加的鎖是可中斷鎖,競爭激烈時能維持常態(tài);

synchronized能修飾的四種對象

  • 修飾代碼塊:被修飾的代碼稱作同步語句塊,其作用范圍是大括號括起來的代碼,作用于調用的對象(可以自己指定鎖定對象);如果代碼塊就是整個方法體,那么和修飾方式是等同的;
  • 修飾方法:被修飾的方法稱為同步方法,作用范圍是整個方法,作用于調用的對象(this);
  • 修飾靜態(tài)方法:作用范圍是整個靜態(tài)方法,作用于這個類的所有對象,本質是對 Class 對象上鎖;
  • 修飾類:作用范圍是synchronized后面的括號括起來的部分,作用于這個類的所有對象,本質是對 Class 對象上鎖

synchronized修飾代碼塊示例 01 - 正例

  • 同一個對象不同線程中訪問同步代碼塊,同步代碼塊對同一個調用對象是起到同步作用的;
import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

@Slf4j
public class SynchronizedCodeBlock {

    public void test1(int j) {
        synchronized (this) {
            for (int i = 0; i < 10; i++) {
                log.info("synchronized block object {} - {}", j, i);
            }
        }
    }

    public static void main(String[] args) {
        testCodeBlockSync();
    }

    private static void testCodeBlockSync() {
        SynchronizedCodeBlock example1 = new SynchronizedCodeBlock();
        ExecutorService service = Executors.newCachedThreadPool();
        service.execute(() -> {
            example1.test1(1);
        });
        service.execute(() -> {
            example1.test1(1);
        });
    }

}

輸出:

  • 同一個對象所在的2個線程按順序先后執(zhí)行;
17:06:51.597 [pool-1-thread-1] INFO com.example.concurrency.example.SynchronizedCodeBlock - synchronized block object 1 - 0
17:06:51.602 [pool-1-thread-1] INFO com.example.concurrency.example.SynchronizedCodeBlock - synchronized block object 1 - 1
17:06:51.602 [pool-1-thread-1] INFO com.example.concurrency.example.SynchronizedCodeBlock - synchronized block object 1 - 2
17:06:51.602 [pool-1-thread-1] INFO com.example.concurrency.example.SynchronizedCodeBlock - synchronized block object 1 - 3
17:06:51.602 [pool-1-thread-1] INFO com.example.concurrency.example.SynchronizedCodeBlock - synchronized block object 1 - 4
17:06:51.602 [pool-1-thread-1] INFO com.example.concurrency.example.SynchronizedCodeBlock - synchronized block object 1 - 5
17:06:51.602 [pool-1-thread-1] INFO com.example.concurrency.example.SynchronizedCodeBlock - synchronized block object 1 - 6
17:06:51.602 [pool-1-thread-1] INFO com.example.concurrency.example.SynchronizedCodeBlock - synchronized block object 1 - 7
17:06:51.602 [pool-1-thread-1] INFO com.example.concurrency.example.SynchronizedCodeBlock - synchronized block object 1 - 8
17:06:51.602 [pool-1-thread-1] INFO com.example.concurrency.example.SynchronizedCodeBlock - synchronized block object 1 - 9
17:06:51.602 [pool-1-thread-2] INFO com.example.concurrency.example.SynchronizedCodeBlock - synchronized block object 1 - 0
17:06:51.602 [pool-1-thread-2] INFO com.example.concurrency.example.SynchronizedCodeBlock - synchronized block object 1 - 1
17:06:51.603 [pool-1-thread-2] INFO com.example.concurrency.example.SynchronizedCodeBlock - synchronized block object 1 - 2
17:06:51.603 [pool-1-thread-2] INFO com.example.concurrency.example.SynchronizedCodeBlock - synchronized block object 1 - 3
17:06:51.603 [pool-1-thread-2] INFO com.example.concurrency.example.SynchronizedCodeBlock - synchronized block object 1 - 4
17:06:51.603 [pool-1-thread-2] INFO com.example.concurrency.example.SynchronizedCodeBlock - synchronized block object 1 - 5
17:06:51.603 [pool-1-thread-2] INFO com.example.concurrency.example.SynchronizedCodeBlock - synchronized block object 1 - 6
17:06:51.603 [pool-1-thread-2] INFO com.example.concurrency.example.SynchronizedCodeBlock - synchronized block object 1 - 7
17:06:51.603 [pool-1-thread-2] INFO com.example.concurrency.example.SynchronizedCodeBlock - synchronized block object 1 - 8
17:06:51.603 [pool-1-thread-2] INFO com.example.concurrency.example.SynchronizedCodeBlock - synchronized block object 1 - 9

synchronized修飾代碼塊示例 02 - 反例

  • 不同對象不同線程中訪問同步代碼塊,同步代碼塊對不同調用對象是不能起到同步作用的;
import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

@Slf4j
public class SynchronizedCodeBlock {

    public void test1(int j) {
        synchronized (this) {
            for (int i = 0; i < 10; i++) {
                log.info("synchronized block object {} - {}", j, i);
            }
        }
    }

    public static void main(String[] args) {
        testCodeBlockNotSync();
    }

    private static void testCodeBlockNotSync() {
        SynchronizedCodeBlock example1 = new SynchronizedCodeBlock();
        SynchronizedCodeBlock example2 = new SynchronizedCodeBlock();
        ExecutorService service = Executors.newCachedThreadPool();
        service.execute(() -> {
            example1.test1(1);
        });
        service.execute(() -> {
            example2.test1(2);
        });
    }

}

輸出:

  • 亂序的,兩個對象所在的線程同時執(zhí)行;
17:07:52.278 [pool-1-thread-2] INFO com.example.concurrency.example.SynchronizedCodeBlock - synchronized block object 2 - 0
17:07:52.278 [pool-1-thread-1] INFO com.example.concurrency.example.SynchronizedCodeBlock - synchronized block object 1 - 0
17:07:52.284 [pool-1-thread-2] INFO com.example.concurrency.example.SynchronizedCodeBlock - synchronized block object 2 - 1
17:07:52.284 [pool-1-thread-1] INFO com.example.concurrency.example.SynchronizedCodeBlock - synchronized block object 1 - 1
17:07:52.285 [pool-1-thread-1] INFO com.example.concurrency.example.SynchronizedCodeBlock - synchronized block object 1 - 2
17:07:52.285 [pool-1-thread-2] INFO com.example.concurrency.example.SynchronizedCodeBlock - synchronized block object 2 - 2
17:07:52.285 [pool-1-thread-1] INFO com.example.concurrency.example.SynchronizedCodeBlock - synchronized block object 1 - 3
17:07:52.285 [pool-1-thread-2] INFO com.example.concurrency.example.SynchronizedCodeBlock - synchronized block object 2 - 3
17:07:52.285 [pool-1-thread-1] INFO com.example.concurrency.example.SynchronizedCodeBlock - synchronized block object 1 - 4
17:07:52.285 [pool-1-thread-2] INFO com.example.concurrency.example.SynchronizedCodeBlock - synchronized block object 2 - 4
17:07:52.285 [pool-1-thread-1] INFO com.example.concurrency.example.SynchronizedCodeBlock - synchronized block object 1 - 5
17:07:52.285 [pool-1-thread-1] INFO com.example.concurrency.example.SynchronizedCodeBlock - synchronized block object 1 - 6
17:07:52.285 [pool-1-thread-1] INFO com.example.concurrency.example.SynchronizedCodeBlock - synchronized block object 1 - 7
17:07:52.285 [pool-1-thread-2] INFO com.example.concurrency.example.SynchronizedCodeBlock - synchronized block object 2 - 5
17:07:52.285 [pool-1-thread-1] INFO com.example.concurrency.example.SynchronizedCodeBlock - synchronized block object 1 - 8
17:07:52.285 [pool-1-thread-1] INFO com.example.concurrency.example.SynchronizedCodeBlock - synchronized block object 1 - 9
17:07:52.285 [pool-1-thread-2] INFO com.example.concurrency.example.SynchronizedCodeBlock - synchronized block object 2 - 6
17:07:52.285 [pool-1-thread-2] INFO com.example.concurrency.example.SynchronizedCodeBlock - synchronized block object 2 - 7
17:07:52.285 [pool-1-thread-2] INFO com.example.concurrency.example.SynchronizedCodeBlock - synchronized block object 2 - 8
17:07:52.285 [pool-1-thread-2] INFO com.example.concurrency.example.SynchronizedCodeBlock - synchronized block object 2 - 9

synchronized修飾代碼塊示例 03 - run() 方法中的 this 是誰?

  • 答案:是 new Thread(instance) 中的 instance;
  • SynchronizedCodeBlock02 的 run() 方法對 this 加鎖,創(chuàng)建 t1 和 t2 傳入的 Runnable 都是 instance,故線程 t1 和 t2 都是對 instance 加鎖,所以 t1 和 t2 的執(zhí)行可以同步;
package com.example.concurrency.example.sync;

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class SynchronizedCodeBlock02 implements Runnable{

    static SynchronizedCodeBlock02 instance = new SynchronizedCodeBlock02();

    @Override
    public void run() {
        synchronized (this) {
            log.info("{} start",Thread.currentThread());
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            log.info("{} end",Thread.currentThread());
        }
    }

    public static void main(String[] args) {
        Thread t1 = new Thread(instance);
        Thread t2 = new Thread(instance);

        t1.start();
        t2.start();

        while (t1.isAlive() || t2.isAlive()) {

        }
        log.info("{} end", Thread.currentThread());
    }

}

輸出:

21:50:16.719 [Thread-0] INFO com.example.concurrency.example.sync.SynchronizedCodeBlock02 - Thread[Thread-0,5,main] start
21:50:19.725 [Thread-0] INFO com.example.concurrency.example.sync.SynchronizedCodeBlock02 - Thread[Thread-0,5,main] end
21:50:19.725 [Thread-1] INFO com.example.concurrency.example.sync.SynchronizedCodeBlock02 - Thread[Thread-1,5,main] start
21:50:22.725 [Thread-1] INFO com.example.concurrency.example.sync.SynchronizedCodeBlock02 - Thread[Thread-1,5,main] end
21:50:22.725 [main] INFO com.example.concurrency.example.sync.SynchronizedCodeBlock02 - Thread[main,5,main] end
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

友情鏈接更多精彩內容