从零学Java 线程池

这篇具有很好参考价值的文章主要介绍了从零学Java 线程池。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

Java 线程池

1 线程池概念

1.1 现有问题

  • 线程是宝贵的内存资源、单个线程约占1MB空间,过多分配易造成内存溢出。
  • 频繁的创建及销毁线程会增加虚拟机回收频率、资源开销,造成程序性能下降。

1.2 线程池

  • 线程容器,可设定线程分配的数量上限。
  • 将预先创建的线程对象存入池中,并重用线程池中的线程对象。
  • 避免频繁的创建和销毁。

2 线程池原理

从零学Java 线程池,从零学Java,java,开发语言

将任务提交给线程池,由线程池分配线程、运行任务,并在当前任务结束后复用线程。

3 如何使用线程池

3.1 获取线程池

常用的线程池接口和类(所在包java.util.concurrent):

  • Executor:线程池的顶级接口。
    • execute(); 执行任务
  • ExecutorService:线程池接口,可通过submit(Runnable task) 提交任务代码。
    • shutdown() 关闭线程池
    • isTerminated() 判断线程池中任务和线程是否已经执行完毕。
    • submit() 提交任务
  • Executors工厂类:通过此类可以获得一个线程池。
    • newFixedThreadPool(int nThreads) 获取固定数量的线程池。
      参数:指定线程池中线程的数量。
    • newCachedThreadPool() 获得动态数量的线程池,如不够则创建新的,无上限。

eg:文章来源地址https://www.toymoban.com/news/detail-798657.html

//1 创建固定数量的线程池
ExecutorService es = Executors.newFixedThreadPool(4);
//2 创建动态数量的线程池
ExecutorService es = Executors.newCachedThreadPool();
public class TestExecutors {
    public static void main(String[] args) {
        //1.1 创建固定数量的线程池
        //ExecutorService es = Executors.newFixedThreadPool(4);
        //1.2 创建动态数量的线程池
        ExecutorService es = Executors.newCachedThreadPool();
        //2 添加任务
        Runnable ticket = new Runnable() {
            private int count = 1000;
            @Override
            public void run() {
                while (true) {
                    synchronized (this) {
                        if (count<=0) {
                            break;
                        }
                        System.out.println(
                                Thread.currentThread().getName()+"卖了第"+count+"张票"
                        );
                        count--;
                    }
                }
            }
        };
        for (int i = 0; i < 4; i++) {
            //提交任务
            es.submit(ticket);
        }
        //3 关闭线程池
        es.shutdown();
    }
}

eg:

//3 创建单线程线程池, 只有一个线程的线程池
public class TestSingThread {
    public static void main(String[] args) {
        //3 创建单线程线程池, 只有一个线程的线程池
        ExecutorService es = Executors.newSingleThreadExecutor();
        for (int i = 0; i < 10; i++) {
            es.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName()+"...");
                }
            });
        }
        es.shutdown();
    }
}

eg:

//4 创建调度线程池, 实现周期执行和延迟执行
public class TestScheduleThread {
    public static void main(String[] args) {
        //4 创建调度线程池, 实现周期执行和延迟执行
        //周期执行: 间隔指定时间执行一次, 不能关闭线程
        ScheduledExecutorService es = Executors.newScheduledThreadPool(1);
        es.scheduleAtFixedRate(new Runnable() {
            int num = 0;
            @Override
            public void run() {
                System.out.println(new Date()+"..."+num);
                num++;
                if (num == 10) {
                    es.shutdown();
                }
            }
        },0,1000, TimeUnit.MILLISECONDS);

        //延迟执行: 延迟一定时间执行
        es.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println(new Date());
            }
        },3000, TimeUnit.MILLISECONDS);
        es.shutdown();
    }
}

eg:

//5 工作窃取线程池
public class TestWorkStealingPool {
    public static void main(String[] args) {
        //5 工作窃取线程池
        //5.1 工作窃取线程池中的都属于后台线程
        //5.2 每一个线程都有自己的双端队列, 队头和队尾都可以加入和删除元素
        //5.3 自己线程的队列中任务执行完后, 会去别的线程队列的队尾窃取一个任务执行
        //默认使用CPU内核个数作为线程个数
        ExecutorService es = Executors.newWorkStealingPool();
        for (int i = 0; i < 10; i++) {
            es.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName()+"...");
                }
            });
        }
        es.shutdown();
        //阻止主线程结束
        while (!es.isTerminated()); //判断线程池中的任务和线程是否执行完毕
        System.out.println("执行完毕");
    }
}

4 创建线程的第四种方式

实现 Callable 接口, 重写 call() 方法

  • JDK5加入,与Runnable接口类似,实现之后代表一个线程任务。
  • Callable具有泛型返回值、可以声明异常。

语法:

public interface Callable<V>{
	public V call() throws Exception;
}

Future接口

  • 概念:异步接收ExecutorService.submit()所返回的状态结果,当中包含了call()的返回值。
  • 方法:V get()以阻塞形式等待Future中的异步处理结果(call()的返回值)

eg:

MyCallable:

public class MyCallable implements Callable<Integer> {
    @Override
    public Integer call() throws Exception {
        int sum = 0;
        System.out.println(Thread.currentThread().getName()+"开始计算...");
        for (int i = 1; i <= 100; i++) {
            sum+=i;
            Thread.sleep(100);
        }
        System.out.println(Thread.currentThread().getName()+"结束计算...");
        return sum;
    }
}

Test:

public class Test {
    public static void main(String[] args) throws Exception{
        //1 创建可调用对象
        MyCallable mc = new MyCallable();
        //2 创建线程池(单线程线程池)
        ExecutorService es = Executors.newSingleThreadExecutor();
        //3 提交任务
        //Future: 表示线程将要执行完毕的结果
        Future<Integer> future = es.submit(mc);
        //4 获取结果(阻塞方法, 直到线程池中的任务执行完毕才返回)
        System.out.println("结果是:"+future.get());
        //5 关闭线程池
        es.shutdown();
    }
}

课堂案例

eg:

需求:使用两个线程,并发计算1~50、51~100的和,再进行汇总统计。
public class Test {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //创建动态线程池
        ExecutorService es = Executors.newCachedThreadPool();
        //添加任务1
        Future<Integer> f1 = es.submit(new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                int sum = 0;
                for (int i = 1; i <= 50 ; i++) {
                    sum+=i;
                }
                return sum;
            }
        });
        //添加任务2
        Future<Integer> f2 = es.submit(new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                int sum = 0;
                for (int i = 51; i <= 100 ; i++) {
                    sum+=i;
                }
                return sum;
            }
        });
        //汇总
        System.out.println("结果为:"+(f1.get() + f2.get()));
        //关闭线程池
        es.shutdown();
    }
}

到了这里,关于从零学Java 线程池的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 从零学Java - String类

    Java程序中的所有字符串文本(例如“abc”)都是此类的实例。 字符串字面值是常量,具有不可变性,创建之后不可改变。 1.1 常用两种创建方式 1.2 比较两种创建方式 字符串字面值保存在常量池中,JDK1.7之前常量池存在方法区中,JDK1.7(包括1.7)之后移入堆中;常量池中数据

    2024年02月02日
    浏览(37)
  • 从零学Java 单例模式

    单例(Singleton):保证只能创建一个该类的对象。 实现单例三个步骤 私有化构造方法 在类内部创建一个对象 在类中添加一个公开的方法,返回单例对象 2.1 饿汉式 饿汉式: 类加载时, 对象则实例化 代码实现: SingleTon: Test: 2.2 懒汉式 懒汉式: 使用时创建, 线程不安全 代码实现

    2024年01月23日
    浏览(33)
  • Jeecg-Boot 低代码开发平台之路(一) —— 开始从零学起

    今天开始详细学习下 Jeecg-Boot 低代码开发平台,官方网站对该平台的介绍是如下。 JeecgBoot是一款基于BPM的低代码平台!前后端分离架构 SpringBoot 2.x,SpringCloud,Ant DesignVue,Mybatis-plus,Shiro,JWT,支持微服务。强大的代码生成器让前后端代码一键生成,实现低代码开发! Jee

    2023年04月08日
    浏览(89)
  • Java/Python/Go不同开发语言在进程、线程和协程的设计差异

    在多线程项目开发时,最常用、最常遇到的问题是 1,线程、协程安全 2,线程、协程间的通信和控制 本文主要探讨不同开发语言go、java、python在进程、线程和协程上的设计和开发方式的异同。 进程 进程是 操作系统进行资源分配的基本单位,每个进程都有自己的独立内存空

    2024年01月23日
    浏览(50)
  • 【从零开始学习JAVA | 第四十篇】了解线程池

    目录 前言: 线程池: 线程池的工作流程: 代码实现线程池: 任务拒绝策略:  线程池多大才算合适? 总结:         在Java编程中,线程池是一个强大的工具,它能够管理和复用线程,提供高效的并发处理能力。通过线程池,我们可以有效地控制并发线程的数量,并降

    2024年02月13日
    浏览(56)
  • 【从零开始学习JAVA | 三十九篇】深入多线程

    目录 前言:         ​1.线程的寿命周期​ 2.线程的安全问题 3.锁 同步代码块: 同步方法: 死锁: 4.生产者和消费者模式(等待唤醒机制) 总结:         当今软件开发领域中,多线程编程已成为一项至关重要的技能。然而,要编写出高效、可靠的多线程程序并不容

    2024年02月13日
    浏览(62)
  • 【从零开始学习JAVA | 第三十七篇】初识多线程

    目录 前言: ​编辑 引入: 多线程:         什么是多线程:         多线程的意义:         多线程的应用场景: 总结:                 本章节我们将开始学习多线程,多线程是一个很重要的知识点,他在我们实际开发中应用广泛并且基础,可以说掌握多线程编写程

    2024年02月14日
    浏览(107)
  • 【从零开始学习JAVA | 第三十八篇】应用多线程

    目录 前言: 多线程的实现方式: Thread常见的成员方法: 总结:            多线程的引入不仅仅是提高计算机处理能力的技术手段,更是适应当前时代对效率和性能要求的必然选择。在本文中,我们将深入探讨多线程的应用和实践,帮助读者更好地理解和应用多线程技术,

    2024年02月13日
    浏览(68)
  • 从零开始学习 Java:简单易懂的入门指南之线程池(三十六)

    当线程被创建并启动以后,它既不是一启动就进入了执行状态,也不是一直处于执行状态。线程对象在不同的时期有不同的状态。那么Java中的线程存在哪几种状态呢?Java中的线程 状态被定义在了java.lang.Thread.State枚举类中,State枚举类的源码如下: 通过源码我们可以看到Ja

    2024年02月08日
    浏览(53)
  • 从零开始学习 Java:简单易懂的入门指南之线程同步(三十五)

    1.1卖票【应用】 案例需求 某电影院目前正在上映国产大片,共有100张票,而它有3个窗口卖票,请设计一个程序模拟该电影院卖票 实现步骤 定义一个类SellTicket实现Runnable接口,里面定义一个成员变量:private int tickets = 100; 在SellTicket类中重写run()方法实现卖票,代码步骤如下

    2024年02月08日
    浏览(49)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包