ReentrantLock
是可重入的独占锁, 同时只能有一个线程可以获取该锁,其他获取该锁的线程会被阻塞而被放入该锁的AQS
阻塞队列里面。
ReentrantLock
根据参数来决定其内部是一个公平还是非公平锁,默认是非公平锁。其中Sync 类直接继承自AQS , 它的子类NonfairSync
和FairSync
分别实现了获取锁的非公平与公平策略。
1 | public ReentrantLock() { |
ReentrantLock
是可重入的独占锁, 同时只能有一个线程可以获取该锁,其他获取该锁的线程会被阻塞而被放入该锁的AQS
阻塞队列里面。
ReentrantLock
根据参数来决定其内部是一个公平还是非公平锁,默认是非公平锁。其中Sync 类直接继承自AQS , 它的子类NonfairSync
和FairSync
分别实现了获取锁的非公平与公平策略。
1 | public ReentrantLock() { |
AbstractQueuedSynchronizer
抽象同步队列简称AQS ,它是实现同步器的基础组件,并发包中锁的底层就是使用AQS 实现的。另外,大多数开发者可能永远不会直接使用AQS ,但是知道其原理对于架构设计还是很有帮助的。
CopyOnWriteArrayList
是一个线程安全的ArrayList
,对其进行的修改操作都是在底层的一个复制的数组(快照)上进行的,也就是使用了写时复制策略。
在CopyOnWriteArrayList
的类图中,每个CopyOnWriteArrayList
对象里面有一个使用volatile
修饰的array
数组对象用来存放具体元素,独占锁lock
用来保证同时只有1个线程对array
进行修改。
JUC 包提供了一系列的原子性操作类,这些类都是使用非阻塞算法CAS 实现的,相比使用锁实现原子性操作这在性能上有很大提高。由于原子性操作类的原理都大致相同,所以本文只讲解最简单的AtomicLong
类及JDK 8 中新增的LongAdder
的实现原理。
synchronized块
是Java 提供的一种原子性内置锁, Java
中的每个对象都可以把它当作一个同步锁来使用, 这些Java 内置的使用者看不到的锁被称为内部锁,也叫作监视器锁。synchronized
关键字底层原理属于 JVM 层面。线程的执行代码在进入synchronized
代码块前会自动获取内部锁,这时候其他线程访问该同步代码块时会被阻塞挂起。拿到内部锁的线程会在正常退出同步代码块或者抛出异常后或者在同步块内调用了该内置锁资源的wait
系列方法时释放该内置锁。内置锁是排它锁,也就是当一个线程获取这个锁后, 其他线程必须等待该线程释放锁后才能获取该锁。
当访问共享变量时,往往需要加锁来保证数据同步。一种避免使用同步的方式就是不共享数据。如果仅在单线程中访问数据,就不需要同步了,这种技术称为线程封闭。
线程封闭技术有一个常见的应用,JDBC的Connection对象。Connection对象在实现的时候并没有对线程安全做太多的处理,JDBC的规范里也没有要求Connection对象必须是线程安全的。实际在服务器应用程序中,线程从连接池获取了一个Connection对象,使用完再把Connection对象返回给连接池,由于大多数请求都是由单线程采用同步的方式来处理的,并且在Connection对象返回之前,连接池不会将它分配给其他线程。因此这种连接管理模式处理请求时隐含的将Connection对象封闭在线程里面,这样我们使用的Connection对象虽然本身不是线程安全的,但是它通过线程封闭也做到了线程安全。
实现线程封闭,一般有三种方法:Ad-hoc 线程封闭、堆栈封闭、ThreadLocal线程封闭。
1 | serverBootstrap |
每次有新连接到来的时候,都会调用 ChannelInitializer
的 initChannel()
方法,然后这里相关的 handler
都会被 new
一次。许多 handler
,他们内部都是没有成员变量的,也就是说是无状态的,我们完全可以使用单例模式,即调用 pipeline().addLast()
方法的时候,都直接使用单例,不需要每次都 new
,提高效率,也避免了创建很多小的对象。
单例改造:对于无状态Handler
,使用单例模式,多个channel共享一个实例