1. 信号量
信号量(Semaphore)是一个线程同步结构,是用于在线程间传递信号,以避免出现信号丢失,或者像锁一样用于保护一个关键区域,信号量负责协调各个线程,以保证线程能够正确、合理的使用公用资源。信号量通过使用计数器来控制对共享资源的访问。如果计数器大于零,则允许访问。如果为零,则拒绝访问。计数器的计数是允许访问共享资源的许可。因此,要访问资源,必须从信号量向线程授予许可。
2.线程池
线程池可以从字面意思进行理解,即可以理解成为多个线程池封装在一起进行操作,因此形成了线程池。线程池主要是为了解决线程生命周期开销问题和资源不足的问题。一个简单的线程池至少需要包含:线程池管理器【创建、销毁并管理线程池,讲工作线程放在线程池中】;工作线程【一个可以循环执行任务的线程,在没有任务时进行等待】;任务队列【提供一种韩冲机制,将没有处理的任务放在任务队列中】;任务接口【每个任务必须实现的接口,主要用来规定任务的入口、任务执行完成后的收尾工作、任务执行状态等、工作线程听过该接接口调度任务的执行】。
构造线程池的几种方式如下所示:
-
newFixedThreadPool();
-
newSingleThreadExecutor;
-
newCachedThreadPool();
-
newSingleThreadScheduledExecutor();
-
newScheduledThreadPool();
下面以newFixedThreadPool()为例创建一个线程池。package App.executors; import java.util.concurrent.Executors; import java.util.concurrent.ExecutorService; public class MyTest{ public static void main(String[] args){ // 创建一个可重用固定线程数的线程池 ExecutorService ee=Executors.newFixedThreadPool(5); // 创建线程 Thread t1 = new MyThread(); Thread t2 = new MyThread(); Thread t3 = new MyThread(); Thread t4 = new MyThread(); Thread t5 = new MyThread(); // 将线程放入池中进行执行 ee.execute(t1); ee.execute(t2); ee.execute(t3); ee.execute(t4); ee.execute(t5); // 关闭线程池 ee.shutdown(); } } class MyThread extends Thread(){ public void run(){ system.out.println(Thread.currentThread().getName() + "正在执行"); } }
3. ThreadLocal
ThreadLocal从字面理解就是一个线程局部变量,线程局部变量(ThreadLocal)就是每个线程都会有一个局部变量,独立于变量的初始化副本,而各个副本是通过线程唯一标识相关联的。
ThreadLocal的方法摘要如下:
4.原子操作类
从JDK5之后,java提供了粒度更细、量级更轻,并且在多核处理器具有高性能的原子操作类。因为原子操作类把竞争的范围缩小到单个变量上,这可以算是粒度最细的情况了。
原子操作类相当于泛化的volatile变量,能够支持原子读取-修改-写入操作。原子操作类在java.util.concurrent.atomic包下,可以分为4种类型的原子更新类:
- 原子更新基本类型
- 原子更新数组类型
- 原子更新引用
- 原子更新属性
使用原子方式更新基本类型: - AtomicBoolean:原子更新布尔变量
- AtomicInteger:原子更新整型变量
- AtomicLong:原子更新长整型变量
通过原子更新数组里的某个元素: - AtomicIntegerArray:原子更新整型数组的某个元素
- AtomicLongArray:原子更新长整型数组的某个元素
- AtomicReferenceArray:原子更新引用类型数组的某个元素
AtomicIntegerArray常用的方法有: - int addAndSet(int i, int delta):以原子方式将输入值与数组中索引为i的元素相加
- boolean compareAndSet(int i, int expect, int update):如果当前值等于预期值,则以原子方式更新数组中索引为i的值为update值
需要更新引用类型往往涉及多个变量: - AtomicReference:原子更新引用类型
- AtomicReferenceFieldUpdater:原子更新引用类型里的字段
- AtomicMarkableReference:原子更新带有标记位的引用类型
如果需要原子更新某个类的某个字段,就需要用到原子更新字段类,可以使用以下几个类: - AtomicIntegerFieldUpdater:原子更新整型字段
- AtomicLongFieldUpdater:原子更新长整型字段
- AtomicStampedReference:原子更新带有版本号的引用类型。
要想原子更新字段,需要两个步骤: - 每次必须使用newUpdater创建一个更新器,并且需要设置想要更新的类的字段
- 更新类的字段(属性)必须为public volatile
5. 生产者-消费者模式
生产者和消费者问题是一个经典的线程同步问题。生产者(Producer)生产产品(Product),放入仓库(Repertory);消费者(Consumer)消费产品,从仓库里获取。仓库爆满时生产者等待消费者消费,仓库为空时消费者等待生产者生产。
实际上,生产者消费者模式准确说应该是“生产者-消费者-仓储”模式,离开了仓储,生产者消费者模型就显得没有说服力了,对于此模型,应该明确一下几点:
- 生产者仅仅在仓储未满时候生产,仓满则停止生产
- 消费者仅仅在仓储有产品时候才能消费,仓空则等待
- 当消费者发现仓储没产品可消费时候会通知生产者生产
- 生产者在生产出可消费产品时候,应该通知等待的消费者去消费