深入理解Java并发编程(二):线程

本文围绕Java线程展开,介绍了创建线程的三种方式,包括继承Thread类、实现Runable和Callable接口,对比了它们的区别。阐述了线程的生命周期,涵盖新建、就绪等5种状态。还介绍了Thread方法、线程优先级设置,以及守护线程的特点和使用注意事项。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

创建线程的方式

  • 继承 Thread 类,重写 run 方法
  • 实现 Runable 接口,重写 run 方法
  • 实现 Callable 接口,重写 call 方法
package com.trs.node;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

/**
 * 创建线程的方式
 * 1、继承Thread,重写run方法
 * 2、实现Runable,重写run方法
 * 3、实现Callable,重写call方法
 * 
 * @author Admin
 *
 */
public class CreateThreadModes {

    public static void main(String[] args) throws InterruptedException, ExecutionException, TimeoutException {
        Thread1 thread1 = new Thread1();
        thread1.start();

        Thread thread2 = new Thread(new Thread2());
        thread2.start();

        FutureTask<Integer> future = new FutureTask<>(new Thread3());
        Thread thread3 = new Thread(future);
        thread3.start();
        // 获取返回值
        Integer result1 = future.get();
        // 获取返回值,等待超时抛出异常
        Integer result2 = future.get(1000, TimeUnit.MILLISECONDS);
        // 取消任务,未开始或已完成返回false,参数表示是否中断执行中的线程
        // 等待状态,此时调用cancel()方法不管传入true还是false都会标记为取消,任务依然保存在任务队列中,但当轮到此任务运行时会直接跳过
        // 完成状态,此时cancel()不会起任何作用,因为任务已经完成了
        // 运行中,此时传入true会中断正在执行的任务,传入false则不会中断
        boolean cancelFlag = future.cancel(false);
        // 执行结束(完成/取消/异常)返回true
        boolean isDoneFlag = future.isDone();
        // 任务完成前被取消返回true
        boolean isCanceledFlag = future.isCancelled();
    }

}

/**
 * 继承Thread,重写run方法
 * 
 * @author Admin
 *
 */
class Thread1 extends Thread {
    @Override
    public void run() {
        System.out.println("I'm extends Thread!");
    }
}

/**
 * 实现Runable接口,重写run方法
 * 
 * @author Admin
 *
 */
class Thread2 implements Runnable {
    @Override
    public void run() {
        System.out.println("I'm implements Runable!");
    }
}

/**
 * 实现Callable接口,重写call方法
 * 
 * @author Admin
 *
 */
class Thread3 implements Callable<Integer> {
    @Override
    public Integer call() throws Exception {
        System.out.println("I'm implements Callable! and I have result! and I can throw exception!");
        return 1;
    }
}

上述三种创建线程的方式中,两种实现接口,一种继承类,它们最大的区别在于 Java 的单继承,以实现接口形式创建线程还可以继承其他的类。

而实现 Runable 和实现 Callable 的区别,首先是重写方法的签名不同,实现 Runable 接口重写 run() 方法,实现 Callable 接口重写 call() 方法;其次 call() 方法有返回值,且能抛出异常,run() 方法没有,且不能抛出异常;通过 Future 对象可以了解任务执行情况,可取消任务的执行,还可获取执行结果。

run() 方法和 start() 方法:调用 run() 方法只是调用一个普通方法,具备同步执行的效果;调用 start() 方法,才会真正的启动一个线程,具备异步执行的效果。

线程的生命周期

线程的生命周期分为:新建(New)、就绪(Runnable)、运行(Running)、阻塞(Blocked)和死亡(Dead)5种状态。

新建(NEW):

当创建 Thread 类的一个实例(对象)时,此线程进入新建状态(未被启动)。

就绪(Runnable):

线程已经被启动,正在等待被分配给 CPU 时间片,也就是说此时线程正在就绪队列中排队等候得到 CPU 资源。

运行(Running):

线程获得 CPU 资源正在执行任务(run()方法),此时除非此线程自动放弃 CPU 资源或者有优先级更高的线程进入,线程将一直运行到结束。

阻塞(Blocked):

由于某种原因导致正在运行的线程让出 CPU 并暂停自己的执行,即进入堵塞状态。

同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则 JVM 会把该线程放入锁池(lock pool)中。

等待阻塞:运行的线程执行 wait() 方法,JVM 会把该线程放入等待队列(waitting queue)中。

其他阻塞:运行的线程执行 sleep(long ms) 或 join() 方法,或者发出了 I/O 请求时,JVM 会把该线程置为阻塞状态。当 sleep() 状态超时、join() 等待线程终止或者超时、或者 I/O 处理完毕时,线程重新转入可运行状态。

线程从阻塞状态只能进入就绪状态,无法进入运行状态。 

死亡(Dead):

线程 run()、main() 方法执行结束,或者因异常退出了 run() 方法,则该线程结束生命周期,死亡的线程不可再次复生。

run() 或 call() 方法执行完成,线程正常结束。 

线程抛出一个未捕获的 Exception 或 Error。 

直接调用该线程的 stop() 方法来结束该线程——该方法容易导致死锁,通常不推荐使用。 

isAlive() 方法测试某个线程是否已经死亡。

Thread方法

Thread类对象调用的方法

序号方法描述
1public void start()
使该线程开始执行,Java 虚拟机调用该线程的 run 方法。
2public void run()
如果该线程是使用独立的 Runnable 运行对象构造的,则调用该 Runnable 对象的 run 方法;否则,该方法不执行任何操作并返回。
3public final void setName(String name)
改变线程名称,使之与参数 name 相同。
4public final void setPriority(int priority)
更改线程的优先级。
5public final void setDaemon(boolean on)
将该线程标记为守护线程或用户线程。
6public final void join(long millisec)
等待该线程终止的时间最长为 millis 毫秒。
7public void interrupt()
中断线程。
8public final boolean isAlive()
测试线程是否处于活动状态。

Thread类的静态方法

序号方法描述
1public static void yield()
暂停当前正在执行的线程对象,并执行其他线程。
2public static void sleep(long millisec)
在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。
3public static boolean holdsLock(Object x)
当且仅当当前线程在指定的对象上保持监视器锁时,才返回 true。
4public static Thread currentThread()
返回对当前正在执行的线程对象的引用。
5public static void dumpStack()
将当前线程的堆栈跟踪打印至标准错误流。

线程优先级

线程的调度工作是不可控的,线程只能被动的被分配时间片,我们只能通过提高线程的优先级来改善线程获取时间片的几率。

优先级可以用整数表示,取值范围为 0~10,0 为最低优先级,10 为最高优先级。Thread 类有三个优先级静态常量MAX_PRIORITY 为 10,为线程最高优先级;MIN_PRIORITY 取值为 1,为线程最低优先级;NORM_PRIORITY 取值为 5,为线程中间位置的优先级。默认情况下,线程的优先级为 NORM_PRIORITY。

Java 中的线程在同等情况下,对于两个同时启动的线程,优先级高的线程先获取 CPU 资源,先被真正运行,优先级低的线程后获取 CPU 资源,后被执行。特殊情况在于现在计算机都是多核多线程的配置,有可能优先级低的线程比优先级高的线程先运行,线程的执行先后还是由 Java 虚拟机调度决定的。

可以使用 setPriority(int P) 方法设置线程的优先级。

守护线程

在 Java 中有两类线程:User Thread(用户线程)、Daemon Thread(守护线程)。

所谓守护线程是指在程序运行的时候在后台提供一种通用服务的线程,比如垃圾回收线程就是一个很称职的守护者,并且这种线程并不属于程序中不可或缺的部分。因此,当所有的非守护线程结束时,程序也就终止了,同时会杀死进程中的所有守护线程。反过来说,只要任何非守护线程还在运行,程序就不会终止。

守护线程和用户线程没啥本质的区别,唯一的不同之处就在于虚拟机的离开,如果用户线程已经全部退出运行了,只剩下守护线程存在了,虚拟机也就退出了, 因为没有了被守护者,守护线程也就没有工作可做了,也就没有继续运行程序的必要了。

将线程转换为守护线程可以通过调用 Thread 对象的 setDaemon(true) 方法来实现。在使用守护线程时需要注意一下几点:

setDaemon(true) 必须在 thread.start() 之前设置,否则会抛出一个 IllegalThreadStateException 异常。

你不能把正在运行的常规线程设置为守护线程。

在Daemon线程中产生的新线程也是 Daemon 的。

守护线程应该永远不去访问固有资源,如文件、数据库,因为它会在任何时候甚至在一个操作的中间发生中断。

在 Java 中 java.lang.Thread.isDaemon() 方法用来测试线程是否为守护线程,如果结果返回 True 该线程就是守护线程。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值