创建线程
创建线程的方式有两种:
继承 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。