线程的中断机制
第一种方法: Thread.stop() (已废弃)
【缺点】线程不安全,已不再使用
第二种方法: Thread.interrupt()
当一个线程运行时,另一个线程可以调用对应的 Thread 对象的 interrupt()方法来中断它,该方法只是在目标线程中设置一个标志,表示它已经被中断,并立即返回。
如果只是单纯的调用 interrupt()方法,线程并没有实际被中断,会继续往下执行
public class InteruptThread implements Runnable {
@Override
public void run() {
boolean stop = false;
while(!stop){
System.out.println("This Thread is Running ......");
long time = System.currentTimeMillis();
while((System.currentTimeMillis() - time < 1000)){
}
//当interrupt=true时,需要去处理它的中断状态,不然线程会一直运行
if(Thread.currentThread().isInterrupted()){
Thread.currentThread().interrupt();
break;
}
}
System.out.println("This Thread Exit Under Request");
}
}
运行
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(new InteruptThread(),"Interupt");
System.out.println("InteruptThread starting ...");
t.start();
Thread.sleep(3000);
System.out.println("InteruptThread interupted");
t.interrupt();
System.out.println("线程是否中断---->"+t.isInterrupted());
Thread.sleep(3000);
System.out.println("Stop Application");
}
console
InteruptThread starting ...
This Thread is Running ......
This Thread is Running ......
This Thread is Running ......
InteruptThread interupted
线程是否中断---->true
This Thread Exit Under Request
Stop Application
如果去掉if语句判断去处理中断状态,线程一直会一直运行(形成死循环)
InteruptThread starting ...
This Thread is Running ......
This Thread is Running ......
This Thread is Running ......
InteruptThread interupted
线程是否中断---->true
This Thread is Running ......
This Thread is Running ......
This Thread is Running ......
Stop Application
This Thread is Running ......
This Thread is Running ......
注意:一定要对Thread.isInterrupted()状态进行处理
补充(yield()和join()使用)
- join 方法用线程对象调用,如果在一个线程 A 中调用另一个线程 B 的 join 方法,线程 A 将会等待线程 B 执行完毕后再执行。
- yield 可以直接用 Thread 类调用,yield 让出 CPU 执行权给同等级的线程,如果没有相同级别的线程在等待 CPU 的执行权,则该线程继续执行。
守护线程(后台运行线程)
守护线程不需要关心它何时结束,进程结束,守护线程自动结束
不要在守护线程中执行业务逻辑操作(比如对数据的读写等)
必须在启动线程前标记守护线程
setDeamon(true);
Java垃圾回收、内存管理就是一个守护线程
注意
- setDaemon(true)必须在调用线程的 start()方法之前设置,否则会跑出 IllegalThreadStateException 异常。
- 在守护线程中产生的新线程也是守护线程。
- 不要认为所有的应用都可以分配给守护线程来进行服务,比如读写操作或者计算逻辑
线程阻塞
- 当线程执行 Thread.sleep()时,它一直阻塞到指定的毫秒时间之后,或者阻塞被另一个线程打断;
- 当线程碰到一条 wait()语句时,它会一直阻塞到接到通知(notify())、被中断或经过了指定毫秒时间为止(若制定了超时值的话)
- 线程阻塞与不同 I/O 的方式有多种。常见的一种方式是 InputStream的read()方法,该方法一直阻塞到从流中读取一个字节的数据为止,它可以无限阻塞,因此不能指定超时时间;
- 线程也可以阻塞等待获取某个对象锁的排他性访问权限(即等待获得 synchronized 语句必须的锁时阻塞)。
线程组
方便管理线程,并且可以为某些线程设置相同特定的属性
例如:setDeamon()、设置未处理异常的处理方法、设置统一的安全策略等
每个ThreadGroup都可以包含一组子线程或者一组子线程组
在一个进程中线程组是以树形存在
system线程组是所有线程的顶级父线程组
//获取当前线程的线程组
Thread.currentThread().getThreadGroup()
- Java中允许对一个线程组中所有线程进行操作
- Java多线程另一个重要的特性就是线程安全。线程组机制允许通过分组来区分不同特性的线程
线程池和线程组区别:线程组方便管理线程对象。线程池管理线程的声明周期,复用线程,减少创建销毁进程的开销
线程安全
当前线程副本ThreadLocal
- ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每个线程都可以独立的改变自己的副本(Local想表达的意思)
- remove() 是jdk5以后新增的方法,java会自动垃圾回收变量,调用该方法加快垃圾回收的速度
- get()/set() 得到/设置ThreadLocal的值
-initialValue() 返回变量的初始值,只执行一次。若缺省,直接返回一个null
public class ThreadLocalValue {
private static ThreadLocal<Integer> seqNum = new ThreadLocal<Integer>(){
protected Integer initialValue() {return 0;};
};
public Integer nextNum(){
seqNum.set(seqNum.get() + 1);
return seqNum.get();
}
public ThreadLocal<Integer> getThreadLocal(){
return seqNum;
}
}
运行
public static void main(String[] args) {
ThreadLocalValue value = new ThreadLocalValue();
TestClient t1 = new TestClient(value);
TestClient t2 = new TestClient(value);
TestClient t3 = new TestClient(value);
t1.start();
t2.start();
t3.start();
}
static class TestClient extends Thread{
private ThreadLocalValue tlv = null;
public TestClient(ThreadLocalValue tlv){
this.tlv = tlv;
}
@Override
public void run() {
for (int i = 0; i < 3; i++) {
System.out.println("thread["+Thread.currentThread().getName()+"] ---> value : "+tlv.nextNum());
}
tlv.getThreadLocal().remove();
}
}
console
thread[Thread-2] ---> value : 1
thread[Thread-1] ---> value : 1
thread[Thread-0] ---> value : 1
thread[Thread-1] ---> value : 2
thread[Thread-2] ---> value : 2
thread[Thread-1] ---> value : 3
thread[Thread-0] ---> value : 2
thread[Thread-0] ---> value : 3
thread[Thread-2] ---> value : 3
上面可得出
所产生的序号虽然共享一个实例,但是没用互相干扰,而是各自独立产生序列号,确定每个线程单独提供了一个变量副本
源码解析
set
1.首先通过getMap(t)得到当前相关ThreadLocalMap,将值放入map中,如果为空则重新创建Map createMap(t,value)
ThreadLocalMap 是线程隔离的核心,它是ThreadLocal的静态内部类,实现了键值对的设置和获取(对比Map) 存储的值,只能被当前线程读取和修改,ThreadLocal操作每个线程特有的ThreadLocalMap,从而实现了变量在不同线程中的隔离
ThreadLocalMap的键的this对象指的就是ThreadLocal,值就是所存储的值。
static class ThreadLocalMap
总结
ThreadLocal处理线程的局部变量,要比synchronized同步机制解决线程问题更简单,更方便,而且结果程序拥有更高的并发性
线程的异常处理
所有异常必须在run方法进行处理,不能抛出throw exception
- 方法一:在try…catch内处理
- 方法二:实现一个UncaughtExceptionHandler接口
处理unchecked异常
设计一个异常线程B
public class ThreadB implements Runnable {
@Override
public void run() {
int num = Integer.parseInt("A");
System.out.println(num);
System.out.println("This is Exception Thread B ");
}
}
处理线程异常
public class ExceptionOfThreadB implements UncaughtExceptionHandler {
@Override
public void uncaughtException(Thread t, Throwable e) {
System.out.println("Thread -->"+t.getName()+"("+t.getId()+")");
System.out.println("Thread Exception -->"+e.getClass().getName());
System.out.println("exception message -->"+e.getMessage());
System.out.println("state -->" + t.getState());
}
}
运行
public static void main(String[] args) {
ThreadB threadException = new ThreadB();
Thread t = new Thread(threadException,"threadException");
//设置异常
t.setUncaughtExceptionHandler(new ExceptionOfThreadB());
t.start();
}
console
Thread -->threadException(10)
Thread Exception -->java.lang.NumberFormatException
exception message -->For input string: "A"
state -->RUNNABLE