Java中synchronized和Lock的区别
前言
最近有一個(gè)需求是需要將數(shù)據(jù)庫(kù)的一些數(shù)據(jù)抽取出來放到文件文件命名方式為“FILENAME_yyyyMMddHHmmss”,例如FILENAME_20200625120011。計(jì)劃使用多線程去實(shí)現(xiàn),這樣可能生成的文件名會(huì)有重復(fù)導(dǎo)致內(nèi)容被覆蓋,因此考慮加鎖實(shí)現(xiàn)生成文件方式。這時(shí)候考慮到是使用synchronized還是Lock?
synchronized
synchronized是Java提供的一個(gè)并發(fā)控制的關(guān)鍵字。主要有兩種用法,分別是同步方法和同步代碼塊。也就是說,synchronized既可以修飾方法也可以修飾代碼塊。
代碼塊被synchronized修飾了,當(dāng)一個(gè)線程獲取了對(duì)應(yīng)的鎖,并執(zhí)行該代碼塊時(shí),其他線程便只能一直等待獲取鎖的線程釋放鎖,這里獲取鎖的線程釋放鎖只會(huì)有兩種情況:
- 獲取鎖的線程執(zhí)行完了該代碼塊,然后線程釋放對(duì)鎖的占有;
- 線程執(zhí)行發(fā)生異常,此時(shí)JVM會(huì)讓線程自動(dòng)釋放鎖。
那么如果這個(gè)獲取鎖的線程由于要等待IO或者其他原因(比如調(diào)用sleep方法)被阻塞了,但是又沒有釋放鎖,其他線程便只能干巴巴地等待,試想一下,這多么影響程序執(zhí)行效率。
使用synchronized實(shí)現(xiàn)創(chuàng)建文件的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("不存在此路徑,正在創(chuàng)建此路徑");concurrentHashMap.put(path, path);return path;} else {getThreadLog("此路徑已經(jīng)存在了,需要等待創(chuàng)建");return getFileName();}}} else {getThreadLog("此文件路徑已經(jīng)存在了,請(qǐng)等待創(chuàng)建。。。");this.wait(2000);return getFileName();}} catch (Exception e) {e.printStackTrace();}return "";} }測(cè)試類代碼:
public class LockTest {public static void main(String[] args) throws InstantiationException, IllegalAccessException {synchronizedDemoThreadTest();}//創(chuàng)建線程池對(duì)象模擬多線程調(diào)用public static void synchronizedDemoThreadTest(){ExecutorService executor = Executors.newCachedThreadPool();for (int i = 0; i < 5; i++) {executor.execute(new synchronizedDemoThread());}executor.shutdown();}//獲取線程名和時(shí)間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());} }測(cè)試日志:
[pool-1-thread-4 2020-06-25 12:08:47.004]不存在此路徑,正在創(chuàng)建此路徑 [pool-1-thread-4 2020-06-25 12:08:47.005]FILENAME_20200625120847 [pool-1-thread-1 2020-06-25 12:08:47.007]此文件路徑已經(jīng)存在了,請(qǐng)等待創(chuàng)建。。。 [pool-1-thread-2 2020-06-25 12:08:47.009]此文件路徑已經(jīng)存在了,請(qǐng)等待創(chuàng)建。。。 [pool-1-thread-3 2020-06-25 12:08:47.008]此文件路徑已經(jīng)存在了,請(qǐng)等待創(chuàng)建。。。 [pool-1-thread-5 2020-06-25 12:08:47.010]此文件路徑已經(jīng)存在了,請(qǐng)等待創(chuàng)建。。。 [pool-1-thread-1 2020-06-25 12:08:49.034]不存在此路徑,正在創(chuàng)建此路徑 [pool-1-thread-1 2020-06-25 12:08:49.037]FILENAME_20200625120849 [pool-1-thread-3 2020-06-25 12:08:49.038]此文件路徑已經(jīng)存在了,請(qǐng)等待創(chuàng)建。。。 [pool-1-thread-2 2020-06-25 12:08:49.038]此文件路徑已經(jīng)存在了,請(qǐng)等待創(chuàng)建。。。 [pool-1-thread-5 2020-06-25 12:08:49.039]此文件路徑已經(jīng)存在了,請(qǐng)等待創(chuàng)建。。。 [pool-1-thread-3 2020-06-25 12:08:51.057]不存在此路徑,正在創(chuàng)建此路徑 [pool-1-thread-3 2020-06-25 12:08:51.060]FILENAME_20200625120851 [pool-1-thread-2 2020-06-25 12:08:51.066]此文件路徑已經(jīng)存在了,請(qǐng)等待創(chuàng)建。。。 [pool-1-thread-5 2020-06-25 12:08:51.066]此文件路徑已經(jīng)存在了,請(qǐng)等待創(chuàng)建。。。 [pool-1-thread-2 2020-06-25 12:08:53.066]不存在此路徑,正在創(chuàng)建此路徑 [pool-1-thread-2 2020-06-25 12:08:53.066]FILENAME_20200625120853 [pool-1-thread-5 2020-06-25 12:08:53.067]此文件路徑已經(jīng)存在了,請(qǐng)等待創(chuàng)建。。。 [pool-1-thread-5 2020-06-25 12:08:55.069]不存在此路徑,正在創(chuàng)建此路徑 [pool-1-thread-5 2020-06-25 12:08:55.069]FILENAME_20200625120855Lock
由于Lock是java.util.concurrent包下提供的一套互斥鎖,相比Synchronized,Lock類提供了一些高級(jí)功能,主要有以下3項(xiàng):
- 等待可中斷,持有鎖的線程長(zhǎng)期不釋放的時(shí)候,正在等待的線程可以選擇放棄等待,這相當(dāng)于Synchronized來說可以避免出現(xiàn)死鎖的情況。通過lock.lockInterruptibly()來實(shí)現(xiàn)這個(gè)機(jī)制。
- 公平鎖,多個(gè)線程等待同一個(gè)鎖時(shí),必須按照申請(qǐng)鎖的時(shí)間順序獲得鎖,Synchronized鎖非公平鎖,ReentrantLock默認(rèn)的構(gòu)造函數(shù)是創(chuàng)建的非公平鎖,可以通過參數(shù)true設(shè)為公平鎖,但公平鎖表現(xiàn)的性能不是很好
- 鎖綁定多個(gè)條件,一個(gè)ReentrantLock對(duì)象可以同時(shí)綁定對(duì)個(gè)對(duì)象。ReenTrantLock提供了一個(gè)Condition(條件)類,用來實(shí)現(xiàn)分組喚醒需要喚醒的線程們,而不是像synchronized要么隨機(jī)喚醒一個(gè)線程要么喚醒全部線程。
使用Lock實(shí)現(xiàn)創(chuàng)建文件的LockDemoThread線程代碼如下:
public class LockDemoThread implements Runnable {//定義一個(gè)concurrentHashMap用來存放文件名public static ConcurrentHashMap<String, String> concurrentHashMap = new ConcurrentHashMap<String, String>();//定義一個(gè)Lock對(duì)象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("不存在此路徑,重新創(chuàng)建");concurrentHashMap.put(path, path);return path;} else {getThreadLog("此文件路徑已經(jīng)存在了,請(qǐng)等待創(chuàng)建。。。");this.wait(2000);return getFileName();}} catch (Exception e) {e.printStackTrace();} finally {lock.unlock();}return "";} }測(cè)試類代碼:
public class LockTest {public static void main(String[] args) throws InstantiationException, IllegalAccessException {LockDemoThreadTest();//synchronizedDemoThreadTest();}//創(chuàng)建線程池對(duì)象模擬多線程調(diào)用public static void LockDemoThreadTest(){ExecutorService executor = Executors.newCachedThreadPool();for (int i = 0; i < 5; i++) {executor.execute(new LockDemoThread());}executor.shutdown();}//獲取線程名和時(shí)間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]不存在此路徑,重新創(chuàng)建 [pool-1-thread-2 2020-06-25 13:43:37.013]此文件路徑已經(jīng)存在了,請(qǐng)等待創(chuàng)建。。。 [pool-1-thread-1 2020-06-25 13:43:37.011]FILENAME_20200625134337 [pool-1-thread-2 2020-06-25 13:43:39.054]不存在此路徑,重新創(chuàng)建 [pool-1-thread-3 2020-06-25 13:43:39.055]此文件路徑已經(jīng)存在了,請(qǐng)等待創(chuàng)建。。。 [pool-1-thread-2 2020-06-25 13:43:39.056]FILENAME_20200625134339 [pool-1-thread-3 2020-06-25 13:43:41.056]不存在此路徑,重新創(chuàng)建 [pool-1-thread-3 2020-06-25 13:43:41.057]FILENAME_20200625134341 [pool-1-thread-4 2020-06-25 13:43:41.057]此文件路徑已經(jīng)存在了,請(qǐng)等待創(chuàng)建。。。 [pool-1-thread-4 2020-06-25 13:43:43.058]不存在此路徑,重新創(chuàng)建 [pool-1-thread-5 2020-06-25 13:43:43.060]此文件路徑已經(jīng)存在了,請(qǐng)等待創(chuàng)建。。。 [pool-1-thread-4 2020-06-25 13:43:43.060]FILENAME_20200625134343 [pool-1-thread-5 2020-06-25 13:43:45.062]不存在此路徑,重新創(chuàng)建 [pool-1-thread-5 2020-06-25 13:43:45.062]FILENAME_20200625134345總結(jié)一下
我們知道了synchronized和Lock都能達(dá)到鎖的目的,那有哪些不同的地方呢?
總結(jié)
以上是生活随笔為你收集整理的Java中synchronized和Lock的区别的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Java线程中wait、await、sl
- 下一篇: 吐血整理:Java线程池源码分析(基于J