Java 多线程学习——常用类与方法

ThreadLocal

Java 中的 ThreadLocal 类允许我们创建只能被同一个线程读写的变量。因此,如果一段代码含有一个 ThreadLocal 变量的引用,即使两个线程同时执行这段代码,它们也无法访问到对方的 ThreadLocal 变量。

1
private ThreadLocal myThreadLocal = new ThreadLocal();

Java进阶(七)正确理解Thread Local的原理与适用场景

volatile

一旦一个共享变量(类的成员变量、类的静态成员变量)被 volatile 修饰之后,那么就具备了两层语义:

1)保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的。

2)禁止进行指令重排序。

Java 内存模型规定所有的变量都是存在主存当中(类似于前面说的物理内存),每个线程都有自己的工作内存(类似于前面的高速缓存)。线程对变量的所有操作都必须在工作内存中进行,而不能直接对主存进行操作。并且每个线程不能访问其他线程的工作内存。

Java并发编程:volatile关键字解析

ReentrantLock

ReentrantLock 重入锁,是实现 Lock 接口的一个类,表示能够对共享资源能够重复加锁,即当前线程获取该锁再次获取不会被阻塞。

ReentrantLock还支持公平锁和非公平锁两种方式。

Java并发学习之ReentrantLock的工作原理及使用姿势

Java并发之ReentrantLock详解

ReentrantLock 与 Synchronized 区别

  1. ReentrantLock 显示的获得、释放锁,synchronized 隐式获得释放锁

  2. ReentrantLock 可响应中断、可轮回,synchronized 是不可以响应中断的,为处理锁的不可用性提供了更高的灵活性

  3. ReentrantLock 是 API 级别的,synchronized 是 JVM 级别的

  4. ReentrantLock 可以实现公平锁

  5. ReentrantLock 通过 Condition 可以绑定多个条件

  6. 底层实现不一样, synchronized 是同步阻塞,使用的是悲观并发策略,lock 是同步非阻塞,采用的是乐观并发策略

  7. Lock 是一个接口,而 synchronized 是 Java 中的关键字,synchronized 是内置的语言实现。

  8. synchronized 在发生异常时,会自动释放线程占有的锁,因此不会导致死锁现象发生;而 Lock 在发生异常时,如果没有主动通过 unLock() 去释放锁,则很可能造成死锁现象,因此使用 Lock 时需要在 finally 块中释放锁。

  9. Lock 可以让等待锁的线程响应中断,而 synchronized 却不行,使用 synchronized 时,等待的线程会一直等待下去,不能够响应中断。

  10. 通过 Lock 可以知道有没有成功获取锁,而 synchronized 却无法办到。

  11. Lock 可以提高多个线程进行读操作的效率,既就是实现读写锁等。

BlockingQueue

阻塞队列(BlockingQueue)是一个支持两个附加操作的队列。这两个附加的操作是:在队列为空时,获取元素的线程会等待队列变为非空。当队列满时,存储元素的线程会等待队列可用。

方法\处理方式抛出异常返回特殊值一直阻塞超时退出
插入方法add(e)offer(e)put(e)offer(e,time,unit)
移除方法remove()poll()take()poll(time,unit)
检查方法element()peek()不可用不可用
  • 抛出异常:是指当阻塞队列满时候,再往队列里插入元素,会抛出 IllegalStateException(“Queue full”) 异常。当队列为空时,从队列里获取元素时会抛出 NoSuchElementException 异常 。

  • 返回特殊值:插入方法会返回是否成功,成功则返回 true。移除方法,则是从队列里拿出一个元素,如果没有则返回 null

  • 一直阻塞:当阻塞队列满时,如果生产者线程往队列里 put 元素,队列会一直阻塞生产者线程,直到拿到数据,或者响应中断退出。当队列空时,消费者线程试图从队列里 take 元素,队列也会阻塞消费者线程,直到队列可用。

  • 超时退出:当阻塞队列满时,队列会阻塞生产者线程一段时间,如果超过一定的时间,生产者线程就会退出。

聊聊并发(七)——Java 中的阻塞队列