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_20200625120855Lock
由于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都能達到鎖的目的,那有哪些不同的地方呢?
總結
以上是生活随笔為你收集整理的Java中synchronized和Lock的区别的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Java线程中wait、await、sl
- 下一篇: java美元兑换,(Java实现) 美元