线程什么意思(线程基本用法)线程(Thread)基本方法清单: 创建线程创建线程有三种方式:继承 Thread 类实现 Runnable 接口实现 Calla
线程什么意思(线程基本用法)线程(Thread)基本方法清单:
创建线程
创建线程有三种方式:
- 继承 Thread 类
- 实现 Runnable 接口
- 实现 Callable 接口
继承 Thread 类
通过继承 Thread 类创建线程的步骤:
- 定义 Thread 类的子类,并覆写该类的 run 方法。run 方法的方法体就代表了线程要完成的任务,因此把 run 方法称为执行体。
- 创建 Thread 子类的实例,即创建了线程对象。
- 调用线程对象的 start 方法来启动该线程。
实现 Runnable 接口
实现 Runnable 接口优于继承 Thread 类,因为:
- Java 不支持多重继承,所有的类都只允许继承一个父类,但可以实现多个接口。如果继承了 Thread 类就无法继承其它类,这不利于扩展。
- 类可能只要求可执行就行,继承整个 Thread 类开销过大。
通过实现 Runnable 接口创建线程的步骤:
- 定义 Runnable 接口的实现类,并覆写该接口的 run 方法。该 run 方法的方法体同样是该线程的线程执行体。
- 创建 Runnable 实现类的实例,并以此实例作为 Thread 的 target 来创建 Thread 对象,该 Thread 对象才是真正的线程对象。
- 调用线程对象的 start 方法来启动该线程。
实现 Callable 接口
继承 Thread 类 和 实现 Callable 接口这两种创建线程的方式都没有返回值。所以,线程执行完后,无法得到执行结果。但如果期望得到执行结果该怎么做?
为了解决这个问题,Java 1.5 后,提供了 Callable 接口和 Future 接口,通过它们,可以在线程执行结束后,返回执行结果。
通过实现 Callable 接口创建线程的步骤:
- 创建 Callable 接口的实现类,并实现 call 方法。该 call 方法将作为线程执行体,并且有返回值。
- 创建 Callable 实现类的实例,使用 FutureTask 类来包装 Callable 对象,该 FutureTask 对象封装了该 Callable 对象的 call 方法的返回值。
- 使用 FutureTask 对象作为 Thread 对象的 target 创建并启动新线程。
- 调用 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有任何问题(学习方法,学习效率,如何就业)都可以问我。希望你也能凭自己的努力,成为下一个优秀的程序员