【Java 基础 6】锁-sychronized和Lock

3 篇文章 0 订阅

sychronized

Java内置锁,基于Montier实现,通过moniterenter进入和moniterexit退出实现方法和代码块的同步,Montier依赖于操作系统底层的MutexLock(互斥锁)实现

作用方式

普通同步方法(实例方法),锁是当前实例对象 ,进入同步代码前要获得当前实例的锁
静态同步方法,锁是当前类的class对象 ,进入同步代码前要获得当前类class对象的锁
同步方法块,锁是括号里面的对象,对给定对象加锁,进入同步代码库前要获得给定对象的锁
 

Lock

实现Lock接口的锁有ReentrantReadWriterLock中的读锁和写锁、StampedLock中的读锁和写锁、ReentrantLock还有ConcurrentHashMap的Segment也是继承了ReentrantLock实现的分段锁(给每一个Segment加一个ReentrantLock锁)。

ReentrantReadWriterLock,ReentrantLock,信息量等内部维护一个继承AbstractQueuedSynchronizer(AQS, 抽象队列同步器)的同步锁Sycn

AQS

AQS内部主要维护了

1. Node head 2. Node tail 用于存储未获取到锁资源的线程,进入队列中的线程被LockSupport.park()阻塞,head为一个指向队列中第一个节点的头节点(本身不存储节点值)

3. int state记录被上锁的次数

4. Thread exclusiveOwnerThread 记录当前持有锁的线程

前3个属性被volatile修饰,基于CAS进行修改,当state被修改为0,exclusiveOwnerThread置空,同时LockSupport.unpark()唤醒队列中线程。

exclusiveOwnerThread属性保证锁的可重入,锁被lock了几次,必须unlock几次才能完全释放。

ReentrantLock

抽象类Sync继承了AQS,它的两个具体实现类FairSync和NonfairSync实现了公平锁和非公平锁。

ReentrantLock默认的无参构造方法通过NonfairSync创建一个非公平锁Sync, 可以通过带一个Boolean形参的构造方法指定锁类型。

加锁过程

ReentrantLock实例调用一次lock()方法,

1. 通过成员Sync锁调用AQS的acqurire(1)去尝试获取资源tryAccquire(1),若获取成功,则acqurire()方法中的if为false不再往下执行,方法正常结束,返回到主线程中表示已获取锁资源;

2. 若获取资源失败则尝试添加到队列中排队acquireQueued(addWaiter(Node.EXCLUSIVE),1),通过parkAndCheckInterrupt()方法调用LockSupport的park方法阻塞线程;

3. 若都失败,则通过selfInterrupt()调用线程的interrupt()方法中断该线程。

解锁过程

ReentrantLock实例调用一次unlock()方法,

1. 通过成员Sync锁调用AQS的release(1)去尝试释放资源tryRelease(1),若释放资源成功,则

2. 通过unparkSuccessor(head)方法调用LockSupport.unpark(head.thread)唤醒阻塞队列中的头节点的线程。

线程不安全是因为写操作造成,多个线程同时读并不会造成线程不安全,便有了ReadWriterLock

ReentrantReadWriteLock

读锁加锁调用的是acqurieShared()方法,解锁调用的是relaseShared()方法(共享方法)

写锁加锁调用的是acqurie()方法,解锁调用的是relase()方法(独占方法)

读锁可重入次数也是有限的,当同时占用读锁的线程数达到上线,新的线程进入阻塞队列等待

写锁的可重入最大数为MAX_COUNT,默认为2的16次方-1。

【Java并发编程】ReentrantReadWriteLock源码及实现原理分析 这篇文章写的很清楚,建议学习。

StampedLock

当读锁被占用时,试图获取写锁的线程阻塞等待,这是一种悲观锁的表现,为了提高性能,jdk1.8开始引入StampedLock,在读的过程中允许写锁获取,但是这样对于读操作可能出现读取数据不一致的问题,因此引入类似Mysql的ABA问题解决方案-版本号,返回一个邮戳(Stamp),通知读线程数据被修改了。

写锁

排它锁或者叫独占锁,同时只有一个线程可以获取该锁,并且不可重入,请求该锁成功后会返回一个stamp票据变量用来表示该锁的版本,当释放该锁时候需要unlockWrite并传递参数stamp;

读锁,支持悲观锁和乐观锁两种模式

悲观锁模式下写锁和读锁不可同时获取,自然能够保证数据的一致性;

乐观锁模式下,不涉及CAS操作,获取读锁只是使用【与或】操作进行检验

如果当前没有线程持有写锁,则简单的返回一个非0的stamp版本信息,

调用乐观读锁判断【tryOptimisticRead()返回stamp后 到 操作时刻】时间段内是否有其他线程持有了写锁,

如果是validate(stamp)返回0,则获取读锁失败,

否则就可以使用该stamp版本的锁对数据进行操作。

 


死锁的产生条件:

1. 互斥

线程T1占有A时,其他线程只能等待T1释放A

2. 不可剥夺

线程T1占有A时,线程T1不释放,其他线程不能得到A

3. 请求和等待

线程T1在等待被其他线程锁住的B时不释放自己锁住的A

4. 循环等待

线程循环等待资源

 

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 在Java编程语言中,synchronized和lock都是用于实现线程同步的机制,但它们有一些区别。 synchronized是Java语言内置的关键字,用于在方法或代码块级别上实现线程同步。当一个线程进入一个synchronized方法或代码块时,它将获得该方法或代码块的,其他线程将被阻塞,直到该线程释放。synchronized是隐式的,即由Java虚拟机自动管理,因此使用synchronized的代码通常更简单。 相比之下,LockJava编程语言中的一个接口,它提供了一组方法来实现线程同步。与synchronized不同,Lock是显式的,需要程序员手动获取和释放。这使得Lock比synchronized更灵活,因为它允许程序员更好地控制线程的行为,如尝试获取的超时时间,可以在多个条件上等待等等。 总的来说,synchronized适用于简单的线程同步问题,而Lock适用于更复杂的线程同步场景。同时,由于Lock提供了更多的控制和灵活性,因此在某些情况下它可能比synchronized更高效。 ### 回答2: synchronized和lock都可以用于多线程编程中的同步机制,但是它们有一些区别。 首先,synchronized是Java语言提供的关键字,而lock是一个接口,属于JavaLock接口族的一员。这意味着synchronized是Java语言的一部分,而lockJava提供的一个类库。 其次,synchronized是隐式的,通过使用synchronized关键字来表示代码块的同步,而lock是显式的,通过Lock接口的实现类来实现代码块的同步。 第三,synchronized只有一种使用方式,即使用synchronized关键字对整个代码块进行同步,而lock提供了更多的灵活性和功能,例如可以实现读写分离、公平等。此外,lock还提供了tryLock()方法,可以非阻塞地尝试获取,而synchronized则是阻塞的。 第四,synchronized在发生异常时会自动释放,而lock需要手动释放,如果没有正确释放,可能导致死或资源泄露等问题。 第五,synchronized可以作用于代码块、方法,或者作为类;而lock只能作用于代码块。 总结来说,synchronized是Java语言提供的关键字,适用于普通的同步需求,而lock是一个类库接口,提供了更灵活、功能更强大的同步机制。在开发中,可以根据具体情况选择使用synchronized或lock来实现多线程的同步。 ### 回答3: synchronized和lock都是用来控制多线程访问共享资源的机制,但它们在实现上有一些区别。 首先,synchronized是Java语言提供的原生关键字,而lock是通过Lock接口及其实现类ReentrantLock来实现的。synchronized是隐式,它的获取和释放是由JVM自动完成的,而lock是显式,需要手动获取和释放。 其次,在使用上,synchronized是非公平,它允许不按顺序地获取,而lock可以选择性地使用公平或非公平。公平是指多个线程按照申请的顺序获取,而非公平是允许线程跳过等待队列直接获取。 另外,lock比synchronized更加灵活。它提供了一些功能,例如可中断、可轮询以及超时等待等。使用lock可以更好地控制线程的等待时间,避免线程长时间阻塞。 此外,synchronized是基于对象的,每个对象都有一个与之关联的监视器。而lock可以同时对多个对象进行加操作,提供了更细粒度的控制。 然而,由于synchronized是Java语言提供的原生支持,使用起来更加简单,不需要显示地获取和释放,而lock需要手动控制。因此,当不需要使用lock提供的额外功能时,推荐使用synchronized来简化代码的复杂度。 总之,synchronized和lock都可以实现多线程间的同步,但在使用方式、灵活性和粒度控制上有一些区别,开发者可以根据实际需求选择适合的机制。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值