Thread类
1、创建线程
线程的创建方法一共有五种,其中lambda表达式的创建方式最为常用,这里简单的给大家介绍一下这五种创建。
1.1、继承 Thread,重写run
class MyThread2 extends Thread { //创建一个类继承Thread,并重写run @Override public void run() { System.out.println("hello thread"); } } public class ThreadDemo2 { public static void main(String[] args) { Thread t = new MyThread2(); //创建MyThread类的实例 t.start(); //调用start方法启动线程 } }
1.2、实现 Runnable,重写run
Thread(Runnable target),使用runnable对象创建线程对象。 class MyThread3 implements Runnable { //实现Runnable接口 @Override public void run() { System.out.println("hello runnable"); } } public class ThreadDemo3 { public static void main(String[] args) { Runnable runnable = new MyThread3(); //创建runnable对象 Thread t = new Thread(runnable); //将runnable对象作为target参数 t.start(); //start启动线程 } }
1.3、使用匿名内部类,继承 Thread,重写run
public class ThreadDemo4 { public static void main(String[] args) { Thread t = new Thread() { //继承thread类的匿名内部类 @Override public void run() { System.out.println("hello thread"); } }; t.start(); } }
1.4、使用匿名内部类,实现 Runnable,重写run
public class ThreadDemo5 { public static void main(String[] args) { Thread t = new Thread(new Runnable() { //实现Runnable接口的匿名内部类 @Override public void run() { System.out.println("hello runnable"); } }); t.start(); } }
1.5、使用 lambda 表达式(最常用)
public class ThreadDemo6 { public static void main(String[] args) { Thread t = new Thread(() -> { //实现Runnable接口重写run的lambda写法【推荐使用】 System.out.println("hello thread"); }); t.start(); } }
2、终止线程
有时我们需要让正在执行的线程终止,为了让线程能够停止,需要添加一些机制。
2.1、通过共享的标记来进行沟通
public class ThreadInterrupt { public static boolean isQuit = false; //设置标志位isQuit充当控制开关 public static void main(String[] args) throws InterruptedException { Thread t = new Thread(() -> { while (!isQuit) { //控制while终止 System.out.println("hello thread"); try { Thread.sleep(1000); //让每个打印间隔1秒 } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("线程执行完毕"); }); t.start(); Thread.sleep(3000); //sleep睡眠3秒后再修改标志位 isQuit = true; } }
2.2、调用 interrupt() 方法来通知
使用 Thread.interrupted() 或者 Thread.currentThread().isInterrupted() 代替自定义标识位。
其中,Thread.currentThread() 表示获取当前线程实例,类似于 this 。而这里没有直接使用this是因为此处的Thread线程使用的是匿名内部类,无法通过this获取当前实例。
最后使用 interrupt() 进行终止线程。
public static void main(String[] args) throws InterruptedException { Thread t = new Thread(() -> { while (!Thread.currentThread().isInterrupted()) { System.out.println("hello thread"); try { Thread.sleep(1000); //让每个打印间隔1秒 } catch (InterruptedException e) { break; //注意此处需要添加break,因为sleep会清空interrupted标志位 } } System.out.println("线程执行完毕"); }); t.start(); Thread.sleep(3000); //sleep睡眠3秒后再调用interrupt终止线程 t.interrupt(); }
3、等待线程
多个线程的执行顺序是不确定的(随机调度,抢占式执行)
虽然线程底层的调度是随机的,但是可以在应用程序中,通过一些api,来影响到线程的调度顺序使用join就是其中一种方式,join()方法可以确定线程的结束顺序。
public static void main(String[] args) { Thread t1 = new Thread(() -> { System.out.println("hello thread1"); }); Thread t2 = new Thread(() -> { System.out.println("hello thread2"); }); t1.start(); //此时t1线程开始执行 t1.join(); //等待t1结束后再执行下面代码 t2.start(); //此时t2线程开始执行 t2.join(); //等待t2结束后再执行下面代码 System.out.println("hello main"); //最后执行main主线程中的打印 }
【谁调用,谁等待】main方法中调用t.join(),main主线程就阻塞等待t线程结束,再继续执行。
典型的使用场景:
使用多个线程并发进行一系列计算,用一个线程阻塞等待上述计算线程,等到所有的线程都计算完了,最终这个线程汇总结果。
4、获取线程实例
有两种获取线程实例的方法,一种是 this ,另一种是 Thread.currentThread() 。
其中需要注意的是:this不能使用到匿名内部类中,因此匿名内部类只能通过Thread.currentThread() 来获取实例。
class MyThread extends Thread { @Override public void run() { System.out.println("MyThread :"+this.getName()); //使用this直接获取实例 } } public class GetThread { public static void main(String[] args) throws InterruptedException { Thread t1 = new Thread(() -> { Thread thread = Thread.currentThread(); //匿名内部类中通过currentThread获取实例 System.out.println("t1线程中: "+thread.getName()); }); Thread t2 = new MyThread(); t1.start(); t1.join(); t2.start(); } }