Thread类是Java中的一个封装类,用于实现多线程操作,下面是Thread类一些基本用法
1.线程创建
1.1继承Thread类
- 继承Thread类,重写run方法,将要执行的语句写进run方法里
class MyThread extends Thread{ @Override public void run() { //下为执行语句 System.out.println("override run method."); } } public class TestDemo1 { public static void main(String[] args) { MyThread myThread = new MyThread(); //实例化对象 myThread.start(); //start启动线程 } }
- 使用匿名内部类,创建Thread子类对象
public class TestDemo3 { public static void main(String[] args) { Thread thread = new Thread(){ @Override public void run() { System.out.println("this is a thread."); } }; thread.start(); } }
1.2实现Runnable接口
- 实现Runnable接口,重写run方法
- 使用匿名内部类,创建Runnable子类对象
class MyRunnable implements Runnable{ @Override public void run() { System.out.println("Runnable implement."); } } public class TestDemo2 { public static void main(String[] args) { Thread thread = new Thread(new MyRunnable()); //实例化Thread,传入参数为新的Runnable对象 thread.start(); //执行线程 } }
public class TestDemo4 { public static void main(String[] args) { Thread thread = new Thread(new Runnable() { @Override public void run() { System.out.println("this is a thread."); } }); thread.start(); } }
Thread中常见的构造方法:
方法 | |
---|---|
Thread() | 创建线程对象 |
Thread(Runnable target) | 使用Runnable对象创建线程对象 |
Thread(String name) | 创建线程对象并命名 |
Thread(Runnable target,String name) | 使用Runnable对象创建线程对象并命名 |
如:
Thread t1 = new Thread();
Thread t2 = new Thread(new MyRunnable());
Thread t3 = new Thread("Thread name1"); //命名
Thread t4 = new Thread(new MyRunnable(), "Thread name2");//命名
2.线程中断
线程一旦执行,在线程内代码执行结束前就不会停止,若执行过程中需要线程停止运行,就要通过线程中断的方法来完成,通常中断的方式有两种
- 共享的标志位
private static class TestFlag implements Runnable{
public volatile boolean flag = false;
@Override
public void run() { //重写run方法
while(!flag){
System.out.println("Thread is running.");
try {
Thread.sleep(1000); //每次循环休眠1秒
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("Thread is interrupted."); //线程中断后跳出循环输出
}
}
public static void main(String[] args) throws InterruptedException {
TestFlag testFlag = new TestFlag();
Thread thread = new Thread(testFlag,"aThread"); //新建线程
System.out.println("start a thread:" + Thread.currentThread().getName());
thread.start(); //线程运行
Thread.sleep(5000); //主线程休眠5秒
testFlag.flag = true; //修改标志
}
设置标志为flag,通过判断flag 来决定线程是否继续运行,上述代码在修改标志位flag后,便停止运行跳出循环了
在使用公共标志位的时候需注意,在多线程的代码中,标志位必须用volatile关键字修饰
因为多线程的环境下,volatile才能保证关键字的代码可见性,保证代码的运行准确
- 通过interrupt()方法
Thread类里包含了一个布尔类型的变量作为线程是否被中断的标记
可以用Thread.interrupted()或Thread.currentThread.isInterrupted()代替自定义标志位
public class TestDemo {
private static class Test implements Runnable{
@Override
public void run() {
//下面也可以用Thread.interrupted()
while(!Thread.currentThread().isInterrupted()){
System.out.println("Thread is running.");
try {
Thread.sleep(1000); //休眠一秒
} catch (InterruptedException e) {
e.printStackTrace();
System.out.println("Interrupted!"); //中断后输出
break; //跳出循环
}
}
}
}
public static void main(String[] args) throws InterruptedException {
Test test = new Test();
Thread thread = new Thread(test,"threadName"); //创建线程
System.out.println(Thread.currentThread().getName()+" start");
thread.start(); //开始线程
Thread.sleep(5000);
thread.interrupt(); //设置标志位,中断线程
}
}
结果如下,可见代码运行5秒后,修改了标志位,线程就中断了
上面的中断用到了interrput()方法,常用的中断方法如下
方法 | 说明 |
---|---|
public void interrupt() | 中断对象关联的线程,如果线程正在阻塞,则以异常方式通知, 否则设置标志位 |
public static boolean interrupted() | 判断当前线程的中断标志位是否设置,调用后清除标志位 |
public boolean isInterrupted() | 判断对象关联的线程的标志位是否设置,调用后不清除标志位 |
thread收到通知通常有两种情况:
- 线程因调用wait(),join()或sleep()等方法而阻塞,则以InterruptedException异常的形式通知,并清除中断标志,此时线程会执行catch里的内容,而线程是否中断则取决于catch中代码的写法,可选择忽略异常,也可以选择跳出循环,如上述例子就是在捕捉异常后通过break跳出循环的
- 若没有调用上述方法,只是内部的一个中断标志位被设置,则thread可以通过Thread.interrupted()或Thread.currentThread().interrupted()判断指定线程的中断标志,其中前者会清除中断标志,后者不会清除
标志位是否被清除:
- Thread.interrupted()方法在使用后是会自动清除标志位的,即在判断完后就回到原状态
private static class MyRunnable implements Runnable{
@Override
public void run() {
//循环5次输出状态
for (int i = 0; i < 5; i++) {
System.out.println(Thread.interrupted()); //输出状态
}
}
}
public static void main(String[] args) {
MyRunnable myRunnable = new MyRunnable();
Thread thread = new Thread(myRunnable,"t1");
thread.start(); //线程开始
thread.interrupt(); //中断线程,然后清除标志位
}
结果:标志位被修改后就被清除了,所以只有第一次输出的是true
- Thread.currentThread(),isInterrupted()方法在使用后不会清除标志位
private static class MyRunnable implements Runnable{ @Override public void run() { //循环5次输出状态 for (int i = 0; i < 5; i++) { System.out.println(Thread.currentThread().isInterrupted()); } } } public static void main(String[] args) { MyRunnable myRunnable = new MyRunnable(); Thread thread = new Thread(myRunnable,"name"); thread.start(); //线程开始 thread.interrupt(); //线程中断 }
结果:
在第一次标志位被修改后输出为true,之后因为标志位未被清除,所以输出依旧为true
3.线程等待
在多线程的使用中,我们常常需要等待其他线程执行完后再开始执行自己的线程,这就涉及到线程等待的问题
线程等待我们使用的方法是join():
public void join() | 等待线程结束 |
public void join(long millis) | 等待线程结束,最多等待millis毫秒 |
public void join(long millis,int nanos) | 精度到纳秒级 |
如thread.join(),就是让其他线程等待直至thread线程执行结束
4.线程休眠
线程休眠使用的是Thread的类方法sleep()
public static void sleep(long millis) | 休眠当前线程至少milli毫秒 |
public static void sleep(long millis,int nanos) | 精度至纳秒级 |
注意,在使用sleep放法的时候,进行异常处理
因为线程调度的不可控性,所以该方法的休眠时间是大于等于参数设置的时间的
5.获取当前线程的引用
当多线程运行时,你想知道现在运行的线程是哪个,就可以用Thread的类方法
Thread.currentThread()来返回这个线程的引用