首页 安全防御正文

线程什么意思(线程基本用法)

线程什么意思(线程基本用法)线程(Thread)基本 *** 清单:

线程基本用法

 

创建线程

创建线程有三种方式:

  • 继承 Thread 类
  • 实现 Runnable 接口
  • 实现 Callable 接口

继承 Thread 类

通过继承 Thread 类创建线程的步骤:

  1. 定义 Thread 类的子类,并覆写该类的 run *** 。run *** 的 *** 体就代表了线程要完成的任务,因此把 run *** 称为执行体。
  2. 创建 Thread 子类的实例,即创建了线程对象。
  3. 调用线程对象的 start *** 来启动该线程。
线程基本用法

 

实现 Runnable 接口

实现 Runnable 接口优于继承 Thread 类,因为:

  • Java 不支持多重继承,所有的类都只允许继承一个父类,但可以实现多个接口。如果继承了 Thread 类就无法继承其它类,这不利于扩展。
  • 类可能只要求可执行就行,继承整个 Thread 类开销过大。

通过实现 Runnable 接口创建线程的步骤:

  1. 定义 Runnable 接口的实现类,并覆写该接口的 run *** 。该 run *** 的 *** 体同样是该线程的线程执行体。
  2. 创建 Runnable 实现类的实例,并以此实例作为 Thread 的 target 来创建 Thread 对象,该 Thread 对象才是真正的线程对象。
  3. 调用线程对象的 start *** 来启动该线程。
线程基本用法

 

实现 Callable 接口

继承 Thread 类 和 实现 Callable 接口这两种创建线程的方式都没有返回值。所以,线程执行完后,无法得到执行结果。但如果期望得到执行结果该怎么做?

为了解决这个问题,Java 1.5 后,提供了 Callable 接口和 Future 接口,通过它们,可以在线程执行结束后,返回执行结果。

通过实现 Callable 接口创建线程的步骤:

  1. 创建 Callable 接口的实现类,并实现 call *** 。该 call *** 将作为线程执行体,并且有返回值。
  2. 创建 Callable 实现类的实例,使用 FutureTask 类来包装 Callable 对象,该 FutureTask 对象封装了该 Callable 对象的 call *** 的返回值。
  3. 使用 FutureTask 对象作为 Thread 对象的 target 创建并启动新线程。
  4. 调用 FutureTask 对象的 get *** 来获得线程执行结束后的返回值。
线程基本用法

 

FAQ

start 和 run *** 有什么区别

  • run *** 是线程的执行体。
  • start *** 会启动线程,然后 JVM 会让这个线程去执行 run *** 。

可以直接调用 Thread 类的 run *** 么

  • 可以。但是如果直接调用 Thread 的 run *** ,它的行为就会和普通的 *** 一样。
  • 为了在新的线程中执行我们的代码,必须使用 Thread 的 start *** 。

线程休眠

使用 Thread.sleep *** 可以使得当前正在执行的线程进入休眠状态。

使用 Thread.sleep 需要向其传入一个整数值,这个值表示线程将要休眠的毫秒数。

Thread.sleep *** 可能会抛出 InterruptedException,因为异常不能跨线程传播回 main 中,因此必须在本地进行处理。线程中抛出的其它异常也同样需要在本地进行处理。

线程基本用法

 

线程礼让

Thread.yield *** 的调用声明了当前线程已经完成了生命周期中最重要的部分,可以切换给其它线程来执行 。

该 *** 只是对线程调度器的一个建议,而且也只是建议具有相同优先级的其它线程可以运行。

线程基本用法

 

终止线程

Thread 中的 stop *** 有缺陷,已废弃

使用 Thread.stop 停止线程会导致它解锁所有已锁定的监视器(由于未经检查的 ThreadDeath 异常会在堆栈中传播,这是自然的结果)。 如果先前由这些监视器保护的任何对象处于不一致状态,则损坏的对象将对其他线程可见,从而可能导致任意行为。Thread.stop 的许多用法应由仅修改某些变量以指示目标线程应停止运行的代码代替。 目标线程应定期检查此变量,如果该变量指示要停止运行,则应按有序方式从其运行 *** 返回。如果目标线程等待很长时间(例如,在条件变量上),则应使用中断 *** 来中断等待。

当一个线程运行时,另一个线程可以直接通过 interrupt *** 中断其运行状态。

线程基本用法

 


如果一个线程的 run *** 执行一个无限循环,并且没有执行 sleep 等会抛出 InterruptedException 的操作,那么调用线程的 interrupt *** 就无法使线程提前结束。

但是调用 interrupt *** 会设置线程的中断标记,此时调用 interrupted *** 会返回 true。因此可以在循环体中使用 interrupted *** 来判断线程是否处于中断状态,从而提前结束线程。

安全地终止线程有两种 *** :

  • 定义 volatile 标志位,在 run *** 中使用标志位控制线程终止
  • 使用 interrupt *** 和 Thread.interrupted *** 配合使用来控制线程终止

示例:使用 volatile 标志位控制线程终止

线程基本用法

 

示例:使用 interrupt *** 和 Thread.interrupted *** 配合使用来控制线程终止

线程基本用法

 


守护线程

什么是守护线程?

  • 守护线程(Daemon Thread)是在后台执行并且不会阻止 JVM 终止的线程。当所有非守护线程结束时,程序也就终止,同时会杀死所有守护线程。
  • 与守护线程(Daemon Thread)相反的,叫用户线程(User Thread),也就是非守护线程。

为什么需要守护线程?

  • 守护线程的优先级比较低,用于为系统中的其它对象和线程提供服务。典型的应用就是垃圾回收器。

如何使用守护线程?

  • 可以使用 isDaemon *** 判断线程是否为守护线程。
  • 可以使用 setDaemon *** 设置线程为守护线程。 正在运行的用户线程无法设置为守护线程,所以 setDaemon 必须在 thread.start *** 之前设置,否则会抛出 llegalThreadStateException 异常; 一个守护线程创建的子线程依然是守护线程。 不要认为所有的应用都可以分配给守护线程来进行服务,比如读写操作或者计算逻辑。
线程基本用法

 

FAQ

sleep、yield、join *** 有什么区别

  • yield *** yield *** 会 让线程从 Running 状态转入 Runnable 状态。 当调用了 yield *** 后,只有与当前线程相同或更高优先级的Runnable 状态线程才会获得执行的机会
  • sleep *** sleep *** 会 让线程从 Running 状态转入 Waiting 状态。 sleep *** 需要指定等待的时间,超过等待时间后,JVM 会将线程从 Waiting 状态转入 Runnable 状态。 当调用了 sleep *** 后,无论什么优先级的线程都可以得到执行机会。 sleep *** 不会释放“锁标志”,也就是说如果有 synchronized 同步块,其他线程仍然不能访问共享数据。
  • join join *** 会 让线程从 Running 状态转入 Waiting 状态。 当调用了 join *** 后,当前线程必须等待调用 join *** 的线程结束后才能继续执行

为什么 sleep 和 yield *** 是静态的

Thread 类的 sleep 和 yield *** 将处理 Running 状态的线程。

所以在其他处于非 Running 状态的线程上执行这两个 *** 是没有意义的。这就是为什么这些 *** 是静态的。它们可以在当前正在执行的线程中工作,并避免程序员错误的认为可以在其他非运行线程调用这些 *** 。

Java 线程是否按照线程优先级严格执行

即使设置了线程的优先级,也无法保证高优先级的线程一定先执行

原因在于线程优先级依赖于操作系统的支持,然而,不同的操作系统支持的线程优先级并不相同,不能很好的和 Java 中线程优先级一一对应。

本皮是一个有着5年工作经验的程序员,关于Java,自己有做材料的整合,一个完整学习Java的路线,学习材料和工具。需要的伙伴可以私信我,发送“交流”后就可免费获取。对于学习Java有任何问题(学习 *** ,学习效率,如何就业)都可以问我。希望你也能凭自己的努力,成为下一个优秀的程序员

来源:博客,欢迎分享本文!

版权声明

本文仅代表作者观点,不代表本站立场。
本文系作者授权发表,未经许可,不得转载。