创建线程
创建线程的方式有两种:
- 继承
Thread
类 - 实现
Runnable
接口
Thread
类实现了 Runnable
接口。使用继承 Thread
类的方式创建线程时,最大的局限是不支持多继承。所以为了支持多继承,应该使用实现 Runnable
接口的方式。两种方式创建的线程在工作时是一样的,没有本质区别。
第一种方式,继承 Thread
类并重写 run()
方法:
public class Work extends Thread { @Override public void run() { System.out.println("Working..."); } } public class Run { public static void main(String[] args) { Work work = new Work(); work.start(); System.out.println("End!"); } }
运行结果可能 “End!”先输出。在使用多线程时,运行结果与调用顺序是无关的。
调用
run()
方法只是普通的方法调用,不会启动线程。如果多次调用start()
方法,会抛出IllegalThreadStateException
异常。
第二种方式,实现 Runnable
接口:
public class Work implements Runnable { @Override public void run() { System.out.println("Working..."); } } public class Run { public static void main(String[] args) { Thread t = new Thread(new Work()); t.start(); System.out.println("End!"); } }
这种方式与第一种在运行上没有什么区别。其优点在于突破了单继承的限制。
Thread 类的部分构造方法:
构造方法 | 说明 |
---|---|
Thread() | 创建一个新的线程 |
Thread(String name) | 创建一个新的线程,并指定名称 |
Thread(Runnable target) | 创建一个新的线程,将 target 作为运行对象 |
Thread(Runnable target, String name) | 将 target 作为运行对象,并指定名称 |
Thread(ThreadGroup group, Runnable target) | 将 target 作为运行对象,并作为线程组的一员 |
线程的方法
currentThread() 方法
currentThread()
方法返回正在被执行的线程的信息。
public class Run() { public static void main(String[] args) { System.out.println(Thread.currentThread().getName()); } }
以上代码在控制台输出 “main“,说明该方法被名为 main 的线程调用。
import static java.lang.System.out; public class Run { static class Work extends Thread { @Override public void run() { out.printf("%s 被调用\n", currentThread().getName()); } } public static void main(String[] args) { Work t1 = new Work(), t2 = new Work(); t1.start(); t2.start(); } }
以上代码运行结果:
Thread-0 被调用 Thread-1 被调用 Process finished with exit code 0
在
run()
方法中可以省略Thread
直接调用currentThread()
方法。
isAlive() 方法
该方法判断当前线程是否处于活动状态。
import static java.lang.System.out; public class Run { static class Work extends Thread { @Override public void run() { out.printf("运行中 %s\n", isAlive()); } } public static void main(String[] args) throws Throwable { Work t = new Work(); out.printf("运行前: %s\n", t.isAlive()); t.start(); // 等待线程运行完成 Thread.sleep(1000); out.printf("运行结束: %s\n", t.isAlive()); } }
以上代码运行结果:
运行前: false 运行中 true 运行结束: false Process finished with exit code 0
sleep() 方法
sleep()
方法指定毫秒数让当前线程休眠(暂停运行),该操作不会释放锁。
停止线程
interrupt() 方法
interrupt()
方法并不能立刻停止线程,只是在在线程中打了一个停止的标记。
import static java.lang.System.out; public class StopThread { static class Work extends Thread { @Override public void run() { for (int i = 1; i <= 50000; i++) { out.printf("i = %d\n", i); } } } public static void main(String[] args) throws Throwable { Work work = new Work(); work.start(); Thread.sleep(200); work.interrupt(); out.println("Call interrupt!"); } }
以上代码运行结果:
... i = 8190 i = 8191 i = 8192 Call interrupt! i = 8193 i = 8194 i = 8195 ...
interrupt()
方法调用后,线程仍在运行。
要使用 interrupt()
方法停止线程,需要在线程中判断中断状态,有两个方法:
interrupted()
:测试当前线程是否是中断状态,执行后将状态清除,设置为false
;isInterrupted()
:作用同上,但是不清除状态。
import static java.lang.System.out; public class StopThread { static class Work extends Thread { @Override public void run() { for (int i = 1; i <= 50000; i++) { if (isInterrupted()) { out.println("跳出循环!"); break; } out.printf("i = %d\n", i); } } } public static void main(String[] args) throws Throwable { Work work = new Work(); work.start(); Thread.sleep(200); work.interrupt(); out.println("Call interrupt!"); } }
以上代码执行结果:
... i = 8301 i = 8302 i = 8303 i = 8304 i = 8305 i = 8306 i = 8307 Call interrupt! 跳出循环! Process finished with exit code 0
在调用 interrupt()
方法后,循环已经退出。但是这种方式只是跳出了循环,假如 for 循环外还有代码,仍然会执行。
抛出异常停止线程
可以在判断线程状态为中断时,抛出一个异常,在 catch
或 finally
块中做中断后的处理:
import static java.lang.System.out; public class StopThread { static class Work extends Thread { @Override public void run() { try { for (int i = 1; i <= 50000; i++) { if (isInterrupted()) { out.println("Interrupted!"); throw new InterruptedException("抛出异常!"); } out.printf("i = %d\n", i); } out.println("for 循环结束!"); } catch (InterruptedException e) { out.println(e.getMessage()); } } } public static void main(String[] args) throws Throwable { Work work = new Work(); work.start(); Thread.sleep(200); work.interrupt(); out.println("Call interrupt!"); } }
以上代码将线程要执行的任务放入 try
块中,当判断为中断状态时,抛出 InterruptedException
,如果需要释放锁,可以在 finally
块中执行。
也可以配合 return
来停止线程:
if (isInterrupted()) { return; }
暂停线程
Java 提供了 和 suspend()
方法来暂停和恢复线程,不过这两个方法已经过期作废了。resume()
suspend()
方法暂停线程时,不会释放锁。所以使用 suspend()
方法容易产生死锁。
如果需要暂停线程,可以加入一个标记,若标记指出线程需要暂停,使用 wait()
进入等待状态,如需要恢复,使用 notify()
唤醒。
import static java.lang.System.out; public class StopThread { static class Work extends Thread { // 暂停标记 private boolean isSuspended = false; void pause() { isSuspended = true; } synchronized void wake() { isSuspended = false; // 唤醒 this.notify(); out.println("已唤醒!"); } @Override public void run() { synchronized (this) { try { for (int i = 1; i <= 5000; i++) { if (isInterrupted()) { return; } if (isSuspended) { out.println("已暂停!"); // 等待 this.wait(); } out.printf("%s i = %d\n", getName(), i); } out.printf("%s end!\n", getName()); } catch (InterruptedException e) { out.println(e.getMessage()); } } } } public static void main(String[] args) throws Throwable { Work work = new Work(); work.start(); Thread.sleep(100); // 暂停 work.pause(); Thread.sleep(100); // 唤醒 work.wake(); } }
以上代码使用 wait()
和 notify()
暂停与恢复线程。运行结果:
... Thread-0 i = 202 Thread-0 i = 203 Thread-0 i = 204 已暂停! 已唤醒! Thread-0 i = 205 Thread-0 i = 206 Thread-0 i = 207 ... Thread-0 i = 4998 Thread-0 i = 4999 Thread-0 i = 5000 Thread-0 end! Process finished with exit code 0
yield 方法
yield()
方法的作用是放弃当前的 CPU 资源,让其他的任务去占用 CPU 执行时间。但放弃的时间不确定,有可能刚刚放弃,马上又获得时间片。
import static java.lang.System.currentTimeMillis; import static java.lang.System.out; public class Yield { static class Work extends Thread { @Override public void run() { long before = currentTimeMillis(); int sum = 0; for (int i =1; i < 2000000; i++) { // yield(); sum += (i + 1); } long after = currentTimeMillis(); out.printf("Cost: %dms\n", after - before); } } public static void main(String[] args) { new Work().start(); } }
以上代码不使用 yield()
方法时大概 15ms 执行完,加上后大概有 500ms。