首页 > 编程语言 >Java Thread

Java Thread

时间:2024-05-25 16:45:59浏览次数:26  
标签:Runnable Java Thread void 任务 线程 public

Thread

一般而言,线程是CPU资源调度的基本单位。在java中,线程通过系统内核线程实现,每个Java线程对应着一个内核线程。

以HotSpot JVM为例,它的每一个Java线程都是直接映射到一个操作系统原生线程来实现的,中间没有额外的结构,HotSpot不会干涉线程调度。线程调度全由操作系统去处理,包括何时冻结或唤醒线程、该给线程分配多少处理器执行时间、该把线程安排给哪个处理器核心去执行等。

新建/运行

创建一个可执行任务,然后再创建一个线程,创建线程时需要绑定一个可执行任务。调用thread.start()方法后,线程就会执行这个任务。

Runnable runnable = new Runnable() {
    @Override
    public void run() {
        System.out.println("hello");
    }
};

// 创建一个Thread执行runnable任务
Thread thread = new Thread(runnable);
thread.start();

线程状态

在java.lang.Thread类的内部类State中,定义了线程所拥有的六种状态。

public enum State {
    NEW,
    RUNNABLE,
    BLOCKED,
    WAITING,
    TIMED_WAITING,
    TERMINATED;
}

状态含义

  • NEW:新建状态,一个线程创建后,但还未调用start()方法时的状态
  • RUNNABLE:就绪状态,调用start()方法后的状态,可能在执行状态,也可能在等待CPU时间,对应操作系统中的Running和Ready
  • WAITING:无限期等待状态,不会被分配CPU时间,需要等待被其它线程显式唤醒,转换为RUNNABLE。例如,在没有设置timeout调用Object.wait()、Thread.join()、LockSupport.park()
  • TIMED_WAITING:有限期等待状态,不会被分配CPU时间,但一定时间后会被自动唤醒,转换为RUNNABLE。例如,在设置timeout之后调用Object.wait()、Thread.join()、LockSupport.parkUntil()
  • BLOCKED:阻塞状态,不会被分配CPU时间,在资源可获取时被唤醒,转换为RUNNABLE。例如,在竞争锁对象时,若获取失败,则阻塞等待,在可获得时转换为RUNNABLE;在发出IO请求后,阻塞等待,在IO处理完成后,转换为RUNNABLE。
  • TERMINATED:终止状态,线程中的任务执行完成之后处于此状态。

状态转换

找了一张状态流程图

常用方法

动态方法

序号 方法描述
1 public void start() 启动线程,由New状态转换为Runnable状态
2 public final void setName(String name) 改变线程名称,使之与参数 name 相同
3 public final void join() 等待线程执行完成,转换为TERMINATED状态,可指定等待时间
4 public void interrupt() 中断线程执行

静态方法

序号 方法描述
1 public static void yield() 当前线程让出CPU时间,由Running转换为Ready状态
2 public static void sleep(long millisec) 在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响

需要注意的是,不管是yield还是sleep,都不会释放当前线程持有的锁(如果持有的话)。

任务

线程是用于执行任务的,那么,如何定义一个任务,又如何执行一个任务?

定义任务

java中主要有两种方式,其余均为相应扩展。

Runnbale

Runnable是一个接口,表示一个可运行的任务。要创建一个可执行任务,实现Runnable接口,重写run()方法即可。

@FunctionalInterface
public interface Runnable {

public abstract void run();
}

Callable

Callable是一个接口,表示一个有返回结果的任务,但任务不可直接运行。实现Callable接口,重写call方法,即可创建一个Callable任务。

@FunctionalInterface
public interface Callable<V> {

V call() throws Exception;
}

执行任务

任务的执行有多种方式,但本质上都是通过一个子线程来执行的。

executor

  • 对于Runnable任务,调用executor.execute()方法执行即可。
// 定义一个单线程任务处理器
ThreadPoolExecutor executor = Executors.newSingleThreadExecutor();

// 执行runnable任务
Runnable runnable = new Runnable() {
    @Override
    public void run() {
        System.out.println("hello");
    }
};

executor.execute(runnable);
  • 对于Callable任务,调用executor.submit()方法提交,无法直接执行。executor将其转换为一个FutureTask后再执行任务。
Callable callable = new Callable() {
    @Override
    public Object call() throws Exception {
        return "hello";
    }
};
Future future = executor.submit(callable);
Objects result = future.get();

thread

Runnable runnable = new Runnable() {
    @Override
    public void run() {
        System.out.println("hello");
    }
};

// 创建一个Thread执行runnable任务
Thread thread = new Thread(runnable);
thread.start();

处理返回结果

在提交任务后,如果任务有返回值,应该如何获取返回值呢?例如,向executor中提交一个callable任务后,获取返回值。

Future

这就不得不说到Future了,在executor.submit(Callable callable)执行之后,返回的就是一个Future对象。通过Future对象你可以获取任务的执行状态和执行结果。

Future:表示异步计算结果的接口,提供了一系列方法来管理异步任务的执行状态和结果。

public interface Future<V> {

    boolean isDone();

    V get() throws InterruptedException, ExecutionException;

V get(long timeout, TimeUnit unit)
        throws InterruptedException, ExecutionException, TimeoutException;
}

executor.submit(Callable callable)返回的Future对象其实是一个RunnableFuture,最终实现是一个FutureTask。

RunnableFuture:继承自 Runnable 和 Future 接口的接口,继承了两个的特性,表示一个既可运行,又有返回结果的任务

public interface RunnableFRecursiveuture<V> extends Runnable, Future<V> {
    /**
     * Sets this Future to the result of its computation
     * unless it has been cancelled.
     */
void run();
}

FutureTask: FutureTask类是RunnableFuture接口的实现类,由一个runnable对象或者callable对象创建。

public class FutureTask<V> implements RunnableFuture<V> {

    // 忽略其余方法和变量
    
    public FutureTask(Callable<V> callable) {
        if (callable == null)
            throw new NullPointerException();
        this.callable = callable;
        this.state = NEW;       // ensure visibility of callable
    }
}

处理异常

子线程中执行的任务,如果在执行过程中,抛出运行时异常,应该怎么处理?

任务中捕获

在任务代码中捕获Exception,然后做一些日志记录的操作。但这种方式侵入性较大,也不优雅。

Runnable runnable = new Runnable() {
    @Override
    public void run() {
        try{
            System.out.println("hello");
        } catch (Exception ex) {
            // 处理异常,例如记录日志或其他操作
}
    }
};

线程中捕获

为Thread设置ExceptionHandler,针对任务抛出的异常进行捕获,然后做一些基础的日志记录操作。这种方式无侵入性,简单易用,但是不灵活,无法为不同的任务设置不同的异常处理策略。

Thread thread = new Thread(runnable);
thread.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
    public void uncaughtException(Thread t, Throwable e) {
        // 处理异常,例如记录日志或其他操作
System.out.println("Thread " + t.getName() + " threw an exception: " + e);
    }
});

针对ThreadPoolExecutor,ExceptionHandler在自定义ThreadFactory时设置。

new ThreadFactory() {
        public Thread newThread(Runnable r) {
            Thread t = new Thread(r);
            t.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
                public void uncaughtException(Thread t, Throwable e) {
                    // 处理异常,例如记录日志或其他操作
                    System.out.println("Thread " + t.getName() + " threw an exception: " + e);
                }
            });
            return t;
        }
    }

使用Future

前面两种方式都有自己的缺陷,而使用Future的方式,正好可以结合它们的优点又规避缺陷。使用JDK 8中提供的CompletableFuture类,可以以更加灵活的方式对执行过程中抛出的异常进行处理,示例如下

  • 无返回值
Runnable runnable = new Runnable() {
    @Override
    public void run() {
        System.out.println("hello");
    }
};

CompletableFuture.runAsync(runnable, executor).whenComplete((useless, throwable) -> {
    if (Objects.nonNull(throwable)) {
        // 日志记录
        log.error(throwable.getMessage(), throwable);
        // 设置一些特殊逻辑
}
});
  • 有返回值

Supplier是一个函数式接口,可提供返回结果,在CompletableFuture中,Supplier实现会被转换为一个AsyncSupply,其实现了Runnable、Future接口,所以可以执行,也可以获取返回结果。

Supplier<String> supplier = () -> "hello"; 
CompletableFuture.supplyAsync(supplier, executor).whenComplete((result, throwable) -> {
    if (Objects.nonNull(throwable)) {
        // 日志记录
        log.error(throwable.getMessage(), throwable);
        return;
    }
    log.info("execute result : {}", result);
});

标签:Runnable,Java,Thread,void,任务,线程,public
From: https://www.cnblogs.com/cd-along/p/18211078

相关文章

  • Java 多线程编程 力扣实题
    多线程编程实例了解内存模型、线程通信和线程安全之后,对多线程编程已经有了理论上的认知,现在来实战一下。所有题目在https://leetcode.cn/problemset/concurrency/。按序打印题干描述给你一个类:publicclassFoo{publicvoidfirst(){print("first");}publicvoidseco......
  • Java 多线程编程基础
    我们的应用程序都是运行在多线程的环境下的,在多线程环境下的许多问题我们都了解吗?线程间如何进行数据交换?线程间如何进行通信与协作?共享一个资源时如何保证线程安全?线程数据交换线程之间无法直接访问对方工作内存中的变量,必须通过主内存进行变量的传递。例如,线程A、B共享一......
  • Java ThreadPoolExecutor
    ThreadPoolExecutor?ThreadPoolExecutor是什么,先拆开来看,ThreadPoolAndExecutor?那ThreadPool是什么?Executor又是什么?Executor:任务执行者,只定义了一个execute方法,接收一个Runable参数。publicinterfaceExecutor{voidexecute(Runnablecommand);}ThreadPool:可以缓存......
  • Java实现图书系统
    首先实现一个图书管理系统,我们要知道有哪些元素?1.用户分成为管理员和普通用户2.书:书架  书3.操作的是:书架目录第一步:建包第二步:搭建框架首先:完成book中的方法其次:完成BookList然后:完成管理员界面和普通用户界面最后:Main第三步:细分方法1.退出系统2......
  • 从零手写实现 nginx-01-为什么不能有 java 版本的 nginx?
    前言大家好,我是老马。很高兴遇到你。作为一个java开发者,工作中一直在使用nginx。却发现一直停留在使用层面,无法深入理解。有一天我在想,为什么不能有一个java版本的nginx呢?一者是理解nginx的设计灵魂,再者java开发者用java语言的服务器不是更加自然吗。于是动手开......
  • arthas:Java调试利器,线上Debug不是梦
    目录前言一、Arthas是什么?二、Arthas能解决啥问题?三、Arthas两种安装、启动方式1、jar包启动2、在线安装3、远程连接:四、Arthas命令使用1、Dashboard命令2、Thread(线程监控)3、JVM(jvm实时运行状态,内存使用情况等)4、trace(当前方法内部调用路径,路径上每个节......
  • 「终极收藏」前端开发必备:超全JavaScript公共方法大全
    目录引言1安装js-tool-big-box工具包1.1安装1.2截至目前的方法集合 2时间日期类 2.1更灵活的年月日时分秒2.2 日期时间转换2.3个性的时间组合 2.4 某个时间距离现在2.5 平年还是闰年2.6指定月份的天数 2.7属相2.8获取指定年份的法定节假日 3......
  • Java根据URL下载文件到本地的2种方式(大型文件与小型文件)
    1.小型文件推荐使用2.大型文件推荐使用总结 各位小伙伴是否有使用java,根据url下载文件到本地的需求,以下介绍两种方式1.小型文件推荐使用代码解析首先创建了一个URL对象website,用来表示远程文件的地址。然后创建了一个ReadableByteChannel对象rbc和一个FileOutputStr......
  • Java 登录错误次数限制,用户禁登1小时
    手机号验证码登录,验证码输入错误次数超5次封禁@OverridepublicbooleancheckCaptcha(StringphoneNum,Stringcaptcha){StringcodeNum=(String)redisTemplate.opsForValue().get(UserCacheNames.USER_CAPTCHA+phoneNum);if(codeNum==......
  • JAVA计算机毕业设计基于SpringBoot的在线古玩市场系统的设计与实现(附源码+springboot+
    本系统(程序+源码)带文档lw万字以上 文末可获取一份本项目的java源码和数据库参考。系统程序文件列表开题报告内容研究背景随着互联网的迅猛发展和电子商务的普及,传统行业纷纷寻求数字化转型以适应市场的新需求。古玩市场作为一个历史悠久、文化底蕴深厚的行业,在数字化浪潮......