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

【Java基础】线程笔记——ThreadApi

2017-03-24 13:42 507 查看

线程的中断机制

第一种方法: 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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  java 线程