线程创建的三种方式和线程池创建的四种方式

线程创建的三种方式和线程池创建的四种方式

1.1线程创建的方式

java创建线程的三种方式:

  1. 继承Thread类创建线程类
  2. 实现Runnable接口
  3. 通过Callable和Future创建线程

1.2创建线程

1.2.1继承Thread类

(1)创建Thread类的子类,并重写该类的run方法,该run方法的方法体就代表了线程要完成的任务。因此把run()方法称为执行体。

(2)创建了Thread子类的实例,即创建了线程对象。

(3)调用线程对象的start()方法来启动该线程。

package com.thread;

public class Thread01 {
    public static void main(String[] args) {
        MyThread01 t1=new MyThread01();
        MyThread01 t2=new MyThread01();
        t1.start();
        t2.start();
    }
}

class MyThread01 extends Thread{

    @Override
    public void run() {
        System.out.println("线程名:"+currentThread().getName());
    }
}

1.2.2实现Runnable接口

(1)定义runnable接口的实现类,并重写该接口的run()方法,该run()方法的方法体同样是该线程的线程执行体。

(2)创建 Runnable实现类的实例,并依此实例作为Thread的target来创建Thread对象,该Thread对象才是真正的线程对象。

(3)调用线程对象的start()方法来启动该线程。

线程的执行流程很简单,当执行代码start()时,就会执行对象中重写的void run()方法,该方法执行完成后,线程就消亡了。

package com.thread;

public class Thread02 {

    public static void main(String[] args) {

        MyThread02 target=new MyThread02();

        Thread t1=new Thread(target);
        Thread t2=new Thread(target);

        t1.start();
        t2.start();
    }
}

class MyThread02 implements Runnable{

    @Override
    public void run() {
        System.out.println("线程名:"+Thread.currentThread().getName());
    }
}

1.2.3实现Callable接口

(1)创建Callable接口的实现类,并实现call()方法,该call()方法将作为线程执行体,并且有返回值。

(2)创建Callable实现类的实例,使用FutureTask类来包装Callable对象,该FutureTask对象封装了该Callable对象的call()方法的返回值。

(3)使用FutureTask对象作为Thread对象的target创建并启动新线程。

(4)调用FutureTask对象的get()方法来获得子线程执行结束后的返回值

package com.thread;

import java.util.concurrent.*;

public class Thread03 {

    public static void main(String[] args) throws ExecutionException, InterruptedException, TimeoutException {
        Callable callable=new MyThread03();
        FutureTask task1=new FutureTask(callable);
        FutureTask task2=new FutureTask(callable);
        new Thread(task1).start();
        new Thread(task2).start();
        Thread.sleep(10);//等待线程执行结束

        //task.get() 获取call()的返回值。若调用时call()方法未返回,则阻塞线程等待返回值
        System.out.println(task1.get());
        System.out.println(task2.get());

        //get的传入参数为等待时间,超时抛出超时异常;传入参数为空时,则不设超时,一直等待
        System.out.println(task1.get(10L, TimeUnit.MILLISECONDS));
        System.out.println(task2.get(10L, TimeUnit.MILLISECONDS));
    }
}

class MyThread03 implements Callable{

    @Override
    public Object call() throws Exception {
        System.out.println("线程名:"+Thread.currentThread().getName());
        return "实现callable:"+Thread.currentThread().getName();
    }
}

1.2.4三种方式的对比

采用继承Thread类方式:

(1)优点:编写简单,如果需要访问当前线程,无需使用Thread.currentThread()方法,直接使用this,即可获得当前线程。
(2)缺点:因为线程类已经继承了Thread类,所以不能再继承其他的父类。

采用实现Runnable接口方式:

(1)优点:线程类只是实现了Runable接口,还可以继承其他的类。在这种方式下,可以多个线程共享同一个目标对象,所以非常适合多个相同线程来处理同一份资源的情况,从而可以将CPU代码和数据分开,形成清晰的模型,较好地体现了面向对象的思想。
(2)缺点:编程稍微复杂,如果需要访问当前线程,必须使用Thread.currentThread()方法。

Runnable和Callable的区别:

(1)Callable规定的方法是call(),Runnable规定的方法是run().
(2)Callable的任务执行后可返回值,而Runnable的任务是不能返回值得
(3)call方法可以抛出异常,run方法不可以,因为run方法本身没有抛出异常,所以自定义的线程类在重写run的时候也无法抛出异常
(4)运行Callable任务可以拿到一个Future对象,表示异步计算的结果。它提供了检查计算是否完成的方法,以等待计算的完成,并检索计算的结果。通过Future对象可以了解任务执行情况,可取消任务的执行,还可获取执行结果。

1.2.5start()和run()的区别

start()方法用来,开启线程,但是线程开启后并没有立即执行,他需要获取cpu的执行权才可以执行

run()方法是由jvm创建完本地操作系统级线程后回调的方法,不可以手动调用(否则就是普通方法)

2.创建线程池的四种方式

(1)newCachedThreadPool 创建一个可缓存的线程池,如果线程池长度超过处理需求,可灵活回收空闲线程,若无可回收,则新建线程。

(2)newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待

(3)newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行

(4)newSingleThreadExecutor 创建一个单线程化的线程池,它只会唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO,LIFO,优先级)执行

线程池的优点:

(1)重用存在的线程,减少对象创建、消亡的开销,性能佳

(2)可有效控制最大并发线程数,提高系统资源的使用率,同时避免过多资源竞争,避免堵塞。

(3)提供定时执行、定期执行、单线程、并发数控制等功能。

使用newThread的弊端

  1. 每次new Thread新建对象性能差
  2. 线程缺乏统一管理,可能无限制新建线程,相互之间竞争,及可能占用过多系统资源导致死机或oom(out of memory)。
  3. 缺乏更多功能,如定时执行、定期执行、线程中断。

2.1使用newCachedThreadPool

创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。

package com.thread;

import sun.rmi.runtime.Log;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ThreadPoolDemo01 {
    public static void main(String[] args) {
        ExecutorService cachedThreadPool= Executors.newCachedThreadPool();
        for(int i=0;i<20;i++){
            final int index=i;
            try {
                Thread.sleep(index*100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            cachedThreadPool.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println("此线程名:"+Thread.currentThread().getName());
                }
            });
        }
    }
}

线程池为无限大,当执行第二个任务时第一个任务已经完成,会复用执行第一个任务的线程,而不用每次新建线程。

2.2 newFixedThreadPool

需要指定线程池的大小,创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。

package com.thread;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ThreadPoolDemo02 {

    public static void main(String[] args) {

        ExecutorService fixedThreadPool= Executors.newFixedThreadPool(3);
        for(int i=0;i<10;i++){
            final int index=i;

            fixedThreadPool.execute(new Runnable() {
                @Override
                public void run() {
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("线程名:"+Thread.currentThread().getName());
                }
            });
        }
    }
}

因为线程池大小为3,每个任务输出index后sleep 2秒,所以每两秒打印3个数字。定长线程池的大小最好根据系统资源进行设置。

2.3newScheduledThreadPool

创建一个定长线程池,支持定时及周期性任务执行。延迟执行示例代码如下:

延迟3秒执行

package com.thread;

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class ThreadPoolDemo03 {

    public static void main(String[] args) {
        ScheduledExecutorService scheduledThreadPool= Executors.newScheduledThreadPool(5);
        scheduledThreadPool.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("线程名:"+Thread.currentThread().getName());
            }
        },3, TimeUnit.SECONDS);
    }
}

定期执行:

表示延迟1秒后每3秒执行一次。

ScheduledExecutorService比Timer更安全,功能更强大

package com.thread;

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class ThreadPoolDemo03_2 {
    public static void main(String[] args) {
        ScheduledExecutorService scheduledThreadPool= Executors.newScheduledThreadPool(5);
        scheduledThreadPool.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                System.out.println("线程名:"+Thread.currentThread().getName());
            }
        },1,3, TimeUnit.SECONDS);
    }
}

2.4newSingleThreadExecutor

创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。示例代码如下:

package com.thread;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ThreadPoolDemo04 {
    public static void main(String[] args) {
        ExecutorService singleThreadExecutor= Executors.newSingleThreadExecutor();
        for(int i=0;i<10;i++){
            singleThreadExecutor.execute(new Runnable() {
                @Override
                public void run() {
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("线程名:"+Thread.currentThread().getName());
                }
            });
        }
    }
}

结果依次输出,相当于顺序执行各个任务。

线程池的作用:

线程池作用就是限制系统中执行线程的数量。

根据系统的环境情况,可以自动或手动设置线程数量,达到运行的最佳效果;少了浪费了系统资源,多了造成系统拥挤效率不高。用线程池控制线程数量,其他线程排队等候。一个任务执行完毕,再从队列的中取最前面的任务开始执行。若队列中没有等待进程,线程池的这一资源处于等待。当一个新任务需要运行时,如果线程池中有等待的工作线程,就可以开始运行了;否则进入等待队列。

为什么要用线程池:

1.减少了创建和销毁线程的次数,每个工作线程都可以被重复利用,可执行多个任务。

2.可以根据系统的承受能力,调整线程池中工作线线程的数目,防止因为消耗过多的内存,而把服务器累趴下(每个线程需要大约1MB内存,线程开的越多,消耗的内存也就越大,最后死机)。

Java里面线程池的顶级接口是Executor,但是严格意义上讲Executor并不是一个线程池,而只是一个执行线程的工具。真正的线程池接口是ExecutorService。

比较重要的几个类:

ExecutorService: 真正的线程池接口。

ScheduledExecutorService: 能和Timer/TimerTask类似,解决那些需要任务重复执行的问题。

ThreadPoolExecutor: ExecutorService的默认实现。

ScheduledThreadPoolExecutor: 继承ThreadPoolExecutor的ScheduledExecutorService接口实现,周期性任务调度的类实现。

要配置一个线程池是比较复杂的,尤其是对于线程池的原理不是很清楚的情况下,很有可能配置的线程池不是较优的,因此在Executors类里面提供了一些静态工厂,生成一些常用的线程池。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/770212.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

单调栈(左小大,右小大)

①寻找每个数左边第一个比它小的数 给定一个长度为 N 的整数数列&#xff0c;输出每个数左边第一个比它小的数&#xff0c;如果不存在则输出 −1。 输入样例&#xff1a; 3 4 2 7 5 输出样例&#xff1a; -1 3 -1 2 2 从左到右遍历&#xff0c;用单调递增&#xff08;栈底到栈顶…

c->c++(二):class

本文主要探讨C类的相关知识。 构造和析构函数 构造函数(可多个)&#xff1a;对象产生时调用初始化class属性、分配class内部需要的动态内存 析构函数&#xff08;一个&#xff09;&#xff1a;对对象消亡时调用回收分配动态内存 C提供默认构造和析构,…

行人检测技术:思通数科大模型在自动驾驶安全中的应用

在自动驾驶技术飞速发展的今天&#xff0c;行人检测已成为确保道路交通安全的关键技术之一。本文将探讨如何结合思通数科大模型和计算机视觉技术&#xff0c;实现在城市交通环境中对行人的高效检测&#xff0c;为自动驾驶车辆提供必要的行人安全保障。 引言 行人检测技术是利…

Dubbo内部通信流程

我当时在学习的过程中搭建过demo&#xff0c;具体流程就是&#xff0c;我先定义了一个api接口模块&#xff0c;还定义一个服务提供者模块&#xff0c;然后服务提供方实现该接口&#xff0c;定义该方法具体的实现impl类&#xff0c;服务提供方启动时&#xff0c;将要暴露的服务和…

【架构-20】死锁

什么是死锁&#xff1f; 死锁(Deadlock)是指两个或多个线程/进程在执行过程中,由于资源的互相占用和等待,而陷入一种互相等待的僵局,无法继续往下执行的情况。 产生死锁的四个必要条件: &#xff08;1&#xff09;互斥条件(Mutual Exclusion)&#xff1a;至少有一个资源是非共享…

跨阻放大器

#创作灵感# 最近涉及到微电流的监测项目&#xff0c;而里面的核心就是跨阻放大器&#xff0c;所以这里做一个简单的介绍&#xff0c;后续等项目完成了&#xff0c;再做一个实例的介绍。 #正文# 跨阻放大器&#xff08;Transimpedance Amplifier, TIA&#xff09;是一种将输入电…

Windows编程之多线程事件对象(Event Object)用法详解

目录 一、前言 二、基础用法 三、API详解 1.创建事件对象 2控制事件状态 3.等待事件对象&#xff1a; 四、实战案例 1.案例描述 2.代码设计 3.总设计代码 4.运行结果 一、前言 事件对象&#xff08;Event Object&#xff09;是我们在大型项目中&#xff0c;进行多线…

股价持续低迷,业绩颓势不减,冀光恒难救平安银行?

文&#xff5c;新熔财经 作者&#xff5c;宏一 周一一上班&#xff0c;就听到旁边的同事感慨今年股市行情很不错&#xff0c;尤其是银行股&#xff0c;上半年累计上涨了17.02%&#xff0c;是涨幅最大的板块。 听到这里&#xff0c;我美滋滋地打开自己的账户&#xff0c;结…

如何对低代码平台进行分类?

现在市面上的低代码平台就像雨后春笋一样冒出来&#xff0c;而且源源不绝&#xff0c;但总结下来&#xff0c;大致的也就以下三类。 一、 aPaaS多引擎类&#xff08;有很多成熟引擎、做好东西要一起用&#xff09; 这类产品包括&#xff1a;织信Informat&#xff08;国内&…

照明物联网:基于网关的智能照明云监控系统解决方案

智能照明系统就是利用物联网技术&#xff0c;将同一空间的照明、空调、新风、排风等系统共同接入物联网平台&#xff0c;实现了“设备互联、数据互通”的智慧物联能力。照明数据、环境监测数据通过网关上传云端&#xff0c;在云端进行统计分析并将结果通过各种终端共享&#xf…

MySQL—常用的数据类型

数据类型 整型 1.创建一个含有无符号/有符号整型的字段的表 CREATE TABLE L1(id tinyint unsigned #无符号 ) CREATE TABLE L2(id tinyint #默认为有符号 ) 数值型(bit) 2.数值型(bit)的使用 小数 3.数值型(小数)的基本使用 字符串 4.字符串的基本使用 #演示字符串类型…

REGX52.H报错

keil cannot open source input file "REGX52.H": No such file or directory 选择下面这个目录 Keil\C51\INC\Atmel

AI绘画Stable Diffusion 新手入门教程:万字长文解析Lora模型的使用,快速上手Lora模型!

大家好&#xff0c;我是设计师阿威 今天给大家讲解一下AI绘画Stable Diffusion 中的一个重要模型—Lora模型&#xff0c;如果还有小伙伴没有SD安装包的&#xff0c;可以看我往期入门教程2024最新超强AI绘画Stable Diffusion整合包安装教程&#xff0c;零基础入门必备&#xff…

【软件测试】Selenium自动化测试框架 | 相关介绍 | Selenium + Java环境搭建 | 常用API的使用

文章目录 自动化测试一、selenium1.相关介绍1.Selenium IDE2.Webdriverwebdriver的工作原理&#xff1a; 3.selenium Grid 2.Selenium Java环境搭建3.常用API的使用1.定位元素2.操作测试对象3.添加等待4.打印信息5.浏览器的操作6.键盘事件7.鼠标事件8.定位一组元素9.多层框架定…

手把手家教你进行ChatGPT私有化部署

背景 随着AI技术的不断成熟&#xff0c;加上ChatGPT如火如荼的发布新版本迭代更新&#xff0c;人工智能的热度也升温到史无前例的高度。 我们有理由相信&#xff0c;现在身边还不愿主动去接触这项技术&#xff0c;深入了解的小伙伴&#xff0c;在不久的将来&#xff0c;一定会…

网络攻防——kali操作系统基本使用

1.阅读前的声明 本文章中生成的木马带有一定的攻击性&#xff0c;使用时请遵守网络安全相关的法律法规&#xff08;恶意攻击操作系统属于违法行为&#xff09;。 2.环境安装 生成木马主要需要如下工具&#xff1a;kali操作系统&#xff0c;VMware15&#xff08;搭建kali操作…

用Python制作动态钟表:实时显示时间的动画

文章目录 引言准备工作前置条件 代码实现与解析导入必要的库初始化Pygame绘制钟表函数主循环 完整代码 引言 动态钟表是一种直观且实用的UI元素&#xff0c;能够实时显示当前时间。在这篇博客中&#xff0c;我们将使用Python创建一个动态钟表&#xff0c;通过利用Pygame库来实…

无线物联网题集

测试一 未来信息产业的发展在由信息网络向 全面感知和 智能应用两个方向拓展、延伸和突破。 各国均把 物联网作为未来信息化战略的重要内容,融合各种信息技术,突破互联网的限制,将物体接入信息网络。 计算机的出现,开始了第四次工业革命,开始了人机物的高度融合&#xff08;&…

LVS负载均衡群集部署之——DR模式的介绍及搭建步骤

一、LVS-DR集群介绍1.1 LVS-DR 工作原理1.2 数据包流向分析1.3 LVS-DR 模式的特点1.4 LVS-DR中的ARP问题1.4.1 问题一1.4.2 问题二二、构建LVS-DR集群2.1 构建LVS-DR集群的步骤&#xff08;理论&#xff09;1.配置负载调度器&#xff08;192.168.80.30&#xff09;&#xff08;…

护眼指南之适合学生写作业的台灯:看看学生护眼台灯哪个品牌好

随着人们健康意识的提高&#xff0c;越来越多的人开始关注眼睛的健康问题&#xff0c;照明技术的进步也为缓解眼疲劳提供了可能&#xff0c;现在的照明产品可以通过调整光线亮度、色温、频闪等参数&#xff0c;使光线更加柔和、均匀&#xff0c;减少眼睛的不适感。人们都希望通…