Java多线程学习笔记

一、wait/notify

在这里插入图片描述
Owner线程发现条件不满足,调用wait方法,即可进入WaitSet变为WAITING状态
BLOCKED和WAITING的线程都处于阻塞状态,不占用CPU时间
BLOCKED线程会在Owner线程释放锁时唤醒
WAITING线程会在Owner线程调用notify或notifyAll时唤醒,但唤醒后并不意味着重新获得锁,仍须进入EntryList重新竞争

Sleep和Wait的区别

1、sleep是Thread方法,而wait是Object方法
2、sleep不需要强制和synchronized方法使用,而wait需要和sychronized一起使用
3、sleep再睡眠时不会释放对象锁,但wait在等待的时候会释放对象锁
4、他们的状态都是TIMED_WAITING

二、多锁

1、锁的活跃性
死锁、活锁、饥饿
2、Reentrantlock
相对于synchronized特点:

  • 可中断
  • 可以设置超时时间
  • 可以设置为公平锁
  • 支持多个条件变量

3、ReentrantLock特性

  • 可重入
  • 可打断
  • 超时锁
  • 公平锁

条件变量

二、共享模型之内存

上节的Monitor是访问共享变量时,保证临界区代码的原子性
本章主要学习共享变量在多线程间的可见性,问题与多条指令执行时的有序性问题
2.1 Java内存模型
JMM定义了主存、工作内存抽象概念,底层对应着CPU寄存器、缓存、硬件内存、CPU指令优化等
JMM主要体现在:

  • 原子性-保证指令不会受到线程上下文切换的影响
  • 可见性-保证指令不会受CPU缓存的影响
  • 有序性-保证指令不会受CPU指令并行优化的影响

Volatile
轻量级,保证线程指令的可见性,但不能保证原子性
Synchornized
重量级,既能保证线程指令的原子性,又能保证可见性
指令重排序
JVM在不影响正确性的前提下,可以调整语句的执行顺序,这种特性称之为【指令重排】,多线程下指令重排会影响正确性。
现代CPU支持多级指令流水线,CPU可以在一个时钟周期内,同时运行多条指令的不同阶段,流水线技术不能缩短单条指令的执行时间,但变相提高了指令的吞吐率。
Volatile的底层实现原理是内存屏障

  • 对volatile变量的写指令后会加入写屏障
  • 对volatile变量的读指令前会加入读屏障

三、共享模型之无锁

主要内容:

  • CAS与volatile
  • 原子整数
  • 原子引用
  • 原子累加器
  • Unsafe

3.1原子累加器
源码之LongAdder
关键域:

// 累加单元数组,懒惰初始化
transient volatile Cell[] cells;
// 基础值,如果没有竞争,则cas累加这个域
transient volatile long base;
// 在cells创建或扩容时,置为1,表示加锁
transient volatile int cellsBusy;

3.2 Unsafe
是在sun.misc包下的类,不属于java标准,提供了一些相对底层方法来操作系统底层资源,Unsafe类在提升Java运行效率,增强Java语言底层操作能力方面起了很大的作用。

四、共享模型之不可变

  • 不可变类的使用
  • 不可变类设计
  • 无状态类设计

五、AQS

全称是AbstractQueuedSynchronizer,是阻塞式锁和相关的同步器工具的框架
特点:

  • 用state属性来表示资源的状态(分独占模式和共享模式),子类需要定义如何维护这个状态,控制如何获取锁和释放锁
  • getState-获取state状态
  • setState-设置state状态
  • compareAndSetState - cas机制设置state状态
  • 独占模式是只有一个线程能访问资源,共享模式允许多个线程访问资源
  • 提供了基于FIFO的等待队列,类似于Monitor的EntryList
  • 条件变量来实现等待、唤醒机制,支持多个条件变量,类似于Monitor的WaitSet

获取锁的姿势

// 如果获取锁失败
if (!tryAcquire(arg)) {
	// 入队,可以选择阻塞当前线程 park unpark
}

释放锁的姿势

// 如果释放锁成功
if (tryRelease(arg)) {
	// 让阻塞线程恢复运行
}

5.1ReentrantLock读写锁
注意事项

  • 读锁不支持条件变量
  • 重入时升级不支持:即持有读锁的情况下去获取写锁,会导致写锁永久等待
r.lock();
try {
	w.lock();
	try {
	} finally {
	  w.unlock();
	}
} finally {
	r.unlock();
}
  • 重入时降级支持:即持有写锁的情况下去获取读锁
class CachedData {
	Object data;
	// 是否有效,如果失效,需重新计算
	volatile boolean cacheValid;
	final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
	void processCachedData(){
		rwl.readLock.lock();
		if (!cahceValid) {
			// 获取写锁前必须释放读锁
			rwl.readLock.unlock();
			rwl.writeLock.lock();
			try {
				//判断是否有其他线程已经获取了写锁、更新了缓存,避免重复更新
				if (!cachedValid) {
					data = ...
					cachedValid = true;
				}
				//降级为读锁,释放写锁,这样能够让其他线程获取写锁
				rwl.readLock.lock();
			} finally {
				rwl.writeLock.unlock();
			}
		}
		try {
			use(data)
		} finally {
			rwl.readLock.unlock();
		}		
	}
}

六、ReentrantLock

相对于sychronized具备如下特点:

  • 可中断
  • 可以设置超时时间
  • 可以设置为公平锁
  • 支持多个条件变量
    与sychronized一样,都支持可重入
    可重入
    指同一个线程如果首次获得了这把锁,那么因为他是这把锁的持有者,因此有权利再次获得这把锁,如果是不可重入锁,那么第二次获得锁时,自己也会被锁挡住
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值