您的位置:首页 > 编程语言 > Java开发

Java线程和线程池(一)

2015-10-07 15:39 369 查看
晚上或周末会抽出点时间对一些知识点进行整理,但是记录的都是十分散碎的点,无法回头去看,因此还是在一个阶段之后进行一篇知识整理。本期整理主题为Java线程池。

线程池是基础开发中常用的一种技术。业务开发经常由于容器的封装不直接面对线程和相应的任务。线程池的主要作用是可以减少创建和销毁线程所花费的时间以及系统资源的开销。同时,线程池限制了并发线程的数量,防止过度的创建线程导致系统资源的耗尽,以及频繁的上下文切换降低 CPU 使用率。

在整理线程池之前先去挖一挖线程的一些点:

Java线程

Thread实现了Runnable接口。在编写线程任务时,应该实现Runnable接口而不是直接继承Thread,除非需要在子类中增强Thread。

线程状态:JVM中线程的模型与教学中提到的有些不同,它进行了一些状态细化。包含以下六种状态:New, Runnable, Blocked, Waiting, Timed_Waiting, Terminated.状态以及之间的转换条件如下图所示:

Runnable状态:处于这个状态的线程只是表明在JVM中是活跃的,此时可细分为两种情况:获得操作系统的资源处于运行中,或者等待处理器资源。

Blocked:通常由一些同步或者I/O操作导致,在获得相应的资源后恢复。

Waiting/Timed_Waiting:一般都是主动操作进入相应的状态,通常由Object.wait、Thread.join、Thread.sleep等触发。由Object.notify(),Object.notifyAll()或者达到等待时间后恢复。

Terminated:中断或者正常执行结束。

Thread中常见的方法:

1、yield()

yield: static native 方法。使用该方法,当前线程会让出CPU的使用权,给其他线程执行机会, 但并不能保证下一个执行线程是该线程。这个方法在实际应用中很少会被使用,经常用于测试或者debug。

2、sleep(long millis), sleep(longmillis, int nanos)

sleep: static native方法。让当前线程至少睡眠一段时间,睡眠期间依然保留资源。时间到达后并不一定立刻会执行,处于runnable状态等待CPU调度。如果在等待期间被其它线程interrupt,会抛出InterruptedException异常并清空interrupted状态。

3、join(long)

同步方法,在指定的时间内等待引用线程执行。如果参数是0,则一直等待直到引用线程结束。

实现是通过判断this.isLive循环调用wait方法。代码如下:

注意:当前线程与引用线程的区别

看到以上代码有点奇怪,isAlive和wait都是引用对象的方法,为何是使用该方法的线程等待而不是引用对象等待。事实上isAlive作用于引用线程,而wait作用于当前线程。看源码的时候注释的区别是:this thread和current thread。

4、start

start方法中调用native start0方法,然后JVM调用该线程的run方法。方法返回后主线程与子线程并发执行。如果主线程只调用run方法,这仅仅是执行run方法体不会产生子线程。

5、interrupt、isInterrupted、interrupted

interrupt:

线程中断自己总是被允许的,否者将会checkaccess。

中断状态被设定,线程不会立刻被中断,只有遇到中断检查时才会中断线程。线程调用以下方法时:(object或者thread)wait,join,sleep,会有中断检查,抛出InterruptedException,中断状态被清空。

isInterrupted

判断该线程是否被中断,中断状态不会被影响

interrupted

判断该线程是否被中断,并将中断状态清空。

ThreadLocal

另外一个跟线程息息相关的类是Threadlocal。Threadlocal是线程的本地变量,在并发控制中经常用到。每个线程通过相应的get,set方法获取或者设定属于自身的独立的副本。当线程结束后,所有本地变量都将被回收。

ThreadLocal在业务开发中也会常见,比如,在由Spring管理的单例bean中使用全局变量会被各个线程公用,此时如果需要单独副本可以使用ThreadLocal变量。

我们看一下它的实现原理:

(1)存储结构:

每个Thread维护了一个:Map(ThreadLocal.ThreadLocalMap)变量. 它的key是ThreadLocal,value是局部变量。每个线程可以有多个线程本地变量。

ThreadLocalMap与常见的HashMap不同:ThreadLocalMap没有实现Map接口。在处理冲突时使用的是开放定址法,HashMap是使用链表处理冲突。

(2)操作逻辑

set:当使用ThreadLocal变量设定线程局部变量时,它首先会获取到当前线程,然后拿到当前线程维护的ThreadLocalMap,并将ThreadLocal自身作为key。源码如下:

get同理:先拿到当前线程维护的ThreadLocalMap,然后用ThreadLocal自身作为key来获取对应的值。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: