线程的启动和停止简介
线程的启动
方法一 实现Runnable接口,重写run()函数,运行start()方法
Runnable run = new Runnable() {
@Override
public void run() {
while(true && !Thread.currentThread().isInterrupted()){
try{
System.out.println("运行");
Thread.sleep(100);
}catch (InterruptedException e){
e.printStackTrace();
Thread.currentThread().interrupt();
}
}
}
};
Thread thread = new Thread(run);
thread.start();
方法二 集成Thread类,重写run()函数,运行start()方法
public class FaultThreadPro extends Thread{
@Override
public void run(){
System.out.println("123");
}
public static void main(String[] args) throws InterruptedException {
Thread thread = new FaultThreadPro();
thread.start();
}
}
其他方法均为对以上两种方法的复用。
以上两种方法本质上也是相同的,均为对Thread类中run方法的设定。
start()方法的详解
start()方法为通知JVM,让JVM在空闲的时间在创建一个线程,Java语言本身并不能创建线程。需要调用底层native方法。
if (threadStatus != 0)
throw new IllegalThreadStateException();
/* Notify the group that this thread is about to be started
* so that it can be added to the group's list of threads
* and the group's unstarted count can be decremented. */
group.add(this);
boolean started = false;
try {
//该方法为native方法,为启动线程的核心方法
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
/* do nothing. If start0 threw a Throwable then
it will be passed up the call stack */
}
}
注意事项:
- start在启动线程时会检查线程状态只有在new状态下的线程才能继续,否则会抛出illegalThreadStateException。因此同一个线程不能重复start().
线程的停止
Java语言不能强行终止线程,只能通知某个线程它需要停止了,使用interrupt来通知。
停止线程的正确方法
- 代码运行结束线程自动停止
- 使用interrupt来请求停止线程
线程的interrupt机制
如果线程中没有死循环,一般线程不需要手动停止,interrupt方法一般用于线程中有死循环的情况。
run方法内没有sleep或wait方法等可以阻塞线程的方法时
可以直接在循环的判断条件中检测是否有其他线程通知自己需要中断,若检测到有中断信号,则跳出循环即可。
run方法中存在阻塞并且该阻塞可以响应中断而抛出InterruptedException
常见的方法有
- Object.wait()/wait(long)/wait(long,int)
- Thread.sleep(long)/sleep(long,int)
- Thread.join()/join(long)/join(long,int)
- java.util.concurrent.BlockingQueue.take()/put()
- java.util.concurrent.locks/Lock.lockInterruptibly()
- java.util.concurrent.CountDownLatch.await()
- java.util.concurrent.CyclicBarrier.await()
- java.util.concurrent.Exchanger.exchange(V)
- java.nio.channels.InterruptibleChannel
- java.nio.channels.Selector
当线程被以上方法阻塞时,在其他线程向该线程提供中断信号时,可以抛出InterruptedException信号。
当线程抛出中断后的处理方法
在有死循环的线程中,如果对中断异常执行不当,会导致该线程没有终止。
public class FaultThreadPro {
public static void main(String[] args) throws InterruptedException {
Runnable run = new Runnable() {
@Override
public void run() {
while(true && !Thread.currentThread().isInterrupted()){
try{
System.out.println("运行");
Thread.sleep(100);
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
};
Thread thread = new Thread(run);
thread.start();
Thread.sleep(1000);
System.out.println("发送信号");
thread.interrupt();
}
}
- 优先选择:传递中断
- 不想或无法传递:恢复中断
传递中断几位向上层抛出异常,一直抛至run方法。在run方法统一处理中断。
恢复中断,如果不想传递中断,那么我们在try{}catch中断之后,应该继续保持中断信号。若不保持中断信号,则该中断信号会被trycatch吞掉,使得上层方法无法得知自己被通知需要中断。
public class FaultThreadPro {
public static void main(String[] args) throws InterruptedException {
Runnable run = new Runnable() {
@Override
public void run() {
while(true && !Thread.currentThread().isInterrupted()){
try{
System.out.println("运行");
Thread.sleep(100);
}catch (InterruptedException e){
e.printStackTrace();
Thread.currentThread().interrupt();
//自己给自己发一个中断信号
}
}
}
};
Thread thread = new Thread(run);
thread.start();
Thread.sleep(1000);
System.out.println("发送信号");
thread.interrupt();
}
}
常见的有问题的停止线程有关的方法
被弃用的stop,suspend和resume方法
反对使用stop(),是因为它不安全。它会解除由线程获取的所有锁定,当在一个线程对象上调用stop()方法时,这个线程对象所运行的线程就会立即停止,假如一个线程正在执行:synchronized void { x = 3; y = 4;} 由于方法是同步的,多个线程访问时总能保证x,y被同时赋值,而如果一个线程正在执行到x = 3;时,被调用了 stop()方法,即使在同步块中,它也干脆地stop了,这样就产生了不完整的残废数据。而多线程编程中最最基础的条件要保证数据的完整性,所以请忘记线程的stop方法,以后我们再也不要说“停止线程”了。而且如果对象处于一种不连贯状态,那么其他线程能在那种状态下检查和修改它们。结果很难检查出真正的问题所在。
suspend()方法容易发生死锁。调用suspend()的时候,目标线程会停下来,但却仍然持有在这之前获得的锁定。此时,其他任何线程都不能访问锁定的资源,除非被"挂起"的线程恢复运行。对任何线程来说,如果它们想恢复目标线程,同时又试图使用任何一个锁定的资源,就会造成死锁。所以不应该使用suspend(),而应在自己的Thread类中置入一个标志,指出线程应该活动还是挂起。若标志指出线程应该挂起,便用 wait()命其进入等待状态。若标志指出线程应当恢复,则用一个notify()重新启动线程。
用volatile设置boolean标志位
该方法有其使用的局限性例如下面的例子
public class Tree{
ArrayBlockingQueue storage = new ArrayBlockingQueue(10);
static boolean isCanced = false;
static boolean isPause = false;
static Tree tree = new Tree();
static Thread consumer;
static Thread producer;
public static void main(String[] args) throws InterruptedException {
consumer = new Thread(tree.new Consumer());
producer = new Thread(tree.new Producer());
producer.start();
Thread.sleep(1000);
consumer.start();
Thread.sleep(2000);
isPause = true;
}
class Producer implements Runnable{
private int i = 0;
@Override
public void run() {
try {
while(!isCanced){
storage.put(i = i+100);
System.out.println(i+"已被生产");
Thread.sleep(10);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class Consumer implements Runnable{
@Override
public void run() {
try {
while(!isPause){
System.out.println(storage.take()+"已经被消费");
Thread.sleep(100);
}
//tree.producer.interrupt();
//这里如果使用isCances=true通知生产者线程停止工作,
//由于生产者线程的生产速度大于消费者线程的消费速度,
//所以生产者线程会经常阻塞在put方法中,如果我们在
//生产者线程阻塞的时候对改变生产者线程的isCanced信号,
//生产者线程就不会停止。
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}