多线程开发实战:Java实现多线程四种方式及相关方法原理_java多线程有几种实现方法-程序员宅基地

技术标签: jvm  java  servlet  Java全栈编程实战  开发语言  

78e52c393e19483398d4ac6a93453582.png

 个人简介

‍个人主页:陈橘又青

‍♂️博客记录心情,代码编写人生。

如果文章对你有用,麻烦关注点赞收藏走一波,感谢支持!

欢迎订阅我的专栏:

【Java进阶之路】带你入门Java,从0到1。

【Java全栈编程】带你项目实战,掌握核心。

前言

Java多线程实现方式主要有四种:

① 继承Thread类、实现Runnable接口

② 实现Callable接口通过FutureTask包装器来创建Thread线程

③ 使用ExecutorService、Callable

④ Future实现有返回结果的多线程

其中前两种方式线程执行完后都没有返回值,后两种是带返回值的。


目录

 一、四种方式实现多线程

1、继承Thread类创建线程

2、实现Runnable接口创建线程

3、实现Callable接口通过FutureTask包装器来创建Thread线程

4、使用ExecutorService、Callable、Future实现有返回结果的线程

二、多线程相关知识

1.Runnable 和 Callable 的区别?

 2. 如何启动一个新线程、调用 start 和 run 方法的区别?

3.线程相关的基本方法?

5.wait()和 sleep()的区别?

6.多线程原理


 一、四种方式实现多线程

1、继承Thread类创建线程

 Thread类本质上是实现了Runnable接口的一个实例,代表一个线程的实例。启动线程的唯一方法就是通过Thread类的start()实例方法。start()方法是一个native方法,它将启动一个新线程,并执行run()方法。这种方式实现多线程很简单,通过自己的类直接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() ,唤醒再 次监视器上等待的所有线程。

5.wait()和 sleep()的区别?

 · ① 来自不同的类 wait():来自 Object 类; sleep():来自 Thread 类;

 · ② 关于锁的释放: wait():在等待的过程中会释放锁; sleep():在等待的过程中不会释放锁

 · ③ 使用的范围: wait():必须在同步代码块中使用; sleep():可以在任何地方使用;

 · ④ 是否需要捕获异常 wait():不需要捕获异常; sleep():需要捕获异常;

6.多线程原理

    多线程原理:多线程是通过并发的方式进行。对于一个CPU它在某个时间点上,只能执行一个程序,即同一时间只能运行一个进程,CPU会不断地在这些进程之间切换,每个线程执行一个时间。因为CPU的执行速度相对我们的感觉实在太快了,虽然CPU在多个进程之间轮换执行,但我们自己感到好像多个进程在同时执行。

    CPU会在多个进程之间做着切换,如果我们开启的程序过多,CPU切换到每一个进程的时间也会变长,我们也会感觉机器运行变慢。所以合理的使用多线程可以提高效率,但是大量使用,并不能给我们带来效率上的提高。

    多线程技术主要解决处理器单元内多个线程执行的问题,它可以显著减少处理器单元的闲置时间,增加处理器单元的吞吐能力。
 

a7a4b799da0945089dd3eedffc561db9.png


 以上就是本篇博客的全部内容,如果对你有用麻烦关注点赞收藏三连走一波!
如有问题欢迎留言,如有错误烦请指正,期待你的评论!

6df8cced6a694d93b70fb00140789b08.png

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/m0_63947499/article/details/125300866

智能推荐

拥抱了IDEA却发现再也回不去Eclipse...-程序员宅基地

文章浏览阅读165次。拥抱了IDEA却发现再也回不去Eclipse... 一、背景  还记得去年入职的时候,发现很多同事都在用Intellij IDEA,其实在那之前都已经接触过,只不过没有在开发中实际应用而已. 这时候我下定决心要拥抱IDEA了,尤其被它酷酷的黑色主题所吸引,感觉那才是我们程序猿的正确打开方式.二、说明  要想从使用了很久的Eclips..._用完idea后不会用eclipse

数据收集-数据提取_openvigil2.1使用教程-程序员宅基地

文章浏览阅读780次。数据提取响应内容的分类xml和html的区别xmlxml和html的区别常用数据解析方法jsonpath模块使用方法案例练习lxml模块和xpath语法谷歌浏览器xpath helper插件安装xpath的节点关系xpath语法基础节点选择语法xpath定位节点以及提取属性或文本内容的语法语法练习节点修饰语法响应内容的分类结构化的响应内容json字符串可以使用re、json等模块来提取特定数据xml字符串可以使用re、lxml等模块来提取特定数据非结构化的响应内容html字符_openvigil2.1使用教程

unbuntu14.04密码正确登录不进去的解决_ubuntu14.04用户密码对但进不去-程序员宅基地

文章浏览阅读1.9k次。今天我想进入unbuntu系统学习一下linux的常用命令时发现竟然登录不进去了,昨晚还好好的,今天怎么就登不进去了。非常纳闷,于是想起上一次修改了environment文件,和profile文件,本人时linux菜鸟,修改了环境变量文件,以为是和windows环境变量一样,没想到导致登录老是循环在同一界面上,进不去,于是上网查了一下解决办法,发现原来是因为环境变量的问题。我具体的解决步骤为:_ubuntu14.04用户密码对但进不去

emmc 读写信号区分_EMMC转接座读写出错的解决方法-程序员宅基地

文章浏览阅读3.6k次。EMMC转接座使用:下面三个选项在“输入芯片印字”下面输入“EMMC”即可看到:EMMC_AUTO:速度8BIT(默认),智能识别-EMMC时就是它。EMMC_AUTO_4BIT:4BIT,建议大多数芯片选择它,稳定。EMMC_AUTO_1BIT:1BIT,以上两者均不能读时,可以选择它,虽速度慢但或许能挽救片内数据。另外,芯片型号后面_1/4/8BIT@FBGA153/FBGA169等(其中1/..._read emmc id failed

phpstrom配置连接虚拟机服务器_phpstorm使用虚拟机启动php-程序员宅基地

文章浏览阅读469次。踩坑的一天开发环境是Linux,在虚拟机里搭了个centos7,一键配置的lnmp在windows下利用phpstrom远程连接如何配置,步骤如下:首先打开界面,从顶部菜单栏选择Tools选项,选择Deployment,再选择configuration点击左上角加号选择SFTP输入一个昵称,随意,点击OK创建进来后,点击图示按钮进行配置输入服务器的ip,用户昵称,密码,点击testCconnection,测试是否能够连连通。能连通点击OK配置root path(网站根目录所在路径)和_phpstorm使用虚拟机启动php

Android--腾讯Bugly集成文档(吐槽版)_android bugly 符号表-程序员宅基地

文章浏览阅读1w次,点赞3次,收藏15次。本文是在原有的Bugly官方文档上进行修改(吐槽)而成官方开发文档:https://bugly.qq.com/docs/user-guide/instruction-manual-android-hotfix/?v=20170815114059准备好入坑了么?没准备好的,先去吃个糖压压惊ヽ(*。>Д゜゜゜介绍(一些废话,看看即可)热更新能力是Bugly为_android bugly 符号表

随便推点

Eclipse下代码字体背景变红/变绿/变黄原因-----已解决_eclipse测试爆黄色底纹-程序员宅基地

文章浏览阅读1.1k次。问题还原:为什么会出现这种情况eclipse 有测试代码覆盖率的功能,也就是执行Run——>Coverage后会出现这样 的效果原因来自解决办法*将Coverage调出来,[window – show view – other – java – Coverage],点击删除即可..._eclipse测试爆黄色底纹

LVS Centos7 安装_centos7安装lvs-程序员宅基地

文章浏览阅读3.2k次。Centos7已经自带了LVS,因此只需要安装LVS管理程序 ipvsadm(理解为ipvs admin)并配置即可。(1)安装ipvsadmyum -y install ipvsadm(2)设置ipv4转发sysctl -w net.ipv4.ip_forward=1(3)关闭防火墙systemctl stop firewalld.service #停止firewa..._centos7安装lvs

基于matlab的语义分割_使用扩张卷积进行语义分割-程序员宅基地

文章浏览阅读524次。加载训练数据该示例使用 32×32 三角形图像的简单数据集进行说明。该数据集包括附带的像素标签真实值数据。使用 imageDatastore 和 pixelLabelDatastore 加载训练数据。dataFolder = fullfile(toolboxdir('vision'),'visiondata','triangleImages');imageFolderTrain = fullfil..._matlab 语义分割 trainingimages

Quartz任务调度[Spring+Quartz结合]_实现任务的动态添加、修改和删除_synchronized quartz-程序员宅基地

文章浏览阅读1.3w次,点赞2次,收藏11次。项目框架图 下面开始贴代码了,不过先贴数据库^^-- Create table Oracle数据库create table QUARTZ_SCHEDULEJOB( id VARCHAR2(32), job_name VARCHAR2(32) not null, job_status NVARCHAR2(3) default 0 no_synchronized quartz

结构-行为-样式-JqueryUI拖放使用实例(全)_"jquery 水果拖放<div class=\"ui-weight-header\" id=\"f-程序员宅基地

文章浏览阅读310次。最近工作中有个需要是动态配置页面,想到之前公司有做过类似的,用的是JqueryUi,所以就看了下它的Api。下面就是我做的小Demo,想用的同学可以参考:Html: div class="page-component"> div class="pct-content"> div class="btn btn-default_"jquery 水果拖放"

spring AOP 动态代理 jkd动态代理和cglib动态代理 hibernate使用cglib延迟加载_jkd、cgl代理-程序员宅基地

文章浏览阅读1.9k次。spring 的AOP 实现 可以使用jdk的动态代理,也可以使用cglib的动态代理 先说下两者区别: 静态代理:代理之前就已经知道了代理者和被代理者 动态代理:代理之前并不清楚,在运行时使用反射机制动态生成代理类的字节码 无需我们手动编写它的源代码 jdk动态代理:java.lang.reflect 包中的Proxy类,InvocationHandler 接口提供了生成动态代理类的_jkd、cgl代理

推荐文章

热门文章

相关标签