日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

Java中synchronized和Lock的区别

發布時間:2025/3/19 java 22 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Java中synchronized和Lock的区别 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

前言

最近有一個需求是需要將數據庫的一些數據抽取出來放到文件文件命名方式為“FILENAME_yyyyMMddHHmmss”,例如FILENAME_20200625120011。計劃使用多線程去實現,這樣可能生成的文件名會有重復導致內容被覆蓋,因此考慮加鎖實現生成文件方式。這時候考慮到是使用synchronized還是Lock?

synchronized

synchronized是Java提供的一個并發控制的關鍵字。主要有兩種用法,分別是同步方法和同步代碼塊。也就是說,synchronized既可以修飾方法也可以修飾代碼塊。

代碼塊被synchronized修飾了,當一個線程獲取了對應的鎖,并執行該代碼塊時,其他線程便只能一直等待獲取鎖的線程釋放鎖,這里獲取鎖的線程釋放鎖只會有兩種情況:

  • 獲取鎖的線程執行完了該代碼塊,然后線程釋放對鎖的占有;
  • 線程執行發生異常,此時JVM會讓線程自動釋放鎖。

那么如果這個獲取鎖的線程由于要等待IO或者其他原因(比如調用sleep方法)被阻塞了,但是又沒有釋放鎖,其他線程便只能干巴巴地等待,試想一下,這多么影響程序執行效率。

使用synchronized實現創建文件的synchronizedDemoThread線程代碼如下:

public class synchronizedDemoThread implements Runnable {public static ConcurrentHashMap<String, String> concurrentHashMap = new ConcurrentHashMap<String, String>();@Overridepublic void run() {getThreadLog(getFileName());}public synchronized String getFileName() {try {String path = "FILENAME_" + new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());if (!concurrentHashMap.containsKey(path)) {synchronized (synchronizedDemoThread.class) {if (!concurrentHashMap.containsKey(path)) {getThreadLog("不存在此路徑,正在創建此路徑");concurrentHashMap.put(path, path);return path;} else {getThreadLog("此路徑已經存在了,需要等待創建");return getFileName();}}} else {getThreadLog("此文件路徑已經存在了,請等待創建。。。");this.wait(2000);return getFileName();}} catch (Exception e) {e.printStackTrace();}return "";} }

測試類代碼:

public class LockTest {public static void main(String[] args) throws InstantiationException, IllegalAccessException {synchronizedDemoThreadTest();}//創建線程池對象模擬多線程調用public static void synchronizedDemoThreadTest(){ExecutorService executor = Executors.newCachedThreadPool();for (int i = 0; i < 5; i++) {executor.execute(new synchronizedDemoThread());}executor.shutdown();}//獲取線程名和時間public static void getThreadLog(String logContent) {StringBuffer stringBuffer = new StringBuffer();stringBuffer.append("[");stringBuffer.append(Thread.currentThread().getName());stringBuffer.append(" ");stringBuffer.append(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date()));stringBuffer.append("]");stringBuffer.append(logContent);System.out.println(stringBuffer.toString());} }

測試日志:

[pool-1-thread-4 2020-06-25 12:08:47.004]不存在此路徑,正在創建此路徑 [pool-1-thread-4 2020-06-25 12:08:47.005]FILENAME_20200625120847 [pool-1-thread-1 2020-06-25 12:08:47.007]此文件路徑已經存在了,請等待創建。。。 [pool-1-thread-2 2020-06-25 12:08:47.009]此文件路徑已經存在了,請等待創建。。。 [pool-1-thread-3 2020-06-25 12:08:47.008]此文件路徑已經存在了,請等待創建。。。 [pool-1-thread-5 2020-06-25 12:08:47.010]此文件路徑已經存在了,請等待創建。。。 [pool-1-thread-1 2020-06-25 12:08:49.034]不存在此路徑,正在創建此路徑 [pool-1-thread-1 2020-06-25 12:08:49.037]FILENAME_20200625120849 [pool-1-thread-3 2020-06-25 12:08:49.038]此文件路徑已經存在了,請等待創建。。。 [pool-1-thread-2 2020-06-25 12:08:49.038]此文件路徑已經存在了,請等待創建。。。 [pool-1-thread-5 2020-06-25 12:08:49.039]此文件路徑已經存在了,請等待創建。。。 [pool-1-thread-3 2020-06-25 12:08:51.057]不存在此路徑,正在創建此路徑 [pool-1-thread-3 2020-06-25 12:08:51.060]FILENAME_20200625120851 [pool-1-thread-2 2020-06-25 12:08:51.066]此文件路徑已經存在了,請等待創建。。。 [pool-1-thread-5 2020-06-25 12:08:51.066]此文件路徑已經存在了,請等待創建。。。 [pool-1-thread-2 2020-06-25 12:08:53.066]不存在此路徑,正在創建此路徑 [pool-1-thread-2 2020-06-25 12:08:53.066]FILENAME_20200625120853 [pool-1-thread-5 2020-06-25 12:08:53.067]此文件路徑已經存在了,請等待創建。。。 [pool-1-thread-5 2020-06-25 12:08:55.069]不存在此路徑,正在創建此路徑 [pool-1-thread-5 2020-06-25 12:08:55.069]FILENAME_20200625120855

Lock

由于Lock是java.util.concurrent包下提供的一套互斥鎖,相比Synchronized,Lock類提供了一些高級功能,主要有以下3項:

  • 等待可中斷,持有鎖的線程長期不釋放的時候,正在等待的線程可以選擇放棄等待,這相當于Synchronized來說可以避免出現死鎖的情況。通過lock.lockInterruptibly()來實現這個機制。
  • 公平鎖,多個線程等待同一個鎖時,必須按照申請鎖的時間順序獲得鎖,Synchronized鎖非公平鎖,ReentrantLock默認的構造函數是創建的非公平鎖,可以通過參數true設為公平鎖,但公平鎖表現的性能不是很好
  • 鎖綁定多個條件,一個ReentrantLock對象可以同時綁定對個對象。ReenTrantLock提供了一個Condition(條件)類,用來實現分組喚醒需要喚醒的線程們,而不是像synchronized要么隨機喚醒一個線程要么喚醒全部線程。

使用Lock實現創建文件的LockDemoThread線程代碼如下:

public class LockDemoThread implements Runnable {//定義一個concurrentHashMap用來存放文件名public static ConcurrentHashMap<String, String> concurrentHashMap = new ConcurrentHashMap<String, String>();//定義一個Lock對象public static Lock lock = new ReentrantLock();@Overridepublic void run() {getThreadLog(getFileName());}//獲取文件名public synchronized String getFileName() {try {lock.lock();String path = "FILENAME_" + new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());if (!concurrentHashMap.containsKey(path)) {getThreadLog("不存在此路徑,重新創建");concurrentHashMap.put(path, path);return path;} else {getThreadLog("此文件路徑已經存在了,請等待創建。。。");this.wait(2000);return getFileName();}} catch (Exception e) {e.printStackTrace();} finally {lock.unlock();}return "";} }

測試類代碼:

public class LockTest {public static void main(String[] args) throws InstantiationException, IllegalAccessException {LockDemoThreadTest();//synchronizedDemoThreadTest();}//創建線程池對象模擬多線程調用public static void LockDemoThreadTest(){ExecutorService executor = Executors.newCachedThreadPool();for (int i = 0; i < 5; i++) {executor.execute(new LockDemoThread());}executor.shutdown();}//獲取線程名和時間public static void getThreadLog(String logContent) {StringBuffer stringBuffer = new StringBuffer();stringBuffer.append("[");stringBuffer.append(Thread.currentThread().getName());stringBuffer.append(" ");stringBuffer.append(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date()));stringBuffer.append("]");stringBuffer.append(logContent);System.out.println(stringBuffer.toString());} }

打印日志:

[pool-1-thread-1 2020-06-25 13:43:37.009]不存在此路徑,重新創建 [pool-1-thread-2 2020-06-25 13:43:37.013]此文件路徑已經存在了,請等待創建。。。 [pool-1-thread-1 2020-06-25 13:43:37.011]FILENAME_20200625134337 [pool-1-thread-2 2020-06-25 13:43:39.054]不存在此路徑,重新創建 [pool-1-thread-3 2020-06-25 13:43:39.055]此文件路徑已經存在了,請等待創建。。。 [pool-1-thread-2 2020-06-25 13:43:39.056]FILENAME_20200625134339 [pool-1-thread-3 2020-06-25 13:43:41.056]不存在此路徑,重新創建 [pool-1-thread-3 2020-06-25 13:43:41.057]FILENAME_20200625134341 [pool-1-thread-4 2020-06-25 13:43:41.057]此文件路徑已經存在了,請等待創建。。。 [pool-1-thread-4 2020-06-25 13:43:43.058]不存在此路徑,重新創建 [pool-1-thread-5 2020-06-25 13:43:43.060]此文件路徑已經存在了,請等待創建。。。 [pool-1-thread-4 2020-06-25 13:43:43.060]FILENAME_20200625134343 [pool-1-thread-5 2020-06-25 13:43:45.062]不存在此路徑,重新創建 [pool-1-thread-5 2020-06-25 13:43:45.062]FILENAME_20200625134345

總結一下

我們知道了synchronized和Lock都能達到鎖的目的,那有哪些不同的地方呢?

  • 如果獲取線程需要等待IO或者其他(比如調用sleep方法)被阻塞了,但是沒有釋放鎖,如果使用synchronized則其他線程一直無限期等待下去。這種場景適合使用Lock作為鎖。
  • 當有多個線程讀寫文件時,讀操作和寫操作會發生沖突現象,寫寫操作會發生沖突現象,但是讀讀操作不會發生沖突現象。但是采用synchronized關鍵字來實現同步的話,如果多個線程都只是進行讀操作,所以當一個線程在進行讀操作時,其他線程只能等待無法進行讀操作。這種場景適合使用Lock作為鎖。
  • 通過Lock可以知道線程有沒有成功獲取到鎖。這個是synchronized無法辦到的。
  • Lock不是Java語言內置的,synchronized是Java語言的關鍵字,因此是內置特性。Lock是一個類,通過這個類可以實現同步訪問。
  • Lock和synchronized有一點非常大的不同,采用synchronized不需要用戶去手動釋放鎖,當synchronized方法或者synchronized代碼塊執行完之后,系統會自動讓線程釋放對鎖的占用;而Lock則必須要用戶去手動釋放鎖,如果沒有主動釋放鎖,就有可能導致出現死鎖現象。
  • 總結

    以上是生活随笔為你收集整理的Java中synchronized和Lock的区别的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。