如何正确停止线程 新建线程 新建线程的方式有多种
实现Runnable接口,重写run()方法
继承Thread类,重写run()方法
实现callable接口,重写call方法
使用线程池
使用定时器
…………..
代码举例-Runnable接口 Runnable接口重写run方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public class NewRunnable implements Runnable { @Override public void run () { System.out.println("I am NewRunnable Thread" ); } public static void main (String[] args) { NewRunnable newRunnable = new NewRunnable (); Thread thread = new Thread (newRunnable); thread.start(); } }
上述代码中新建NewRunnable
类实现了Runnable
接口后重写了run方法,实现新建一个线程,可能这样体现不出来多线程,可以加个循环。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 public class NewThread { public static void main (String[] args) { Thread thread = new Thread (new Runnable () { @Override public void run () { for (int i = 0 ; i < 5 ; i++) { try { Thread.sleep(200 ); System.out.println("i am " + i); } catch (InterruptedException e) { e.printStackTrace(); } } } }); thread.start(); try { Thread.sleep(500 ); System.out.println("i am mainThread" ); } catch (InterruptedException e) { e.printStackTrace(); } }
运行结果
1 2 3 4 5 6 i am 0 i am 1 i am mainThread i am 2 i am 3 i am 4
使用了匿名内部类重写了Runnable接口run方法,然后循环五次,每次睡眠200毫秒,主线程中睡眠500毫秒,所以当子线程循环两次后,在第三次打印中间主线程开始打印。
代码举例-Thread类 继承Thread类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public class NewThread extends Thread { @Override public void run () { System.out.println("I am NewThread!" ); } public static void main (String[] args) { NewThread newThread = new NewThread (); newThread.start(); } }
新建线程总结 无论是实现Runnable接口还是继承Thread类,发现最终都是通过Thread类的start()方法开启的线程,而不是通过run()方法,看一下run方法和start方法的源代码
run()方法 1 2 3 4 5 6 7 8 9 10 11 12 13 public class Thread implements Runnable { public Thread (Runnable target) { init(null , target, "Thread-" + nextThreadNum(), 0 ); } @Override public void run () { if (target != null ) { target.run(); } } }
发现Thread也实现了Runnable接口,并重写了run方法,如上所示,我们在使用实现Runnable接口,并把对象当作参数传给Thread类中 public Thread(Runnable target);构造器时,这时候run()方法执行的就是传入对象的run()方法,run()方法中也只有这三行代码,所以并没有实现多线程。
start()方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 public synchronized void start () { if (threadStatus != 0 ) throw new IllegalThreadStateException (); group.add(this ); boolean started = false ; try { start0(); started = true ; } finally { try { if (!started) { group.threadStartFailed(this ); } } catch (Throwable ignore) { } } }
可以看到首先run()方法if (threadStatus != 0)
判断线程状态,而后调用了start0方法,而start0方法private native void start0();
可以看到是一个native方法,并非是由java实现
搜索后得知
1 2 3 在Java中,Thread类是用于实现多线程编程的类。当我们创建一个新线程并启动它时,实际上是调用了Thread类中的start()方法,该方法会执行一个本地方法start0()。start0()方法会启动一个新的系统线程,并调用run()方法。run()方法是线程执行的代码,当run()方法执行完毕后,线程就会自动结束。 start0()方法是一个本地方法(native method),它是由JVM实现的。本地方法是指使用C/C++等低级语言编写的方法,它们通常由JVM加载并在本地系统上执行。start0()方法在底层实现了线程的创建和启动,其具体实现会依赖于不同的操作系统和JVM实现。在JVM中,start0()方法的实现是由native 层面提供的,因此我们无法直接查看其源代码。
所以我们可以得知,在java中真正实现多线程的是Thread类中的start()方法。
补充 callable接口 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 public class NewCallable implements Callable { @Override public String call () throws Exception { String s = "I am newCallable!" ; System.out.println(Thread.currentThread().getName()); return s; } public static void main (String[] args) { NewCallable newCallable = new NewCallable (); FutureTask futureTask = new FutureTask <>(newCallable); Thread thread = new Thread (futureTask); thread.start(); try { Object o = futureTask.get(); System.out.println(o); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } System.out.println("i am mainThread" ); System.out.println(Thread.currentThread().getName()); } }
可以看到实现callable接口也是通过Thread类创建的新线程,不同的是使用了FutureTask
类接收了返回值
简要说一下futureTask
可以看到该类间接继承了Runnable接口,所以依然可以理解成callable接口就是实现Runnbale
接口,重写了run方法的方式实现了多线程,其他方式不在过多阐释。
停止线程 如何停止线程?
stop()?
destroy()?
suspend()?
interrupt()?
查看 API,我们会看到 java.lang.Thread
类型提供了一系列的方法如 start()、stop()、resume()、suspend()、destroy()等方法来管理线程。但是除了 start() 之外,其它几个方法都被声名为已过时(deprecated)
详细原因可看jdk
文档的解释
1 https://docs.oracle.com/javase/1.5 .0 /docs/guide/misc/threadPrimitiveDeprecation.html
正确停止线程的方式是使用interrupt()方法
Interrupt()方法 在 Java 中,Thread.interrupt()
方法用于中断线程,其作用是设置线程的中断标志位为 true
。被中断的线程可以通过检查自身的中断标志位来判断是否被中断,然后执行适当的操作。
如果线程被阻塞在某些操作上(如等待 I/O 操作、sleep()
等),调用 interrupt()
方法会中断该线程的阻塞状态,抛出 InterruptedException
异常,并清除中断标志位。如果线程没有被阻塞,则调用 interrupt()
方法只是设置线程的中断标志位为 true
,线程仍然可以继续运行。
举个例子 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 public class MyThread extends Thread { public void run () { try { while (!Thread.currentThread().isInterrupted()) { System.out.println("MyThread is running..." ); Thread.sleep(1000 ); } } catch (InterruptedException e) { System.out.println("MyThread is interrupted!" ); } } public static void main (String[] args) { MyThread myThread = new MyThread (); myThread.start(); try { Thread.sleep(5000 ); } catch (InterruptedException e) { e.printStackTrace(); } myThread.interrupt(); } }
新建了一个线程,在主线程中,我们睡眠5S后将新建线程中断,这是新线程捕获中断,并打印了MyThread is interrupted!
,
中断标志位 上方代码中,如果我们把while (!Thread.currentThread().isInterrupted())
换成 while (true)
发现运行结果是一样的,为什么呢?这就牵扯到中断标志位了,如上方所言 如果线程被阻塞在某些操作上(如等待 I/O 操作、sleep()
等),调用 interrupt()
方法会中断该线程的阻塞状态,抛出 InterruptedException
异常,并清除中断标志位。 上方代码中5s中断线程时,线程在sleep状态,这时候程序抛出 InterruptedException
异常,并清除中断标志位。在抛出异常时线程已经停止了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 public class MyThread extends Thread { public void run () { int i = 0 ; while (!Thread.currentThread().isInterrupted()){ System.out.println("正在打印:" + ++i); } } public static void main (String[] args) { MyThread myThread = new MyThread (); myThread.start(); int sum = 0 ; for (int i = 0 ; i < 1000000 ; i++) { sum += i; } myThread.interrupt(); } }
可以看到我们通过判断程序的中断信号来执行代码,当程序被中断时,跳出while循环,新线程运行结束
循环中使用注意事项 如果我们将try catch 放在循环中发现程序响应中断后并未停止,这是因为sleep()清除了中断标志位,所以不能将try catch语句放在循环中,而应该放在循环外。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public class MyThread extends Thread { public void run () { while (!Thread.currentThread().isInterrupted()) { try { System.out.println("MyThread is running..." ); Thread.sleep(500 ); } catch (InterruptedException e) { e.printStackTrace(); } } } public static void main (String[] args) { MyThread myThread = new MyThread (); myThread.start(); try { Thread.sleep(2000 ); } catch (InterruptedException e) { e.printStackTrace(); } myThread.interrupt(); } }
结果