范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文

Java中的锁synchronized和Lock介绍

  在java中锁主要两类:内部锁synchronized和显示锁java.util.concurrent.locks.Lock。但细细想这貌似总结的也不太对。应该是由java内置的锁和concurrent实现的一系列锁。为什么这说,因为在java中一切都是对象,而java对每个对象都内置了一个锁,也可以称为对象锁/内部锁。通过synchronized来完成相关的锁操作。而因为synchronized的实现有些缺陷以及并发场景的复杂性,有人开发了一种显式的锁,而这些锁都是由java.util.concurrent.locks.Lock派生出来的。当然目前已经内置到了JDK1.5及之后的版本中。synchronized
  首先来看看用的比较多的synchronized,我的日常工作中大多用的也是它。synchronized是用于为某个代码块的提供锁机制,在java的对象中会隐式的拥有一个锁,这个锁被称为内置锁(intrinsic)或监视器锁(monitor locks)。线程在进入被synchronized保护的块之前自动获得这个锁,直到完成代码后(也可能是异常)自动释放锁。内置锁是互斥的,一个锁同时只能被一个线程持有,这也就会导致多线程下,锁被持有后后面的线程会阻塞。正因此实现了对代码的线程安全保证了原子性。 可重入
  既然java内置锁是互斥的而且后面的线程会导致阻塞,那么如果持有锁的线程再次进入试图获得这个锁时会如何呢?比如下面的一种情况: public class BaseClass {     public synchronized void do() {         System.out.println("is base");     } } public class SonClass extends BaseClass {     public synchronized void do() {       System.out.println("is son");       super.do();     } } SonClass son = new SonClass(); son.do();
  此时派生类的do方法除了会首先会持有一次锁,然后在调用super.do()的时候又会再一次进入锁并去持有,如果锁是互斥的话此时就应该死锁了。
  但结果却不是这样的,这是因为内部锁是具有可重入的特性,也就是锁实现了一个重入机制,引用计数管理。当线程1持有了对象的锁a,此时会对锁a的引用计算加1。然后当线程1再次获得锁a时,线程1还是持有锁a的那么计算会加1。当然每次退出同步块时会减1,直到为0时释放锁。 synchronized的一些特点修饰方法 public class BaseClass {     public synchronized void do() {         System.out.println("is base");     } }
  这种就是直接对某个方法进行加锁,进入这个方法块时需要获得锁。 修饰代码块 public class BaseClass {     private static Object lock = new Object();     public void do() {         synchronized (lock) {             System.out.println("is base");         }     } }
  这里就将锁的范围减少到了方法中的部分代码块,这对于锁的灵活性就提高了,毕竟锁的粒度控制也是锁的一个关键问题。 对象锁的类型
  经常看到一些代码中对synchronized使用比较特别,看一下如下的代码: public class BaseClass {     private static Object lock = new Object();     public void do() {         synchronized (lock) {         }     }     public synchronized void doVoid() {     }     public synchronized static void doStaticVoid() {     }     public  static void doStaticVoid() {         synchronized (BaseClass.class) {         }     }     }
  这里出现了四种情况:修饰代码块,修饰了方法,修饰了静态方法,修饰BaseClass的class对象。那这几种情况会有什么不同呢? 修饰代码块
  这种情况下我们创建了一个对象lock,在代码中使用synchronized(lock)这种形式,它的意思是使用lock这个对象的内置锁。这种情况下就将锁的控制交给了一个对象。当然这种情况还有一种方式: public void do() {     synchronized (this) {         System.out.println("is base");     } }
  使用this的意思就是当前对象的锁。这里也道出了内置锁的关键,我提供一把锁来保护这块代码,无论哪个线程来都面对同一把锁咯。 修饰对象方法
  这种直接修饰在方法是咱个情况?其实和修饰代码块类似,只不过此时默认使用的是this,也就是当前对象的锁。这样写起代码来倒也比较简单明确。前面说过了与修饰代码块的区别主要还是控制粒度的区别。 修饰静态方法
  静态方法难道有啥不一样吗?确实是不一样的,此时获取的锁已经不是this了,而this对象指向的class,也就是类锁。因为Java中的类信息会加载到方法常量区,全局是唯一的。这其实就提供了一种全局的锁。 修饰类的Class对象
  这种情况其实和修改静态方法时比较类似,只不过还是一个道理这种方式可以提供更灵活的控制粒度。 小结
  通过这几种情况的分析与理解,其实可以看内置锁的主要核心理念就是为一块代码提供一个可以用于互斥的锁,起到类似于开关的功能。
  java中对内置锁也提供了一些实现,主要的特点就是java都是对象,而每个对象都有锁,所以可以根据情况选择用什么样的锁。 java.util.concurrent.locks.Lock
  前面看了synchronized,大部分的情况下差不多就够啦,但是现在系统在并发编程中复杂性是越来越高,所以总是有许多场景synchronized处理起来会比较费劲。或者像中说的那样,concurrent中的lock是对内部锁的一种补充,提供了更多的一些高级特性。 java.util.concurrent.locks.Lock简单分析
  这个接口抽象了锁的主要操作,也因此让从Lock派生的锁具备了这些基本的特性:无条件的、可轮循的、定时的、可中断的。而且加锁与解锁的操作都是显式进行。下面是它的代码: public interface Lock {     void lock();     void lockInterruptibly() throws InterruptedException;     boolean tryLock();     boolean tryLock(long time, TimeUnit unit) throws InterruptedException;     void unlock();     Condition newCondition(); }ReentrantLock
  ReentrantLock就是可重入锁,连名字都这么显式。ReentrantLock提供了和synchronized类似的语义,但是ReentrantLock必须显式的调用,比如: public class BaseClass {     private Lock lock = new ReentrantLock();     public void do() {         lock.lock();         try {         //....         } finally {           lock.unlock();         }     } }
  这种方式对于代码阅读来说还是比较清楚的,只不过有个问题,就是如果忘了加try finally或忘 了写lock.unlock()的话导致锁没释放,很有可能导致一些死锁的情况,synchronized就没有这个风险。 trylock
  ReentrantLock是实现Lock接口,所以自然就拥有它的那些特性,其中就有trylock。trylock就是尝试获取锁,如果锁已经被其他线程占用那么立即返回false,如果没有那么应该占用它并返回true,表示拿到锁啦。
  另一个trylock方法里带了参数,这个方法的作用是指定一个时间,表示在这个时间内一直尝试去获得锁,如果到时间还没有拿到就放弃。
  因为trylock对锁并不是一直阻塞等待的,所以可以更多的规避死锁的发生。 lockInterruptibly
  lockInterruptibly是在线程获取锁时优先响应中断,如果检测到中断抛出中断异常由上层代码去处理。这种情况下就为一种轮循的锁提供了退出机制。为了更好理解可中断的锁操作,写了一个demo来理解。 package com.test; import java.util.Date; import java.util.concurrent.locks.ReentrantLock; public class TestLockInterruptibly { 	static ReentrantLock lock = new ReentrantLock(); 	public static void main(String[] args) { 		Thread thread1 = new Thread(new Runnable() { 			@Override 			public void run() { 				try { 					doPrint("thread 1 get lock."); 					do123(); 					doPrint("thread 1 end."); 				} catch (InterruptedException e) { 					doPrint("thread 1 is interrupted."); 				} 			} 		}); 		Thread thread2 = new Thread(new Runnable() { 			@Override 			public void run() { 				try { 					doPrint("thread 2 get lock."); 					do123(); 					doPrint("thread 2 end."); 				} catch (InterruptedException e) { 					doPrint("thread 2 is interrupted."); 				} 			} 		});  		thread1.setName("thread1"); 		thread2.setName("thread2"); 		thread1.start(); 		try { 			Thread.sleep(100);//等待一会使得thread1会在thread2前面执行 		} catch (InterruptedException e) { 			e.printStackTrace(); 		} 		thread2.start(); 	} 	private static void do123() throws InterruptedException { 		lock.lockInterruptibly(); 		doPrint(Thread.currentThread().getName() + " is locked."); 		try { 			doPrint(Thread.currentThread().getName() + " doSoming1...."); 			Thread.sleep(5000);//等待几秒方便查看线程的先后顺序 			doPrint(Thread.currentThread().getName() + " doSoming2...."); 			doPrint(Thread.currentThread().getName() + " is finished."); 		} finally { 			lock.unlock(); 		} 	}  	private static void doPrint(String text) { 		System.out.println((new Date()).toLocaleString() + " : " + text); 	} }
  上面代码中有两个线程,thread1比thread2更早启动,为了能看到拿锁的过程将上锁的代码sleep了5秒钟,这样就可以感受到前后两个线程进入获取锁的过程。最终上面的代码运行结果如下: 2016-9-28 15:12:56 : thread 1 get lock. 2016-9-28 15:12:56 : thread1 is locked. 2016-9-28 15:12:56 : thread1 doSoming1.... 2016-9-28 15:12:56 : thread 2 get lock. 2016-9-28 15:13:01 : thread1 doSoming2.... 2016-9-28 15:13:01 : thread1 is finished. 2016-9-28 15:13:01 : thread1 is unloaded. 2016-9-28 15:13:01 : thread2 is locked. 2016-9-28 15:13:01 : thread2 doSoming1.... 2016-9-28 15:13:01 : thread 1 end. 2016-9-28 15:13:06 : thread2 doSoming2.... 2016-9-28 15:13:06 : thread2 is finished. 2016-9-28 15:13:06 : thread2 is unloaded. 2016-9-28 15:13:06 : thread 2 end.
  可以看到,thread1先获得锁,一会thread2也来拿锁,但这个时候thread1已经占用了,所以thread2一直到thread1释放了锁后才拿到锁。
  **这段代码说明lockInterruptibly后面来获取锁的线程需要等待前面的锁释放了才能获得锁。**但这里还没有体现出可中断的特点,为此增加一些代码: thread2.start(); try { 	Thread.sleep(1000); } catch (InterruptedException e) { 	e.printStackTrace(); } //1秒后把线程2中断 thread2.interrupt();
  在thread2启动后调用一下thread2的中断方法,好吧,先跑一下代码看看结果: 2016-9-28 15:16:46 : thread 1 get lock. 2016-9-28 15:16:46 : thread1 is locked. 2016-9-28 15:16:46 : thread1 doSoming1.... 2016-9-28 15:16:46 : thread 2 get lock. 2016-9-28 15:16:47 : thread 2 is interrupted. <--直接就响应了线程中断 2016-9-28 15:16:51 : thread1 doSoming2.... 2016-9-28 15:16:51 : thread1 is finished. 2016-9-28 15:16:51 : thread1 is unloaded. 2016-9-28 15:16:51 : thread 1 end.
  和前面的代码相比可以发现,thread2正在等待thread1释放锁,但是这时thread2自己中断了,thread2后面的代码则不会再继续执行。 ReadWriteLock
  顾名思义就是读写锁,这种读-写锁的应用场景可以这样理解,比如一波数据大部分时候都是提供读取的,而只有比较少量的写操作,那么如果用互斥锁的话就会导致线程间的锁竞争。如果对于读取的时候大家都可以读,一旦要写入的时候就再将某个资源锁住。这样的变化就很好的解决了这个问题,使的读操作可以提高读的性能,又不会影响写的操作。
  一个资源可以被多个读者访问,或者被一个写者访问,两者不能同时进行。
  这是读写锁的抽象接口,定义一个读锁和一个写锁。 public interface ReadWriteLock {     /**      * Returns the lock used for reading.      *      * @return the lock used for reading      */     Lock readLock();     /**      * Returns the lock used for writing.      *      * @return the lock used for writing      */     Lock writeLock(); }
  在JDK里有个ReentrantReadWriteLock实现,就是可重入的读-写锁。ReentrantReadWriteLock可以构造为公平的或者非公平的两种类型。如果在构造时不显式指定则会默认的创建非公平锁。在非公平锁的模式下,线程访问的顺序是不确定的,就是可以闯入;可以由写者降级为读者,但是读者不能升级为写者。
  如果是公平锁模式,那么选择权交给等待时间最长的线程,如果一个读线程获得锁,此时一个写线程请求写入锁,那么就不再接收读锁的获取,直到写入操作完成。 简单的代码分析 在ReentrantReadWriteLock里其实维护的是一个sync的锁,只是看起来语义上像是一个读锁和写锁。看一下它的构造函数: public ReentrantReadWriteLock(boolean fair) {     sync = fair ? new FairSync() : new NonfairSync();     readerLock = new ReadLock(this);     writerLock = new WriteLock(this); }  //读锁的构造函数  protected ReadLock(ReentrantReadWriteLock lock) {     sync = lock.sync; } //写锁的构造函数 protected WriteLock(ReentrantReadWriteLock lock) {     sync = lock.sync; }
  可以看到实际上读/写锁在构造时都是引用的ReentrantReadWriteLock的sync锁对象。而这个Sync类是ReentrantReadWriteLock的一个内部类。总之读/写锁都是通过Sync来完成的。它是如何来协作这两者关系呢? //读锁的加锁方法 public void lock() {     sync.acquireShared(1); } //写锁的加锁方法 public void lock() {     sync.acquire(1); }
  区别主要是读锁获得的是共享锁,而写锁获取的是独占锁。这里有个点可以提一下,就是ReentrantReadWriteLock为了保证可重入性,共享锁和独占锁都必须支持持有计数和重入数。而ReentrantLock是使用state来存储的,而state只能存一个整形值,为了兼容两个锁的问题,所以将其划分了高16位和低16位分别存共享锁的线程数量或独占锁的线程数量或者重入计数。 其他
  写了一大篇感觉要写下去篇幅太长了,还有一些比较有用的锁: CountDownLatch
  就是设置一个同时持有的计数器,而调用者调用CountDownLatch的await方法时如果当前的计数器不为0就会阻塞,调用CountDownLatch的release方法可以减少计数,直到计数为0时调用了await的调用者会解除阻塞。 Semaphone
  信号量是一种通过授权许可的形式,比如设置100个许可证,这样就可以同时有100个线程同时持有锁,如果超过这个量后就会返回失败。

智能汽车软件化,数字技术赋能汽车产业实现智慧升级在汽车的电动化网联化智能化共享化的发展趋势下,汽车逐步由机械驱动向软件驱动过渡,汽车电子电气架构的变革也使得汽车的硬件体系趋于集中化,软件体系的差异化成为汽车价值差异化的关键。商业只剩最低配了!苹果最便宜iOS设备瞬间售罄不愧是库存克星前不久,苹果宣布正式停产iPod系列产品线,现款iPodtouch7售完即止,不再推出新款或补货。消息一出,马上吸引了大批消费者前去购买,目前iPodtouch7在美国官网已经全线一些非常有用的shell脚本实例用户猜数字!binbash脚本生成一个100以内的随机数,提示用户猜数字,根据用户的输入,提示用户猜对了,猜小了或猜大了,直至用户猜对脚本结束。RANDOM为系统自带的系统变量,值ISE2022开幕!MiniMicroLEDLED一体机成为亮点北京时间5月11日,一年一度全球最大的视听及集成系统展ISE2022于西班牙巴塞罗那正式开幕!以往ISE都在荷兰阿姆斯特丹举行,今年的ISE2022是移师西班牙巴塞罗那后的第一届展Lululemon以次充好新能源车充电贵五一消费维权类信息近千万条北京商报讯(记者赵述评蔺雨葳)5月12日,中消协发布五一消费维权舆情分析报告。监测发现,今年五一假期消费吐槽信息主要集中在交通出行产品质量网络游戏游乐项目四个方面,与假期主要消费场司马南公开请求联想起诉,柳传志为何不再一次打响捍卫联想保卫战项立刚时隔两年被特斯拉起诉,司马南公开喊话请求联想起诉。柳传志为何不复制2018年打响联想保卫战,是因为不屑还是因为心虚项立刚被特斯拉起诉,真正的该引起重视的一点是国内企业在面对如俄罗斯5G设备断供后,欲投入9000亿翻身,华为及时雨救场阅读之前,希望各位朋友能够点击右上方免费的关注,这样您每天都可以收到作者为您提供的新闻了,谢谢每一位读者的支持。作为第五代移动通信技术5G有着优秀的高效率低延迟的特性,在居家办公的国家计算机病毒应急处理中心监测发现18款违法移动应用国家计算机病毒应急处理中心近期通过互联网监测发现18款移动App存在隐私不合规行为,违反网络安全法个人信息保护法相关规定,涉嫌超范围采集个人隐私信息。1未向用户明示申请的全部隐私权想换个手机,红米产品线有点乱,该怎么选最近因手中苹果6s出现发热卡顿现象越来越难当大任,决定更换一部手机,多年来未关注国产机,尤其是小米的产品线进展,现在选一部国产机略微有些迷茫,心中偏向小米品牌,毕竟之前用过三部红米你会放弃苹果手机,来支持国产品牌吗?你会放弃苹果手机,来支持国产品牌吗?是满足我们的虚荣心,还是真的好用。随着科技技术的发展,我国的一些国产品牌有了长足的进步,但依然在国际大品牌之间存在着不小的差距。那么,我们如何才元宇宙正在催生第四第五产业以往市场上的经济结构都是按一产二产和三产进行划分的,实际上三产泛指服务业,随着市场的高速发展和新兴产业的不断孕育,尤其是当人类进入到元宇宙的发展阶段,简单的三产模型并不能直接表述出
大众机型中最好的游戏体验iQOONeo6玩明白骁龙8了iQOONeo6是一款让人充满了惊喜的高性能手机,在该机尚未发布之时,我们曾经猜测其会采用骁龙888芯片,毕竟它并非是旗舰级别定位,参考上一代的iQOONeo5的配置,骁龙888无科大讯飞2021年营收增速显著高于过去两年主要AI赛道产业红利进一步兑现e公司讯,4月22日下午,科大讯飞召开业绩说明会。公司总裁吴晓如介绍,2021年度科大讯飞营收183。1亿元,同比增长40。6,营收增速显著高于过去两年。公司主要AI赛道的产业红利又一研究院揭牌长安链协作网络朋友圈再扩大中国青年报客户端讯(中青报中青网记者邱晨辉)我国自主创新的区块链软硬件技术体系长安链的朋友圈再扩大。4月20日,智能建造区块链技术联合创新研究院在北京正式揭牌。该研究院由中国建筑集工信部尽快研究明确新能源汽车车购税优惠延续政策作为全球汽车产业绿色发展低碳转型的一个主要方向中国汽车产业高质量发展的一个战略选择,同时也是事关消费大势的重要组成部分,新能源汽车的产业发展一直备受各界瞩目。在国新办4月19日举办抖音发布新公告,部分品牌将被下架封禁4月20日消息,昨日,抖音电商发布商品品牌不一致专项治理公告。抖音表示,店铺在创建商品时,填写的品牌属性与该商品的实际品牌不一致,不利于商品关键信息的真实透传,还容易出现商标侵权等新能源汽车诸多感慨前不久受国际局势影响,油价大幅上升,得到通知前一晚的同事下班后不管自己的车油箱汽油满不满,先去加油站排起了队。等待途中免不了对即将提升的油价产生抱怨。而我却丝毫不为所动,不是因为不新能源汽车电池灯亮了什么意思新能源汽车电池灯亮了分两种情况。如果汽车启动之前电池灯亮,代表发电机没有发电电池没有充电。如果是汽车启动后电池指示灯亮,则说明发电机没有发电。电池灯亮起是为了提示驾驶员及时检查车辆即便华为向美国下跪,最佳的结局不过是成为另一个三星与台积电即便华为向美国下跪,最佳的结局不过是成为另一个三星与台积电,变成为华尔街财团赚钱的工具!如果华为跪上美国,允许美国资本入驻,那么,华为将变成像三星一样为美国资本为华尔街赚钱的工具,暴跌99。99!币茅LUNA陨落的背后,是不为人知的三个秘密币茅陨落!一个韩国人,竟然割了400亿的韭菜!对,这就是五月份的币圈大地震!号称币圈茅台的LUNA一夜几近归零,从最高的90美金跌到不足0。000015美金,400亿美金的资产就此美国第五部州隐私法来了!照片视频不属于生物特征数据据美国康涅狄格州议会官网消息,近日,在获得康涅狄格州立法机构表决通过十几天后,康涅狄格州消费者隐私法(下称隐私法)经州长签署后正式通过,将于明年7月1日起生效。议会官网在隐私法中,离子阱量子计算机的发展现状与趋势在权威科技智库型期刊刊出近日,离子阱量子计算机的发展现状与趋势在中国科学院主办的国家权威科技智库型期刊世界科技研究与发展正式刊出。该文献由启科量子研究员周卓俊中山大学罗乐教授担任通讯作者,汇聚了来自学术界