Thread的创建
Android中有两种方式可以创建一个Thread,可以直接new 一个Thread对象,或者实现一个Runnable对象给Thread来创建Thread对象。
class Thread implements Runnable
Thread thread= new Thread(){
@Override
public void run(){
System.out.println("-------------1");
}
};
thread.start();
Thread thread1 = new Thread(new Runnable() {
@Override
public void run(){
System.out.println("-------------2");
}
});
thread1.start();
复制代码
Thread本身实现了Runnable接口,当调用Thread的run方法时,会判断是否设置了单独的Runnable对象,若设置了则执行Runnable的run方法,如果没有设置我们需要重写Thread的run方法。
Thread的中断
Thread中提供了以下三个方法来中断和判断中断Thread:
public void interrupt(){} //中断线程
public boolean isInterrupted(){} //判断线程是否被中断
public static boolean interrupted(){} //判断线程是否被中断并清除中断状态
复制代码
若当前线程处于阻塞状态或者试图执行一个阻塞操作时,调用Thread对象的interrupt方法来中断线程会导致InterruptedException异常,同时中断状态将会被复位(由中断状态变为非中断状态)。
public class SyncTask implements Runnable{
static int count = 0;
static SyncTask syncTask = new SyncTask();
@Override
public void run(){
synchronized (this){
for(int i = 0;i < 5;i++){
count++;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
System.out.println("interrupt="+Thread.currentThread().isInterrupted());
}
}
}
}
//执行函数
Thread thread1=new Thread(syncTask,"thread1");
thread1.start();
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
thread1.interrupt();
}
//执行结果
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
interrupt=false
复制代码
可以看到当线程调用Thread.sleep时,线程进入阻塞状态,此时调用interrupt方法会抛出异常。如果线程处于运行状态,我们可以发现就算调用interrupt方法依然不会中断线程.
@Override
public void run(){
synchronized (this){
while (true){
System.out.println("itr="+Thread.currentThread().isInterrupted());
}
}
}
//执行函数
Thread thread1=new Thread(syncTask,"thread1");
thread1.start();
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
thread1.interrupt();
}
//执行结果
。。。
itr=false
itr=true
itr=true
itr=true
itr=true
。。。
复制代码
发现指示中断的标志改变了,而线程并没有中断。所以需要我们在线程中判断是否中断了线程,来主动跳出循环结束线程。
@Override
public void run(){
synchronized (this){
while (true){
if(Thread.currentThread().isInterrupted()) return;
System.out.println("itr="+Thread.currentThread().isInterrupted());
}
}
}
复制代码
这里根据isInterrupted来判断是否中断线程,结束循环。看到这里我们可以得出如何结束线程的两种方法:
当线程处于阻塞状态或试图执行一个阻塞操作时,调用interrupt方法会抛出interrupt异常并将中断复位,此时我们可以拦截异常执行结束线程操作。
当线程处于运行状态时,需要调用interrupt方法,并在线程的run方法中判断线程中断状态来结束线程。
join方法
join() // join(0);
join(long millis, int nanos)
join(long millis)
复制代码
调用join方法的线程对象所在的线程会进入WAITING状态,等待线程对象run方法执行完毕,或者millis时间到达后才会继续往下执行。
例如以下代码:main线程会等到20s之后才会执行。
public static void main(String[] args) {
TestThread testThread = new TestThread();
testThread.start();
try {
testThread.join(20000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("main thread is run ");
System.out.println("---------------------");
}
static class TestThread extends Thread {
@Override
public void run() {
System.out.println("child thread is run ");
long time = System.currentTimeMillis()+20000;
while (System.currentTimeMillis()
}
System.out.println("child thread is end ");
}
}
复制代码
如果我们把join方法中millis改为2000,表明child线程执行2s后,会唤醒main线程,main线程继续执行。
join方法内部采用wait和notifyAll来实现线程的等待和唤醒。
public final void join(long millis) throws InterruptedException {
synchronized(lock) {
long base = System.currentTimeMillis();
long now = 0;
if (millis < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
if (millis == 0) {
while (isAlive()) {
lock.wait(0);
}
} else {
while (isAlive()) {
long delay = millis - now;
if (delay <= 0) {
break;
}
lock.wait(delay);
now = System.currentTimeMillis() - base;
}
}
}
}
复制代码
main线程调用child线程的join方法,获得child线程的对象锁,当调用wait方法后,main线程释放对象锁,进入WAITING状态等待唤醒,不在往下执行。这里的isAlive方法判断当前子线程是否存活,若子线程不存活则main线程不在等待。
到这里我们需要考虑一下,main线程进入阻塞状态后,是如何被唤醒的?
结果就是:
唤醒操作由Thread对象的系统操作来完成,当线程执行结束后,在thread.cpp中会调用notifyAll方法唤醒所有等待的线程。
wait、notify、notifyAll使用
wait、notify、notifyAll必须在synchronized修饰的方法或者代码块中使用,否则会抛出IllegalMonitorStateException异常。因为在使用这三类方法时Thread必须获取当前对象锁的monitor监控器对象。注意wait之后,其他线程必须调用notify才会继续执行当前线程,同一时候只有一个线程可以持有monitor监控器对象,未持有monitor监控器对象的Thread调用notifyAll时。会抛出IllegalMonitorStateException异常。
Exception in thread "Thread-0" java.lang.IllegalMonitorStateException
at java.lang.Object.notifyAll(Native Method)
at com.mdy.string_struct.MyThread$TestThread.run(MyThread.java:29)
复制代码
wait 和sleep区别
sleep属于Thread的静态方法,当Thread被中断时,调用sleep方法会抛出InterruptedException异常。wait属于Obkect对象的方法,必须在synchronized修饰的方法或者代码块中使用。
wait会使当前线程暂停进入阻塞状态,并释放监控器monitor对象。sleep只会阻塞当前线程并不会释放monitor对象。调用notify、notifyAll后,也不是立即释放monitor对象,而是在synchronized方法结束后才自动释放锁。