java锁分类

绝对线程安全:无论进行什么操作都是线程安全的。

相对线程安全:比如一些标注为线程安全的容器,它的一些方法被synchronized修饰,但即使如此,在多线程的情况下,在方法调用端依旧需要额外的同步措施来保证多个方法的组合操作是线程安全的。

线程安全的实现方法

互斥同步:悲观的并发策略。同步是指多个线程并发访问共享数据时,保证共享数据在同一时刻只被一个线程使用(使用信号量时是一些)。而互斥是一种手段,临界区,互斥量,信号量都是主要的互斥实现方式。最基本的方式是synchronized(可重入)关键字,还有JUC中的重入锁reentrantlock。reentrantlock有3个更高级的功能:等待可中断,可实现公平锁,锁可以绑定多个条件。

非阻塞同步:互斥同步最大的问题就是线程阻塞和唤醒带来的性能问题。非阻塞同步是乐观的并发策略,由于硬件的发展,支持硬件层面的CAS的原子性,所以可以通过乐观的策略来进行非阻塞同步。

无同步方案:可重入代码,也叫纯代码,代码的执行有着绝对的多线程的安全性。

                      线程本地存储:可以将共享数据的代码保证在同一个线程中执行。那么这个共享数据就是线程安全的。例子有WEB中的一个请求对应一个服务器线程。

 

 

 

公平锁/非公平锁

公平锁指多个线程按申请顺序来获取锁。

可重入锁

在同一个线程在外层方法获取锁的时候,在进入内层方法会自动获取锁,即线程可以进入任何一个它已经拥有的锁所同步的代码块。

独享锁/共享锁

独享锁指该锁一次只能被一个线程所持有,共享锁是允许被多个线程所持有。

乐观锁/悲观锁

悲观锁认为对于同一个数据的并发操作,一定是会发生修改的,悲观的认为不加锁的并发操作一定出问题,所以一定会加锁

乐观锁则认为是不会发生修改的,在更新数据时,会采用尝试更新,不断重新更新的方式。

悲观锁适合写操作多的场景,乐观锁适合读操作多的场景,不加锁会带来很大的性能提升,乐观锁的典型例子是原子类,通过CAS算法实现。

偏向锁/轻量级锁/重量级锁

针对synchronized,指锁的状态。通过对象监视器在对象头中的字段来表明。

偏向锁指一段同步代码一直被一个线程所访问,那么该线程会自动获取锁,降低锁获取代价。

轻量级锁指当锁是偏向锁时,被另一个线程所访问,偏向锁就会升级为轻量级的锁,其他线程会通过自旋的形式尝试获取锁,不会阻塞,提高性能。

重量级锁指当锁为轻量级的锁的时候,另一个线程虽然是自旋,但自旋不会一直持续下去,当自旋一定次数后,还没获取到锁,就会进入阻塞,该锁膨胀为重量级的锁,重量级锁会让其他申请的线程进入阻塞,性能降低。

自旋锁

自旋锁是指尝试获取锁的线程不会立即阻塞,而是采用循环的方式去尝试获取锁,这样的好处是减少线程上下文切换的消耗,缺点是循环会消耗CPU。

synchronized

reentrantLock

reentrantReadWriteLock

stampedLock

     StampedLock是Java8引入的一种新的所机制,简单的理解,可以认为它是读写锁的一个改进版本,读写锁虽然分离了读和写的功能,使得读与读之间可以完全并发,但是读和写之间依然是冲突的,读锁会完全阻塞写锁,它使用的依然是悲观的锁策略.如果有大量的读线程,他也有可能引起写线程的饥饿

     而StampedLock则提供了一种乐观的读策略,这种乐观策略的锁非常类似于无锁的操作,使得乐观锁完全不会阻塞写线程

AQS

它维护了一个volatile int state(代表共享资源)和一个FIFO线程等待队列(多线程争用资源被阻塞时会进入此队列)。这里volatile是核心关键词,具体volatile的语义,在此不述。state的访问方式有三种:

  • getState()
  • setState()
  • compareAndSetState()

  AQS定义两种资源共享方式:Exclusive(独占,只有一个线程能执行,如ReentrantLock)和Share(共享,多个线程可同时执行,如Semaphore/CountDownLatch)。

  不同的自定义同步器争用共享资源的方式也不同。自定义同步器在实现时只需要实现共享资源state的获取与释放方式即可,至于具体线程等待队列的维护(如获取资源失败入队/唤醒出队等),AQS已经在顶层实现好了。自定义同步器实现时主要实现以下几种方法:

  • isHeldExclusively():该线程是否正在独占资源。只有用到condition才需要去实现它。
  • tryAcquire(int):独占方式。尝试获取资源,成功则返回true,失败则返回false。
  • tryRelease(int):独占方式。尝试释放资源,成功则返回true,失败则返回false。
  • tryAcquireShared(int):共享方式。尝试获取资源。负数表示失败;0表示成功,但没有剩余可用资源;正数表示成功,且有剩余资源。
  • tryReleaseShared(int):共享方式。尝试释放资源,成功则返回true,失败则返回false。

  以ReentrantLock为例,state初始化为0,表示未锁定状态。A线程lock()时,会调用tryAcquire()独占该锁并将state+1。此后,其他线程再tryAcquire()时就会失败,直到A线程unlock()到state=0(即释放锁)为止,其它线程才有机会获取该锁。当然,释放锁之前,A线程自己是可以重复获取此锁的(state会累加),这就是可重入的概念。但要注意,获取多少次就要释放多么次,这样才能保证state是能回到零态的。

  再以CountDownLatch以例,任务分为N个子线程去执行,state也初始化为N(注意N要与线程个数一致)。这N个子线程是并行执行的,每个子线程执行完后countDown()一次,state会CAS减1。等到所有子线程都执行完后(即state=0),会unpark()主调用线程,然后主调用线程就会从await()函数返回,继续后余动作。

  一般来说,自定义同步器要么是独占方法,要么是共享方式,他们也只需实现tryAcquire-tryRelease、tryAcquireShared-tryReleaseShared中的一种即可。但AQS也支持自定义同步器同时实现独占和共享两种方式,如ReentrantReadWriteLock。

 

aqs有两个队列,一个同步队列,一个条件队列,抢锁的时候先try一次,不成功,就包装当前线程为node加入同步队列,然后休眠等待唤醒,同步队列的head释放锁后,唤醒next,next继续去抢锁,代码很多地方用到cas,cas只是一个乐观锁的编程思想,aqs是为了解决并发而定制的一个同步组件,

 

合理利用线程池能够带来三个好处。第一:降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。第二:提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。第三:提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。

SingleThreadExecutor:单个后台线程 (其缓冲队列是LinkedBlockingQueue,无界的)。此线程池保证所有任务的执行顺序按照任务的提交顺序执行

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值