前言
Java多线程实现方式主要有四种:
① 继承Thread类、实现Runnable接口
② 实现Callable接口通过FutureTask包装器来创建Thread线程
③ 使用ExecutorService、Callable
④ Future实现有返回结果的多线程
其中前两种方式线程执行完后都没有返回值,后两种是带返回值的。
一、四种方式实现多线程
1.继承Thread类创建线程
Thread类本质上是一个实现了Runnable接口的实例,表示一个线程的实例。使用Thread类的start()实例方法是启动线程的唯一途径。执行run()方法的新线程是通过调用start()方法来启动的,而start()方法是一个原生方法。这种方式实现多线程很简单,通过自己的类直接extend Thread,并复写run()方法,就可以启动新线程并执行自己定义的run()方法。例如:
public class MyThread extends Thread { public void run() { System.out.println("MyThread.run()"); } } MyThread myThread1 = new MyThread(); MyThread myThread2 = new MyThread(); myThread1.start(); myThread2.start();
2.实现Runnable接口创建线程
如果自己的类已经extends另一个类,就无法直接extends Thread,此时,可以实现一个Runnable接口,如下:
public class MyThread extends OtherClass implements Runnable { public void run() { System.out.println("MyThread.run()"); } }
为了启动MyThread,需要首先实例化一个Thread,并传入自己的MyThread实例:
MyThread myThread = new MyThread(); Thread thread = new Thread(myThread); thread.start();
事实上,当传入一个Runnable target参数给Thread后,Thread的run()方法就会调用target.run(),参考JDK源代码:
public void run() { if (target != null) { target.run(); } }
3.实现Callable接口
通过FutureTask包装器来创建Thread线程
Callable接口(也只有一个方法)定义如下:
public interface Callable<V> { V call() throws Exception; } public class SomeCallable<V> extends OtherClass implements Callable<V> { @Override public V call() throws Exception { // TODO Auto-generated method stub return null; } }
Callable<V> oneCallable = new SomeCallable<V>(); //由Callable<Integer>创建一个FutureTask<Integer>对象: FutureTask<V> oneTask = new FutureTask<V>(oneCallable); //注释:FutureTask<Integer>是一个包装器,它通过接受Callable<Integer>来创建,它同时实现了Future和Runnable接口。 //由FutureTask<Integer>创建一个Thread对象: Thread oneThread = new Thread(oneTask); oneThread.start(); //至此,一个线程就创建完成了。
4.实现有返回结果的线程
使用ExecutorService、Callable、Future实现有返回结果的线程
ExecutorService、Callable、Future三个接口实际上都是属于Executor框架。在JDK1.5中,引入了返回结果的线程作为新特性,不再需要费尽周折来获取返回值。而且自己实现了也可能漏洞百出。
可返回值的任务必须实现Callable接口。类似的,无返回值的任务必须实现Runnable接口。
执行Callable任务后,可以获取一个Future的对象,在该对象上调用get就可以获取到Callable任务返回的Object了。
注意:get方法是阻塞的,即:线程无返回结果,get方法会一直等待。
再结合线程池接口ExecutorService就可以实现传说中有返回结果的多线程了。
在JDK1.5下进行了验证并且没有问题,可以直接使用下面提供的有返回结果的多线程测试示例。代码如下:
import java.util.concurrent.*; import java.util.Date; import java.util.List; import java.util.ArrayList; /** * 有返回值的线程 */ @SuppressWarnings("unchecked") public class Test { public static void main(String[] args) throws ExecutionException, InterruptedException { System.out.println("----程序开始运行----"); Date date1 = new Date(); int taskSize = 5; // 创建一个线程池 ExecutorService pool = Executors.newFixedThreadPool(taskSize); // 创建多个有返回值的任务 List<Future> list = new ArrayList<Future>(); for (int i = 0; i < taskSize; i++) { Callable c = new MyCallable(i + " "); // 执行任务并获取Future对象 Future f = pool.submit(c); // System.out.println(">>>" + f.get().toString()); list.add(f); } // 关闭线程池 pool.shutdown(); // 获取所有并发任务的运行结果 for (Future f : list) { // 从Future对象上获取任务的返回值,并输出到控制台 System.out.println(">>>" + f.get().toString()); } Date date2 = new Date(); System.out.println("----程序结束运行----,程序运行时间【" + (date2.getTime() - date1.getTime()) + "毫秒】"); } } class MyCallable implements Callable<Object> { private String taskNum; MyCallable(String taskNum) { this.taskNum = taskNum; } public Object call() throws Exception { System.out.println(">>>" + taskNum + "任务启动"); Date dateTmp1 = new Date(); Thread.sleep(1000); Date dateTmp2 = new Date(); long time = dateTmp2.getTime() - dateTmp1.getTime(); System.out.println(">>>" + taskNum + "任务终止"); return taskNum + "任务返回运行结果,当前任务时间【" + time + "毫秒】"; } }
二、多线程相关知识
1.Runnable 和 Callable 的区别
主要区别 Runnable 接口 run 方法无返回值;
Callable 接口 call 方法有返回值,支持泛型 Runnable 接口 run 方法只能抛出运行时异常,且无法捕获处理;
Callable 接口 call 方 法允许抛出异常,可以获取异常信息
2.如何启动一个新线程、调用 start 和 run 方法的区别
线程对象调用 run 方法不开启线程。仅是对象调用方法。
线程对象调用 start 开启线程,并让 jvm 调用 run 方法在开启的线程中执行调用 start 方法可以启动线程,并且使得线程进入就绪状态,而 run 方法只是 thread 的一 个普通方法,还是在主线程中执行。
3.线程相关的基本方法
线程相关的基本方法有 wait,notify,notifyAll,sleep,join,yield 等
线程等待(wait) 调用该方法的线程进入 waiting状态,只有等待另外线程的通知或被中断才会返回,需要注意的是调用 wait()方法后,会释放对象 的锁。因此,wait 方 法一般用在同步方法或同步代码块中。
线程睡眠(sleep) sleep 导致当前线程休眠,与 wait 方法不同的是 sleep 不会释放当前占 有的锁,sleep(long)会导致线程进入 TIMED-WATING 状态,而 wait()方法 会导致当前线程进入 WATING 状态.
线程让步(yield) yield 会使当前线程让出 CPU 执行时间片,与其他线程一起重新竞争 CPU 时间片。一般情况下,优先级高的线程有更大的可能性成功竞争得到 CPU 时间片,但这又不是绝对的,有的操作系统对 线程优先级并不敏感。
线程中断(interrupt) 中断一个线程,其本意是给这个线程一个通知信号,会影响这个线程内部的 一个中断标识位。这个线程本身并不会因此而改变状态(如阻塞,终止等)
Join 等待其他线程终止 join() 方法,等待其他线程终止,在当前线程中调用一个线程的 join() 方 法,则当前线程转为阻塞状态,回到另一个线程结束,当前线程再由阻塞状态变 为就绪状态,等待 cpu 的宠幸.
线程唤醒(notify) Object 类中的 notify() 方法,唤醒在此对象监视器上等待的单个线程,如 果所有线程都在此对象上等待,则会选择唤醒其中一个线 程,选择是任意的,并在对实现做出决定时发生,线程通过调用其中一个 wait() 方法,在对象的监视 器上等待,直到当前的线程放弃此对象上的锁 定,才能继续执行被唤醒的线程, 被唤醒的线程将以常规方式与在该对象上主动同步的其他所有线程进行竞争。另一个类似的方法是 notifyAll(),它可以唤醒在同一监视器上等待的所有线程。
4.wait()和 sleep()的区别
① 来自不同的类 wait():来自 Object 类; sleep():来自 Thread 类;
② 关于锁的释放: wait():在等待的过程中会释放锁; sleep():在等待的过程中不会释放锁
③ 使用的范围: wait():必须在同步代码块中使用; sleep():可以在任何地方使用;
④ 是否需要捕获异常 wait():不需要捕获异常; sleep():需要捕获异常;
5.多线程原理
多线程原理:多线程是通过并发的方式进行。对于一个CPU它在某个时间点上,只能执行一个程序,即同一时间只能运行一个进程,CPU会不断地在这些进程之间切换,每个线程执行一个时间。因为CPU的执行速度相对我们的感觉实在太快了,虽然CPU在多个进程之间轮换执行,但我们自己感到好像多个进程在同时执行。
CPU会在多个进程之间做着切换,如果我们开启的程序过多,CPU切换到每一个进程的时间也会变长,我们也会感觉机器运行变慢。虽然合理地使用多线程可以提高效率,但是过度使用并不能带来效率上的提高。
多线程技术主要解决处理器单元内多个线程执行的问题,它可以显著减少处理器单元的闲置时间,增加处理器单元的吞吐能力。
以上是Java实现多线程的方法有哪些的详细内容。更多信息请关注PHP中文网其他相关文章!

JVM通过字节码解释、平台无关的API和动态类加载实现Java的WORA特性:1.字节码被解释为机器码,确保跨平台运行;2.标准API抽象操作系统差异;3.类在运行时动态加载,保证一致性。

Java的最新版本通过JVM优化、标准库改进和第三方库支持有效解决平台特定问题。1)JVM优化,如Java11的ZGC提升了垃圾回收性能。2)标准库改进,如Java9的模块系统减少平台相关问题。3)第三方库提供平台优化版本,如OpenCV。

JVM的字节码验证过程包括四个关键步骤:1)检查类文件格式是否符合规范,2)验证字节码指令的有效性和正确性,3)进行数据流分析确保类型安全,4)平衡验证的彻底性与性能。通过这些步骤,JVM确保只有安全、正确的字节码被执行,从而保护程序的完整性和安全性。

Java'splatFormIndepentEncealLowsApplicationStorunonAnyOperatingsystemwithajvm.1)singleCodeBase:writeandeandcompileonceforallplatforms.2)easileupdates:updatebybytecodeforsimultaneDeployment.3)testOnOneOnePlatForforuluniverSalpeforuluniverSaliver.4444.4444

Java的平台独立性通过JVM、JIT编译、标准化、泛型、lambda表达式和ProjectPanama等技术不断增强。自1990年代以来,Java从基本的JVM演进到高性能的现代JVM,确保了代码在不同平台的一致性和高效性。

Java如何缓解平台特定的问题?Java通过JVM和标准库来实现平台无关性。1)使用字节码和JVM抽象操作系统差异;2)标准库提供跨平台API,如Paths类处理文件路径,Charset类处理字符编码;3)实际项目中使用配置文件和多平台测试来优化和调试。

java'splatformentenceenhancesenhancesmicroservicesharchitecture byferingDeploymentFlexible,一致性,可伸缩性和便携性。1)DeploymentFlexibilityAllowsibilityAllowsOllowsOllowSorlowsOllowsOllowsOllowSeStorunonAnyPlatformwithajvM.2)penterencyCrossServAccAcrossServAcrossServiCessImplifififiesDeevelopmentandeDe

GraalVM通过三种方式增强了Java的平台独立性:1.跨语言互操作,允许Java与其他语言无缝互操作;2.独立的运行时环境,通过GraalVMNativeImage将Java程序编译成本地可执行文件;3.性能优化,Graal编译器生成高效的机器码,提升Java程序的性能和一致性。


热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

Video Face Swap
使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热门文章

热工具

SecLists
SecLists是最终安全测试人员的伙伴。它是一个包含各种类型列表的集合,这些列表在安全评估过程中经常使用,都在一个地方。SecLists通过方便地提供安全测试人员可能需要的所有列表,帮助提高安全测试的效率和生产力。列表类型包括用户名、密码、URL、模糊测试有效载荷、敏感数据模式、Web shell等等。测试人员只需将此存储库拉到新的测试机上,他就可以访问到所需的每种类型的列表。

SublimeText3 英文版
推荐:为Win版本,支持代码提示!

禅工作室 13.0.1
功能强大的PHP集成开发环境

SublimeText3 Mac版
神级代码编辑软件(SublimeText3)

记事本++7.3.1
好用且免费的代码编辑器