Java中synchronized 和 ReentrantLock 有什么不同?锁的优化机制了解吗?线程池核心线程数怎么设置呢?

Java synchronized ReentrantLock 有什么不同?
相似点:
这两种同步方式有很多相似之处,它们都是加锁方式同步,而且都是阻塞式的同步,也就是说当如
果一个线程获得了对象锁,进入了同步块,其他访问该同步块的线程都必须阻塞在同步块外面等
待,而进行线程阻塞和唤醒的代价是比较高的 .
区别:
这两种方式最大区别就是对于 Synchronized 来说,它是 java 语言的关键字,是原生语法层面的互
斥,需要 jvm 实现。而 ReentrantLock 它是 JDK 1.5 之后提供的 API 层面的互斥锁,需要 lock()
unlock() 方法配合 try/finally 语句块来完成。
Synchronized 进过编译,会在同步块的前后分别形成 monitorenter monitorexit 这个两个字节码
指令。在执行 monitorenter 指令时,首先要尝试获取对象锁。如果这个对象没被锁定,或者当前线
程已经拥有了那个对象锁,把锁的计算器加 1 ,相应的,在执行 monitorexit 指令时会将锁计算器就
1 ,当计算器为 0 时,锁就被释放了。如果获取对象锁失败,那当前线程就要阻塞,直到对象锁被
另一个线程释放为止。
由于 ReentrantLock java.util.concurrent 包下提供的一套互斥锁,相比 Synchronized
ReentrantLock 类提供了一些高级功能,主要有以下 3 项:
1. 等待可中断,持有锁的线程长期不释放的时候,正在等待的线程可以选择放弃等待,这相当于
Synchronized 来说可以避免出现死锁的情况。
2. 公平锁,多个线程等待同一个锁时,必须按照申请锁的时间顺序获得锁, Synchronized 锁非公平
锁, ReentrantLock 默认的构造函数是创建的非公平锁,可以通过参数 true 设为公平锁,但公平锁
表现的性能不是很好。
3. 锁绑定多个条件,一个 ReentrantLock 对象可以同时绑定对个对象。
锁的优化机制了解吗?
JDK1.6 版本之后, synchronized 本身也在不断优化锁的机制,有些情况下他并不会是一个很重量
级的锁了。优化机制包括自适应锁、自旋锁、锁消除、锁粗化、轻量级锁和偏向锁。
锁的状态从低到高依次为 无锁 -> 偏向锁 -> 轻量级锁 -> 重量级锁 ,升级的过程就是从低到高,降级在
一定条件也是有可能发生的。
自旋锁 :由于大部分时候,锁被占用的时间很短,共享变量的锁定时间也很短,所有没有必要挂起
线程,用户态和内核态的来回上下文切换严重影响性能。自旋的概念就是让线程执行一个忙循环,
可以理解为就是啥也不干,防止从用户态转入内核态,自旋锁可以通过设置 -XX:+UseSpining 来开
启,自旋的默认次数是 10 次,可以使用 -XX:PreBlockSpin 设置。
自适应锁 :自适应锁就是自适应的自旋锁,自旋的时间不是固定时间,而是由前一次在同一个锁上
的自旋时间和锁的持有者状态来决定。
锁消除 :锁消除指的是 JVM 检测到一些同步的代码块,完全不存在数据竞争的场景,也就是不需要
加锁,就会进行锁消除。
锁粗化 :锁粗化指的是有很多操作都是对同一个对象进行加锁,就会把锁的同步范围扩展到整个操
作序列之外。 偏向锁 :当线程访问同步块获取锁时,会在对象头和栈帧中的锁记录里存储偏向锁的线程 ID ,之后
这个线程再次进入同步块时都不需要 CAS 来加锁和解锁了,偏向锁会永远偏向第一个获得锁的线
程,如果后续没有其他线程获得过这个锁,持有锁的线程就永远不需要进行同步,反之,当有其他
线程竞争偏向锁时,持有偏向锁的线程就会释放偏向锁。可以用过设置 -XX:+UseBiasedLocking
启偏向锁。
轻量级锁 JVM 的对象的对象头中包含有一些锁的标志位,代码进入同步块的时候, JVM 将会使用
CAS 方式来尝试获取锁,如果更新成功则会把对象头中的状态位标记为轻量级锁,如果更新失败,
当前线程就尝试自旋来获得锁。
整个锁升级的过程非常复杂,我尽力去除一些无用的环节,简单来描述整个升级的机制。
简单点说,偏向锁就是通过对象头的偏向线程 ID 来对比,甚至都不需要 CAS 了,而轻量级锁主要就
是通过 CAS 修改对象头锁记录和自旋来实现,重量级锁则是除了拥有锁的线程其他全部阻塞。
线程池核心线程数怎么设置呢?
分为 CPU 密集型和 IO 密集型
CPU
这种任务消耗的主要是 CPU 资源,可以将线程数设置为 N CPU 核心数) +1 ,比 CPU 核心数多出
来的一个线程是为了防止线程偶发的缺页中断,或者其它原因导致的任务暂停而带来的影响。一旦
任务暂停, CPU 就会处于空闲状态,而在这种情况下多出来的一个线程就可以充分利用 CPU 的空
闲时间。
IO 密集型
这种任务应用起来,系统会用大部分的时间来处理 I/O 交互,而线程在处理 I/O 的时间段内不会占
CPU 来处理,这时就可以将 CPU 交出给其它线程使用。因此在 I/O 密集型任务的应用中,我们
可以多配置一些线程,具体的计算方法是 : 核心线程数 =CPU 核心数量 *2

你可能感兴趣的:(java,java,开发语言)