版权声明:本文出自汪磊的博客,转载请务必注明出处。

一、ThreadFactory概述以及源码分析

ThreadFactory很简单,就是一个线程工厂也就是负责生产线程的,我们看下ThreadFactory源码;

 public interface ThreadFactory {

     /**
* Constructs a new {@code Thread}. Implementations may also initialize
* priority, name, daemon status, {@code ThreadGroup}, etc.
*
* @param r a runnable to be executed by new thread instance
* @return constructed thread, or {@code null} if the request to
* create a thread is rejected
*/
Thread newThread(Runnable r);
}

很简单吧,就是一个接口,newThread方法就是用来生产线程的,子类需要实现这个方法来根据自己规则生产相应的线程。

那安卓中什么地方用到了ThreadFactory呢?稍有经验的就会知道线程池中用到了,我们看下平常使用线程池是怎么创建的,以下是我一个项目中用到的:

     // 创建线程池对象
public static final Executor poolExecutor = new ThreadPoolExecutor(
CORE_POOL_SIZE, MAX_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS,
new LinkedBlockingQueue<Runnable>());

咦?没有用到线程池啊,别急,我们看下ThreadPoolExecutor创建的源码:

 public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}

看到了吧,最终调用的如下构造函数来创建线程池:

     public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
ThreadFactory传入的参数是Executors.defaultThreadFactory(),那我们继续看下Executors中defaultThreadFactory吧:
     /**
* The default thread factory
*/
static class DefaultThreadFactory implements ThreadFactory {
private static final AtomicInteger poolNumber = new AtomicInteger(1);
private final ThreadGroup group;
private final AtomicInteger threadNumber = new AtomicInteger(1);
private final String namePrefix; DefaultThreadFactory() {
SecurityManager s = System.getSecurityManager();
group = (s != null) ? s.getThreadGroup() :
Thread.currentThread().getThreadGroup();
namePrefix = "pool-" +
poolNumber.getAndIncrement() +
"-thread-";
} public Thread newThread(Runnable r) {
Thread t = new Thread(group, r,
namePrefix + threadNumber.getAndIncrement(),
0);
if (t.isDaemon())
t.setDaemon(false);
if (t.getPriority() != Thread.NORM_PRIORITY)
t.setPriority(Thread.NORM_PRIORITY);
return t;
}
}
DefaultThreadFactory实现了ThreadFactory接口,newThread中生产了一个个线程并且设置为不是守护线程,线程优先级均为Thread.NORM_PRIORITY。
在我们使用线程池的时候如果不自己创建线程工厂类,那么系统会给我们创建一个默认的线程工厂来生产线程(Executors中defaultThreadFactory())
好了,关于线程工厂就见到这里了,知道有这么个玩意用来生产线程的就可以了。
二、BlockingQueue概述以及源码分析
BlockingQueue顾名思义:阻塞队列,简单说就是放入,取出数据都会发生阻塞。比如取出数据时发现容器没有数据,就会等待产生阻塞一直等到有数据
为止,同样放入数据时如果容器已经满了,那么就回等待一直到容器有空间可以放入数据。
BlockingQueue就是一个接口,定义了插入,取出数据的接口,如下:
 public interface BlockingQueue<E> extends Queue<E> {

     //添加元素到队列里,添加成功返回true,由于容量满了添加失败会抛出IllegalStateException异常
boolean add(E e); //添加元素到队列里,成功返回true,失败返回false
boolean offer(E e); //添加元素到队列里,如果容量满了会阻塞直到容量不满
void put(E e) throws InterruptedException; //添加元素到队列里,如果容器已满会阻塞列队,但是不会一直阻塞,只会阻塞timeout时间,在这期间内添加成功则返回true,否则返回false
boolean offer(E e, long timeout, TimeUnit unit)
throws InterruptedException; //从队列中获取元素,如果队列为空,则一直阻塞
E take() throws InterruptedException; //从队列中获取元素,如果容器为空会阻塞列队,但是不会一直阻塞,只会阻塞timeout时间,在这期间内获取成功则返回对应元素,否则返回null
E poll(long timeout, TimeUnit unit)
throws InterruptedException; //存储数据的队列剩余空间大小
int remainingCapacity(); //删除指定的元素,成功返回true,失败返回false
boolean remove(Object o); public boolean contains(Object o); int drainTo(Collection<? super E> c); int drainTo(Collection<? super E> c, int maxElements);
}
主要方法已经给出注释。
BlockingQueue的具体子类如下图所示:

其中最最常用的就是ArrayBlockingQueue以及LinkedBlockingQueue,我们以ArrayBlockingQueue为例详细分析一下实现过程。
三、ArrayBlockingQueue源码分析
ArrayBlockingQueue内部是以数组为容器盛放元素,并且放入和取出元素的时候使用同一个锁,也就是放入的时候不能同时取出元素。
ArrayBlockingQueue中主要属性:
     //存储元素的数组,是个循环数组,至于为什么是循环数组下面会讲到
final Object[] items; //下一次拿数据的时候的索引
int takeIndex; //下一次放数据的时候的索引
int putIndex; //队列中已经存储元素的个数
int count; //锁,只有这一把锁
final ReentrantLock lock; //等待拿数据的的条件对象
private final Condition notEmpty; //等待放数据的的条件对象
private final Condition notFull;

已经给出详细注释就不一一详细解释了。

接下来我们看下构造函数;

     public ArrayBlockingQueue(int capacity) {
this(capacity, false);
} public ArrayBlockingQueue(int capacity, boolean fair) {
if (capacity <= 0)
throw new IllegalArgumentException();
this.items = new Object[capacity];
lock = new ReentrantLock(fair);
notEmpty = lock.newCondition();
notFull = lock.newCondition();
}

初始化的时候我们需要指定盛放元素容器的大小,并且初始化一些属性。

接下来我们分析下ArrayBlockingQueue中添加的方法:add,offer以及put方法。

先看add方法:

 public boolean add(E e) {
return super.add(e);
}

直接调用的父类的方法,我们只好去看看父类中的add方法了:

 public boolean add(E e) {
if (offer(e))
return true;
else
throw new IllegalStateException("Queue full");
}

是不是逻辑很简单,add方法内部调用了offer方法如果offer方法返回true则add直接返回true,否则抛出IllegalStateException异常。

接下来我们看下offer方法:

     public boolean offer(E e) {
if (e == null) throw new NullPointerException();
final ReentrantLock lock = this.lock;
lock.lock();
try {
if (count == items.length)
return false;
else {
enqueue(e);
return true;
}
} finally {
lock.unlock();
}
}

第2行,检查是否为null,为null则抛出空指针异常。

3,4行加锁,保证只有一个线程操作。

6,7行检查当前容器是否已将满了,如果满了则不能再放入元素,直接返回false。

9,10行如果容器没满则执行enqueue方法放入元素,然后返回true,enqueue方法后面会分析。

13行解除锁。以上就是offer方法的逻辑,比较简单,该说的都说了。

接下来分析put方法:

 public void put(E e) throws InterruptedException {
if (e == null) throw new NullPointerException();
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
while (count == items.length)
notFull.await();
enqueue(e);
} finally {
lock.unlock();
}
}

第4行这里调用的是lockInterruptibly方法,与lock方法相比,lockInterruptibly可以被中断,中断的时候产生InterruptedException异常。

6,7行同样判断容器是否已经满了,如果已经满了则执行wait逻辑等待,这里就是阻塞队列的核心,是不是觉得原来如此啊。

8行,如果容器有空间执行enqueue方法,向容器中加入元素。

offer与put都用到了enqueue方法向容器中加入元素,接下来我们看下enqueue方法:

 private void enqueue(E x) {
// assert lock.getHoldCount() == 1;
// assert items[putIndex] == null;
final Object[] items = this.items;
items[putIndex] = x;
if (++putIndex == items.length) putIndex = 0;
count++;
notEmpty.signal();
}

4,5行就是向数组items中放入元素x。

6行,放元素的索引putIndex加1后与数组长度比较,如果达到数组长度则将putIndex置为0,相当于下一次放入元素位置为数组的第一次位置,从头开始放入元素,上面说过items是个循环数组,就是在这里体现出来的,如果我们初始化的时候设定容器items大小为10,然而我们不停放入数据也是没问题的,只不过后面加入的数据会覆盖前面的数据。

8行,唤醒取数据的线程,告诉其哥们我放入了一个数据你可以取数据了。

到这放入数据的核心部分就分析完了,是不是很简单???放入数据还有个offer(E e, long timeout, TimeUnit unit)方法,同样很简单,可以自行分析。

接下来分析取出数据的方法,取出数据主要是poll与take方法。

先来看下poll方法;

 public E poll() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
return (count == 0) ? null : dequeue();
} finally {
lock.unlock();
}
}

2,3行同样是先锁住,保证单线程操作。

5行,判断当前线程中元素数量是否为0,如果为0则没有元素返回null,否则执行dequeue方法取出数据并返回,后续会分析dequeue方法。

7行解除锁。

poll方法是不是很简单,同样poll(long timeout, TimeUnit unit)也不难分析可自行分析。

接下来看下take方法:

 public E take() throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
while (count == 0)
notEmpty.await();
return dequeue();
} finally {
lock.unlock();
}
}

take方法也不难理解,核心就是5,6行逻辑,如果count为0也就是容器内没有数据则执行wait方法,线程一直处于等待状态,如果不为0则执行dequeue

方法取出数据。

我们再看下dequeue方法:

 private E dequeue() {
// assert lock.getHoldCount() == 1;
// assert items[takeIndex] != null;
final Object[] items = this.items;
@SuppressWarnings("unchecked")
E x = (E) items[takeIndex];
items[takeIndex] = null;
if (++takeIndex == items.length) takeIndex = 0;
count--;
if (itrs != null)
itrs.elementDequeued();
notFull.signal();
return x;
}

6,7,13行从数组items中取出数据,并将原数组位置处置为null,最后13行处返回取出的数据。

8行,取数据索引takeIndex加1后与数组总长度比较如果达到数组长度则将takeIndex置为0,下一次从数组开始处取数据。

10,11行通知迭代器有数据取出。

12行通知放入数据的线程有数据取出了,你可以放入数据了。

好了,以上就是ArrayBlockingQueue中放入取出数据的操作源码分析,多线程中总会提到生产者消费者模式,其实用ArrayBlockingQueue实现是很简单的,ArrayBlockingQueue只是将wait,notify操作进行了封装而已。

LinkedBlockingQueue源码就不一一分析了,主要区别的LinkedBlockingQueue内部是用链表来存储数据的,并且有两把锁,放数据锁与取数据锁,也就是放入数据的线程和取出数据的线程可以同时操作LinkedBlockingQueue,而ArrayBlockingQueue中放数据线程与取数据线程是互斥的,不能同时操作,LinkedBlockingQueue初始化的时候可以不指定容器大小,如果不指定则容器大小为Integer.MAX_VALUE,而ArrayBlockingQueue则必须指定容器大小。

好了以上就是本篇全部内容了,希望对你有用。

声明:文章将会陆续搬迁到个人公众号,以后文章也会第一时间发布到个人公众号,及时获取文章内容请关注公众号

java线程池技术(一):ThreadFactory与BlockingQueue的更多相关文章

  1. java线程池技术(二): 核心ThreadPoolExecutor介绍

    版权声明:本文出自汪磊的博客,转载请务必注明出处. Java线程池技术属于比较"古老"而又比较基础的技术了,本篇博客主要作用是个人技术梳理,没什么新玩意. 一.Java线程池技术的 ...

  2. java线程池技术

    1.线程池的实现原理?简介: 多线程技术主要解决处理器单元内多个线程执行的问题,它可以显著减少处理器单元的闲置时间,增加处理器单元的吞吐能力.假设一个服务器完成一项任务所需时间为:T1 创建线程时间, ...

  3. 转:JAVA线程池ThreadPoolExecutor与阻塞队列BlockingQueue

    从Java5开始,Java提供了自己的线程池.每次只执行指定数量的线程,java.util.concurrent.ThreadPoolExecutor 就是这样的线程池.以下是我的学习过程. 首先是构 ...

  4. Java线程池技术以及实现

    对于服务端而言,经常面对的是客户端传入的短小任务,需要服务端快速处理并返回结果.如果服务端每次接受一个客户端请求都创建一个线程然后处理请求返回数据,这在请求客户端数量少的阶段看起来是一个不错的选择,但 ...

  5. java线程池系列(1)-ThreadPoolExecutor实现原理

    前言 做java开发的,一般都避免不了要面对java线程池技术,像tomcat之类的容器天然就支持多线程. 即使是做偏后端技术,如处理一些消息,执行一些计算任务,也经常需要用到线程池技术. 鉴于线程池 ...

  6. 池化技术之Java线程池

     https://blog.csdn.net/jcj_2012/article/details/84906657 作用 线程池,通过复用线程来提升性能; 背景 线程是一个操作系统概念.操作系统负责这个 ...

  7. 深度分析:Java并发编程之线程池技术,看完面试这个再也不慌了!

    线程池的好处 Java中的线程池是运用场景最多的并发框架,几乎所有需要异步或并发执行任务的程序都可以使用线程池.在开发过程中,合理地使用线程池,相对于单线程串行处理(Serial Processing ...

  8. Java基础-Java中的并法库之线程池技术

    Java基础-Java中的并法库之线程池技术 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.什么是线程池技术 二.

  9. Java线程池(ThreadPool)详解

    线程五个状态(生命周期): 线程运行时间 假设一个服务器完成一项任务所需时间为:T1 创建线程时间,T2 在线程中执行任务的时间,T3 销毁线程时间.    如果:T1 + T3 远大于 T2,则可以 ...

随机推荐

  1. 最小生成树之Prim算法

    描述 最近,小Hi很喜欢玩的一款游戏模拟城市开放出了新Mod,在这个Mod中,玩家可以拥有不止一个城市了! 但是,问题也接踵而来--小Hi现在手上拥有N座城市,且已知这N座城市中任意两座城市之间建造道 ...

  2. (Release Candidate)Candidate

    RC:(Release Candidate)Candidate是候选人的意思,用在软件或者操作系统上就是候选版本

  3. intern

    java.lang.String的intern()方法"abc".intern()方法的返回值还是字符串"abc",表面上看起来好像这个方 法没什么用处.但实际 ...

  4. linux_定时任务

    什么是定时任务? linux系统自身定期执行的任务和工作: 轮训系统日志.备份系统数据.清理缓存等 var/log/messages # 系统日志文件, ll /etc/|grep cron # 查询 ...

  5. sping中防止定时任务多处运行解决方法

    @Servicepublic class TimerJobService implements LzhTimerJobDao{ Logger logger = LoggerFactory.getLog ...

  6. junit源码解析--核心类

    JUnit 的概念及用途 JUnit 是由 Erich Gamma 和 Kent Beck 编写的一个开源的单元测试框架.它属于白盒测试,只要将待测类继承 TestCase 类,就可以利用 JUnit ...

  7. maven系列--eclipse的m2插件

    工欲善其事,必先利其器.我是讨厌用CMD指令来操作maven,既然eclipse已经给我们提供了插件,那我们为什么不使用呢?而且我觉得eclipse的各种插件都挺好用的.好了废话不多说了,现在开始整理 ...

  8. spring 声明式事务管理详解

    前言:spring框架对于事务管理提供了两种方案.一,编程式事务.二,声明式事务.本例主要剖析 声明式事务. 何为声明式事务: 通过spring的配置文件配置事务规则,或使用spring @Trans ...

  9. java 常见的几种运行时异常RuntimeException

    常见的几种如下:   NullPointerException - 空指针引用异常ClassCastException - 类型强制转换异常.IllegalArgumentException - 传递 ...

  10. Android4.0新控件

    谷歌在推出Android4.0的同时推出了一些新控件,Android4.0中最常用的新控件有下面5种.  1. Switch的使用 Switch顾名思义,就是开关的意思,有开和关两种状态. 当Swit ...