电脑知识|欧美黑人一区二区三区|软件|欧美黑人一级爽快片淫片高清|系统|欧美黑人狂野猛交老妇|数据库|服务器|编程开发|网络运营|知识问答|技术教程文章 - 好吧啦网

您的位置:首頁技術文章
文章詳情頁

詳解Java并發編程之內置鎖(synchronized)

瀏覽:5日期:2022-08-15 16:17:13
簡介

synchronized在JDK5.0的早期版本中是重量級鎖,效率很低,但從JDK6.0開始,JDK在關鍵字synchronized上做了大量的優化,如偏向鎖、輕量級鎖等,使它的效率有了很大的提升。

synchronized的作用是實現線程間的同步,當多個線程都需要訪問共享代碼區域時,對共享代碼區域進行加鎖,使得每一次只能有一個線程訪問共享代碼區域,從而保證線程間的安全性。

因為沒有顯式的加鎖和解鎖過程,所以稱之為隱式鎖,也叫作內置鎖、監視器鎖。

如下實例,在沒有使用synchronized的情況下,多個線程訪問共享代碼區域時,可能會出現與預想中不同的結果。

public class Apple implements Runnable { private int appleCount = 5; @Override public void run() { eatApple(); } public void eatApple(){ appleCount--; System.out.println(Thread.currentThread().getName() + '吃了一個蘋果,還剩' + appleCount + '個蘋果'); } public static void main(String[] args) { Apple apple = new Apple(); Thread t1 = new Thread(apple, '小強'); Thread t2 = new Thread(apple, '小明'); Thread t3 = new Thread(apple, '小花'); Thread t4 = new Thread(apple, '小紅'); Thread t5 = new Thread(apple, '小黑'); t1.start(); t2.start(); t3.start(); t4.start(); t5.start(); }}

可能會輸出如下結果:

小強吃了一個蘋果,還剩3個蘋果小黑吃了一個蘋果,還剩3個蘋果小明吃了一個蘋果,還剩2個蘋果小花吃了一個蘋果,還剩1個蘋果小紅吃了一個蘋果,還剩0個蘋果

輸出結果異常的原因是eatApple方法里操作不是原子的,如當A線程完成appleCount的賦值,還沒有輸出,B線程獲取到appleCount的最新值,并完成賦值操作,然后A和B同時輸出。(A,B線程分別對應小黑、小強)

如果改下eatApple方法如下,還會不會有線程安全問題呢?

public void eatApple(){System.out.println(Thread.currentThread().getName() + '吃了一個蘋果,還剩' + --appleCount + '個蘋果');}

還是會有的,因為--appleCount不是原子操作,--appleCount可以用另外一種寫法表示:appleCount = appleCount - 1,還是有可能會出現以上的異常輸出結果。

synchronized的使用

synchronized分為同步方法和同步代碼塊兩種用法,當每個線程訪問同步方法或同步代碼塊區域時,首先需要獲得對象的鎖,搶到鎖的線程可以繼續執行,搶不到鎖的線程則阻塞,等待搶到鎖的線程執行完成后釋放鎖。

1.同步代碼塊

鎖的對象是object:

public class Apple implements Runnable { private int appleCount = 5; private Object object = new Object(); @Override public void run() { eatApple(); } public void eatApple(){//同步代碼塊,此時鎖的對象是object synchronized (object) { appleCount--; System.out.println(Thread.currentThread().getName() + '吃了一個蘋果,還剩' + appleCount + '個蘋果'); } } //...省略main方法}

2.同步方法,修飾普通方法

鎖的對象是當前類的實例對象:

public class Apple implements Runnable { private int appleCount = 5; @Override public void run() { eatApple(); } public synchronized void eatApple() { appleCount--; System.out.println(Thread.currentThread().getName() + '吃了一個蘋果,還剩' + appleCount + '個蘋果'); } //...省略main方法}

等價于以下同步代碼塊的寫法:

public void eatApple() {synchronized (this) {appleCount--;System.out.println(Thread.currentThread().getName() + '吃了一個蘋果,還剩' + appleCount + '個蘋果');}}

3.同步方法,修飾靜態方法

鎖的對象是當前類的class對象:

public class Apple implements Runnable { private static int appleCount = 5; @Override public void run() { eatApple(); } public synchronized static void eatApple() { appleCount--; System.out.println(Thread.currentThread().getName() + '吃了一個蘋果,還剩' + appleCount + '個蘋果'); } //...省略main方法}

等價于以下同步代碼塊的寫法:

public static void eatApple() {synchronized (Apple.class) {appleCount--;System.out.println(Thread.currentThread().getName() + '吃了一個蘋果,還剩' + appleCount + '個蘋果');}}

4.同步方法和同步代碼塊的區別

a.同步方法鎖的對象是當前類的實例對象或者當前類的class對象,而同步代碼塊鎖的對象可以是任意對象。

b.同步方法是使用synchronized修飾方法,而同步代碼塊是使用synchronized修飾共享代碼區域。同步代碼塊相對于同步方法來說粒度更細,鎖的區域更小,一般鎖范圍越小效率就越高。如下情況顯然同步代碼塊更適用:

public static void eatApple() {//不需要同步的耗時操作1//...synchronized (Apple.class) {appleCount--;System.out.println(Thread.currentThread().getName() + '吃了一個蘋果,還剩' + appleCount + '個蘋果');}//不需要同步的耗時操作2//...}

內置鎖的可重入性

內置鎖的可重入性是指當某個線程試圖獲取一個它已經持有的鎖時,它總是可以獲取成功。如下:

public static void eatApple() {synchronized (Apple.class) {synchronized (Apple.class) {synchronized (Apple.class) {appleCount--;System.out.println(Thread.currentThread().getName() + '吃了一個蘋果,還剩' + appleCount + '個蘋果');}}}}

如果鎖不是可重入的,那么假如某線程持有了該鎖,然后又需要等待持有該鎖的線程釋放鎖,這不就造成死鎖了嗎?

synchronized可以被繼承嗎?

synchronized不可以被繼承,如果子類中重寫后的方法需要實現同步,則需要手動添加synchronized關鍵字。

public class AppleParent { public synchronized void eatApple(){ }}public class Apple extends AppleParent implements Runnable { private int appleCount = 5; @Override public void run() { eatApple(); } @Override public void eatApple() { appleCount--; System.out.println(Thread.currentThread().getName() + '吃了一個蘋果,還剩' + appleCount + '個蘋果'); } //...省略main方法}基于內置鎖的等待和喚醒

基于內置鎖的等待和喚醒是使用Object類中的wait()和notify()或notifyAll()來實現的。這些方法的調用前提是已經持有對應的鎖,所以只能在同步方法或者同步代碼塊里調用。如果在沒有獲取到對應鎖的情況下調用則會拋出IllegalMonitorStateException異常。下面介紹下相關的幾個方法:

wait():使當前線程無限期地等待,直到另一個線程調用notify()或notifyAll()。

wait(long timeout):指定一個超時時間,超時時間過后線程將會被自動喚醒。線程也可以在超時時間之前被notify()或notifyAll()喚醒。注意,wait(0)等同于調用wait()。

wait(long timeout, int nanos):類似于wait(long timeout),主要區別是wait(long timeout, int nanos)提供了更高的精度。

notify():隨機喚醒一個在相同鎖對象上等待的線程。

notifyAll():喚醒所有在相同鎖對象上等待的線程。

一個簡單的等待喚醒實例:

public class Apple { //蘋果數量 private int appleCount = 0; /** * 買蘋果 */ public synchronized void getApple() { try { while (appleCount != 0) { wait(); } } catch (InterruptedException ex) { ex.printStackTrace(); } System.out.println(Thread.currentThread().getName() + '買了5個蘋果'); appleCount = 5; notify(); } /** * 吃蘋果 */ public synchronized void eatApple() { try { while (appleCount == 0) { wait(); } } catch (InterruptedException ex) { ex.printStackTrace(); } System.out.println(Thread.currentThread().getName() + '吃了1個蘋果'); appleCount--; notify(); }}

/** * 生產者,買蘋果 */public class Producer extends Thread{ private Apple apple; public Producer(Apple apple, String name){ super(name); this.apple = apple; } @Override public void run(){ while (true) apple.getApple(); }}/** * 消費者,吃蘋果 */public class Consumer extends Thread{ private Apple apple; public Consumer(Apple apple, String name){ super(name); this.apple = apple; } @Override public void run(){ while (true) apple.eatApple(); }}

public class Demo { public static void main(String[] args) { Apple apple = new Apple(); Producer producer = new Producer(apple,'小明'); Consumer consumer = new Consumer(apple, '小紅'); producer.start(); consumer.start(); }}

輸出結果:

小明買了5個蘋果小紅吃了1個蘋果小紅吃了1個蘋果小紅吃了1個蘋果小紅吃了1個蘋果小紅吃了1個蘋果小明買了5個蘋果小紅吃了1個蘋果 ......

到此這篇關于Java并發編程之內置鎖(synchronized)的文章就介紹到這了,更多相關Java內置鎖內容請搜索好吧啦網以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持好吧啦網!

標簽: Java
相關文章:
主站蜘蛛池模板: 土壤水分自动监测站-SM150便携式土壤水分仪-铭奥仪器 | 单柱拉力机-橡胶冲片机-哑铃裁刀-江都轩宇试验机械厂 | 找果网 | 苹果手机找回方法,苹果iPhone手机丢了找回,认准找果网! | 天一线缆邯郸有限公司_煤矿用电缆厂家_矿用光缆厂家_矿用控制电缆_矿用通信电缆-天一线缆邯郸有限公司 | 郑州宣传片拍摄-TVC广告片拍摄-微电影短视频制作-河南优柿文化传媒有限公司 | 压接机|高精度压接机|手动压接机|昆明可耐特科技有限公司[官网] 胶泥瓷砖胶,轻质粉刷石膏,嵌缝石膏厂家,腻子粉批发,永康家德兴,永康市家德兴建材厂 | 发电机价格|发电机组价格|柴油发电机价格|柴油发电机组价格网 | 生鲜配送系统-蔬菜食材配送管理系统-连锁餐饮订货配送软件-挪挪生鲜供应链管理软件 | 球形钽粉_球形钨粉_纳米粉末_难熔金属粉末-广东银纳官网 | 多米诺-多米诺世界纪录团队-多米诺世界-多米诺团队培训-多米诺公关活动-多米诺创意广告-多米诺大型表演-多米诺专业赛事 | 定量包装秤,吨袋包装称,伸缩溜管,全自动包装秤,码垛机器人,无锡市邦尧机械工程有限公司 | 湖南长沙商标注册专利申请,长沙公司注册代理记账首选美创! | 广东高华家具-公寓床|学生宿舍双层铁床厂家【质保十年】 | 沈阳庭院景观设计_私家花园_别墅庭院设计_阳台楼顶花园设计施工公司-【沈阳现代时园艺景观工程有限公司】 | 纸塑分离机-纸塑分离清洗机设备-压力筛-碎浆机厂家金双联环保 | 脱硝喷枪-氨水喷枪-尿素喷枪-河北思凯淋环保科技有限公司 | 深圳货架厂家_金丽声精品货架_广东金丽声展示设备有限公司官网 | YAGEO国巨电容|贴片电阻|电容价格|三星代理商-深圳市巨优电子有限公司 | 北京自然绿环境科技发展有限公司专业生产【洗车机_加油站洗车机-全自动洗车机】 | 深圳善跑体育产业集团有限公司_塑胶跑道_人造草坪_运动木地板 | 二手色谱仪器,十万分之一分析天平,蒸发光检测器,电位滴定仪-湖北捷岛科学仪器有限公司 | 锡膏喷印机-全自动涂覆机厂家-全自动点胶机-视觉点胶机-深圳市博明智控科技有限公司 | 彩超机-黑白B超机-便携兽用B超机-多普勒彩超机价格「大为彩超」厂家 | 深圳市超时尚职业培训学校,培训:月嫂,育婴,养老,家政;化妆,美容,美发,美甲. | 杭州顺源过滤机械有限公司官网-压滤机_板框压滤机_厢式隔膜压滤机厂家 | 阻垢剂-反渗透缓蚀阻垢剂厂家-山东鲁东环保科技有限公司 | 机床导轨_导轨板_滚轮导轨-上海旻佑精密机械有限公司 | 磁粉制动器|张力控制器|气胀轴|伺服纠偏控制器整套厂家--台灵机电官网 | 橡胶粉碎机_橡胶磨粉机_轮胎粉碎机_轮胎磨粉机-河南鼎聚重工机械制造有限公司 | 北京工业设计公司-产品外观设计-产品设计公司-千策良品工业设计 北京翻译公司-专业合同翻译-医学标书翻译收费标准-慕迪灵 | 外观设计_设备外观设计_外观设计公司_产品外观设计_机械设备外观设计_东莞工业设计公司-意品深蓝 | 单柱拉力机-橡胶冲片机-哑铃裁刀-江都轩宇试验机械厂 | 上海恒驭仪器有限公司-实验室平板硫化机-小型平板硫化机-全自动平板硫化机 | 东亚液氮罐-液氮生物容器-乐山市东亚机电工贸有限公司 | 楼承板-开口楼承板-闭口楼承板-无锡海逵| 硫化罐-胶管硫化罐-山东鑫泰鑫智能装备有限公司 | AGV无人叉车_激光叉车AGV_仓储AGV小车_AGV无人搬运车-南昌IKV机器人有限公司[官网] | 北京网站建设公司_北京网站制作公司_北京网站设计公司-北京爱品特网站建站公司 | 吹田功率计-长创耐压测试仪-深圳市新朗普电子科技有限公司 | 不锈钢复合板|钛复合板|金属复合板|南钢集团安徽金元素复合材料有限公司-官网 | pH污水传感器电极,溶解氧电极传感器-上海科蓝仪表科技有限公司 |