
多线程入门系列(一) 线程的基本知识
线程的创建
- 继承Thread类
public class MyThread extends Thread{
@Override
public void run() {
super.run();
System.out.println("Hello MyThread");
}
}
- 实现Runnable接口
public class MyRunnableThread implements Runnable {
@Override
public void run() {
System.out.println("Hello MyRunnableThread");
}
}
- 实现Callable接口
class MyCallable implements Callable<String>{
@Override
public String call() throws Exception {
Thread.sleep(10000);
return "回调成功";
}
}
public class Main {
public static void main(String[] args) {
MyCallable myCallable = new MyCallable();
FutureTask<String> futureTask1 = new FutureTask<String>(myCallable);
ExecutorService executor = Executors.newFixedThreadPool(2); // 创建线程池并返回ExecutorService实例
executor.execute(futureTask1);
while(true){
if(futureTask1.isDone()){
try {
System.out.println(futureTask1.get());
break;
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}else{
System.out.println("等待中。。。");
}
}
}
-
总结对比
- 使用继承的方式来开发多线程应用程序在设计上存在局限性。因为Java不支持多继承。而为了改变这一限制我们可以采用实现Runnable的方式。
- Callable与其他两种的区别在于当线程执行完毕后可以接受返回值。
线程的启动
- start 方法
-
继承Thread类的启动方法
public class Run { public static void main(String[] args) { MyThread thread = new MyThread(); thread.start(); Thread thread3 = new Thread(new MyThread()); thread3.start(); } }
-
实现Runnable接口启动方式
class Run { public static void main(String[] args) { Thread thread = new Thread(new MyRunnableThread()); thread.start(); } }
- run 方法
public class Run { public static void main(String[] args) { MyThread thread = new MyThread(); thread.run(); } }
- 总结
-
start方法与run方法对比
- 线程调用start方法通知“线程规划器”当前线程已经做好准备,此过程其实就是通知系统安排一个时间来执行线程的run方法,此过程是异步的。
- 直接调用run方法则是同步的,与我们平常调用一个类的方法没有任何区别。
-
启动方式
- Thread类的构造方法中可以传递Thread对象,这意味着可以将一个线程的run方法交给另一个线程去执行
- Thread类构造能支持传递Runnable接口,这避免的java单继承的局限性
优雅的停止线程
-
使用中断标志使线程停止
- 下文中interrupt停止方法就是一种按照线程标志位停止线程的方式。另外也可以使用自己设置的标志位来停止线程此处不再赘述。
-
使用stop强行终止线程(方法已过时不推荐)
- stop停止线程是调用stop方法来暴力停止线程,这个方法已经过时不推荐使用,且在调用stop方法是会释放锁造成数据不一致的结果。
-
使用interrupt方法终止线程
-
判断线程是否终止状态
-
interrupted 与 isInterrupted
- interrupted 检测当前线程是否中断 具有清除状态的功能。
- isInterrupted 检测线程是否中断
-
举例说明:
-
-
```java
public class MyThread extends Thread {
@Override
public void run() {
super.run();
while(true){
}
}
}
class Run {
public static void main(String[] args) {
MyThread thread = new MyThread();
thread.start();
thread.interrupt();
System.out.println(thread.interrupted());
System.out.println(thread.isInterrupted());
Thread.currentThread().interrupt();
System.out.println(
Thread.currentThread().isInterrupted()
);
System.out.println(
Thread.currentThread().interrupted()
);
System.out.println(
Thread.currentThread().interrupted()
);
}
```
* 前面两个输出分别为false,true,因为interrupted检测当前线程是否中断,而当前线程为main方法的主线程并且并未中断所以为false,而isInterrupted则检测线程是否中断,可以看出线程调用了interrupt方法确实是使其中断标志位为true,细心的可以发现如果在while循环里面加上打印输出,interrupt方法并没有真正的中断线程,仅仅的修改了标志位。
* 后面三个输出分别为true true false ,最后一个输出为false是因为interrupted调用后重置了标志位。
-
interrupt方式中断线程
-
例子01:
```java public class MyThread extends Thread { @Override public void run() { super.run(); for (int i = 0; i < 500000; i++) { System.out.println("运行中"); if(this.isInterrupted()){ break; } } System.out.println("结束运行"); } } class Run { public static void main(String[] args) { MyThread thread = new MyThread(); thread.start(); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } thread.interrupt(); } } ```
-
异常法中断线程
- 例子02:
public class MyThread extends Thread { @Override public void run() { super.run(); try { for (int i = 0; i < 500000; i++) { System.out.println("运行中"); if (this.isInterrupted()) { throw new InterruptedException(); } } System.out.println("end"); } catch (InterruptedException e) { System.out.println("进入catch"); e.printStackTrace(); } } } class Run { public static void main(String[] args) { MyThread thread = new MyThread(); thread.start(); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } thread.interrupt(); } }
小结:
- 例子01的方式不能很好的关闭线程,如果在break后循环终止,但是循环后面的代码将会继续执行,这样并没有起到停止线程的目的
- 例子02在原有的基础上进行改进利用异常法中断线程。只要try方法块中抛出异常,则线程就会终止。
-
-
使用return停止线程
- 此处可以使用上面interrupt例子01的方式,将break语句改为return
-
在休眠中停止线程
- 休眠之前停止线程
```java
public class MyThread extends Thread {
@Override
public void run() {
super.run();
try {
for (int i = 0; i < 500000; i++) {
System.out.println("运行中");
}
Thread.sleep(1000);
System.out.println("end");
} catch (InterruptedException e) {
System.out.println("进入catch" + this.isInterrupted());
e.printStackTrace();
}
}
}
class Run {
public static void main(String[] args) {
try {
MyThread thread = new MyThread();
thread.start();
Thread.sleep(500);
thread.interrupt();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
```
-
休眠之后停止线程
public class MyThread extends Thread { @Override public void run() { super.run(); try { Thread.sleep(3000); for (int i = 0; i < 500000; i++) { System.out.println("运行中"); } System.out.println("end"); } catch (InterruptedException e) { System.out.println("进入catch" + this.isInterrupted()); e.printStackTrace(); } } } class Run { public static void main(String[] args) { try { MyThread thread = new MyThread(); thread.start(); Thread.sleep(500); thread.interrupt(); } catch (InterruptedException e) { e.printStackTrace(); }
-
小结:
通过打印输出可以发现这两种方式在休眠前与休眠后中断线程的结果是不一样的,
- 休眠前后中断线程线程的isInterrupt方法输出为false即在休眠时或者碰到休眠而终止线程会重置线程的状态。区别在于,休眠前中断是先标记为中断状态,然后碰到sleep方法时终止。休眠后中断则会立马出发终止并且重置状态
-
网友评论