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的方法摘要如下:

java 信号wait java 信号量 线程池_线程池

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)消费产品,从仓库里获取。仓库爆满时生产者等待消费者消费,仓库为空时消费者等待生产者生产。
实际上,生产者消费者模式准确说应该是“生产者-消费者-仓储”模式,离开了仓储,生产者消费者模型就显得没有说服力了,对于此模型,应该明确一下几点:

  • 生产者仅仅在仓储未满时候生产,仓满则停止生产
  • 消费者仅仅在仓储有产品时候才能消费,仓空则等待
  • 当消费者发现仓储没产品可消费时候会通知生产者生产
  • 生产者在生产出可消费产品时候,应该通知等待的消费者去消费