1、使用Callable接口的方式实现多线程,这是JDK5.0新增的一种创建多线程的方法

 package com.baozi.java2;

 import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask; public class ThreadNew {
public static void main(String[] args){
//3、创建callable接口实现类的对象
NewThread newThread= new NewThread();
//4、将此callable接口实现类的对象作为参数传递到FutureTask类的构造器中创建出一个FutureTask的实现类
FutureTask futureTask = new FutureTask(newThread);
//5、将FutureTask类的对象作为参数传递给Thread类的构造器创建一个Thread对象然后调用start()方法启动该线程
new Thread(futureTask).start();
try {
//6、可以通过futureTask.get()方法获取call()方法中的返回值
System.out.println(futureTask.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
} }
//1、创建一个类实现callable接口
class NewThread implements Callable<Integer> {
//2、实现该接口中的call()方法
@Override
public Integer call() {
int sum = 0;
for (int i = 1; i <= 100; i++) {
if (i % 2 == 0) {
sum += i;
}
}
return sum;
}
}

2、使用Callable接口创建多线程和使用Runnable接口创建多线程的异同

相比较Runnable接口,Callable接口的功能更加强大。

  • 相比较Runnable接口中需要重写的run()方法,Callable接口需要重写的call()方法有返回值
  • 该方法可以抛出异常,外边的程序可以利用这个异常获取异常信息
  • 支持泛型的返回值
  • 需要借助FutureTask类,获取该线程的返回值

Future接口:

  • 可以对具体的Runnable、Callable任务的执行结果进行取消、查询是否完成、获取结果等操作
  • FutureTask是Future接口的唯一实现类
  • FutureTask同时实现了Runnable、Callable接口,它既可以作为Runnable被线程执行,又可以作为Future得到Callable的返回值

3、使用线程池的方法创建多线程

 package com.baozi.java2;

 import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor; public class ThreadPool {
public static void main(String[] args) {
//1、先创建一个线程池
ExecutorService service = Executors.newFixedThreadPool(10);
//3、为线程池中线程分配任务并执行
ThreadPoolExecutor service1=(ThreadPoolExecutor)service;
System.out.println(service.getClass());
service.execute(new NumberThread1());
service.execute(new NumberThread2());
service.execute(new NumberThread3());
//4、关闭线程池
service.shutdown();
}
}
//2、创建要执行某种操作的线程
class NumberThread1 implements Runnable {
@Override
public void run() {
for (int i = 0; i <= 100; i++) {
if (i % 2 == 0) {
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
}
} class NumberThread2 implements Runnable {
@Override
public void run() {
for (int i = 0; i <= 100; i++) {
if (i % 2 != 0) {
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
}
} class NumberThread3 implements Runnable {
@Override
public void run() {
for (int i = 0; i <= 100; i++) {
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
}

4、为什么要使用线程池的方法创建多线程

  传统的方法创建线程,当程序需要使用多线程的时候进行创建,用完之后就会立即销毁,如果频繁的进行这样的操作会消耗大量的系统资源,严重影响程序的性能。

5、线程池的任务处理策略:

  • 如果当前线程池中的线程数目小于corePoolSize,则每来一个任务,就会分配一个线程去执行这个任务;
  • 如果当前线程池中的线程数目>=corePoolSize,则每来一个任务,会尝试将其添加到任务缓存队列当中,若添加成功,则该任务会等待空闲线程将其取出去执行;若添加失败(一般来说是任务缓存队列已满),则会尝试创建新的线程去执行这个任务;如果当前线程池中的线程数目达到maximumPoolSize,则会采取任务拒绝策略进行处理;
  • 如果线程池中的线程数量大于 corePoolSize时,此时若某线程空闲时间超过keepAliveTime,该线程将被终止,直至线程池中的线程数目不大于corePoolSize;如果允许为核心池中的线程设置存活时间,那么核心池中的线程空闲时间超过keepAliveTime,线程也会被终止。

 public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
/*
* Proceed in 3 steps:
*
* 1. If fewer than corePoolSize threads are running, try to
* start a new thread with the given command as its first
* task. The call to addWorker atomically checks runState and
* workerCount, and so prevents false alarms that would add
* threads when it shouldn't, by returning false.
* 如果正在运行的线程数小于corePoolSize,那么将调用addWorker 方法来创建一个新的线程,并将该任务作为新线程的第一个任务来执行。
       当然,在创建线程之前会做原子性质的检查,如果条件不允许,则不创建线程来执行任务,并返回false.   * 2. If a task can be successfully queued, then we still need
* to double-check whether we should have added a thread
* (because existing ones died since last checking) or that
* the pool shut down since entry into this method. So we
* recheck state and if necessary roll back the enqueuing if
* stopped, or start a new thread if there are none.
* 如果一个任务成功进入阻塞队列,那么我们需要进行一个双重检查来确保是我们已经添加一个线程(因为存在着一些线程在上次检查后他已经死亡)或者
       当我们进入该方法时,该线程池已经关闭。所以,我们将重新检查状态,线程池关闭的情况下则回滚入队列,线程池没有线程的情况则创建一个新的线程。
* 3. If we cannot queue task, then we try to add a new
* thread. If it fails, we know we are shut down or saturated
* and so reject the task.
       如果任务无法入队列(队列满了),那么我们将尝试新开启一个线程(从corepoolsize到扩充到maximum),如果失败了,那么可以确定原因,要么是
       线程池关闭了或者饱和了(达到maximum),所以我们执行拒绝策略。 */
    
    // 1.当前线程数量小于corePoolSize,则创建并启动线程。
int c = ctl.get();
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
        // 成功,则返回 return;
c = ctl.get();
}
    // 2.步骤1失败,则尝试进入阻塞队列,
if (isRunning(c) && workQueue.offer(command)) {
       // 入队列成功,检查线程池状态,如果状态部署RUNNING而且remove成功,则拒绝任务
int recheck = ctl.get();
if (! isRunning(recheck) && remove(command))
reject(command);
       // 如果当前worker数量为0,通过addWorker(null, false)创建一个线程,其任务为null
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
    // 3. 步骤1和2失败,则尝试将线程池的数量有corePoolSize扩充至maxPoolSize,如果失败,则拒绝任务
else if (!addWorker(command, false))
reject(command);
}

线程池中关于任务分配策略的源码分析

6、线程池的关闭

ThreadPoolExecutor提供了两个方法,用于线程池的关闭,分别是shutdown()和shutdownNow(),其中:

  • shutdown():不会立即终止线程池,而是要等所有任务缓存队列中的任务都执行完后才终止,但再也不会接受新的任务
  • shutdownNow():立即终止线程池,并尝试打断正在执行的任务,并且清空任务缓存队列,返回尚未执行的任务

7、使用线程池创建多线程的优点

  • 减少了创建新线程的时间,提高程序的响应速度
  • 重复利用线程池中的线程,不需要每次都创建新的线程降低资源消耗
  • 便于线程的管理:
    • corePoolSize:表示允许线程池中允许同时运行的最大线程数
    • maximumPoolSize:线程池允许的最大线程数,他表示最大能创建多少个线程。maximumPoolSize肯定是大于等于corePoolSize
    • KeepAliveTime:表示线程没有任务时最多保持多久然后停止
    • ...

使用Callable接口创建线程和使用线程池的方式创建线程的更多相关文章

  1. 零基础逆向工程39_Win32_13_进程创建_句柄表_挂起方式创建进程

    1 进程的创建过程 打开系统 --> 双击要运行的程序 --> EXE开始执行 步骤一: 当系统启动后,创建一个进程:Explorer.exe(也就是桌面进程) 步骤二: 当用户双击某一个 ...

  2. Java线程间通信-回调的实现方式

    Java线程间通信-回调的实现方式   Java线程间通信是非常复杂的问题的.线程间通信问题本质上是如何将与线程相关的变量或者对象传递给别的线程,从而实现交互.   比如举一个简单例子,有一个多线程的 ...

  3. String常用使用方法,1.创建string的常用3+1种方式,2.引用类型使用==比较地址值,3.String当中获取相关的常用方法,4.字符串的截取方法,5.String转换常用方法,6.切割字符串----java

    一个知识点使用一个代码块方便查看 1.创建string的常用3+1种方式 /* 创建string的常用3+1种方式 三种构造方法 public String():创建一个空字符串,不含有任何内容: p ...

  4. java多线程 -- 创建线程的第三者方式 实现Callable接口

    Java 5.0 在 java.util.concurrent 提供了一个新的创建执行线程的方式:Callable 接口Callable 接口类似于 Runnable,两者都是为那些其实例可能被另一个 ...

  5. 实现Callable接口创建线程

    创建执行线程有四种方式: 实现implements接口创建线程 继承Thread类创建线程 实现Callable接口,通过FutureTask包装器来创建线程 使用线程池创建线程 下面介绍通过实现Ca ...

  6. 通过Callable接口创建线程

    通过Callable接口创建线程 一.前言 Java中创建线程的方式有四中,前两种在前面我已经详细介绍过了(Runnable和Thread),不清楚的朋友们可看这里: Java多线程之线程的启动以及J ...

  7. 创建线程之三:实现Callable接口

    通过Callable和Future创建线程 i. 创建Callable接口的实现类,并实现call方法,该call方法将作为线程执行体,并且有返回值,可以抛出异常. ii. 创建Callable实现类 ...

  8. 5.创建执行线程的方式之三 :实现Callable 接口

    Callable 接口 一.Java 5.0 在 java.util.concurrent 提供了 一个新的创建执行线程的方式(之前有继承Thread 和 实现Runnable):Callable 接 ...

  9. Java之创建线程的方式三:实现Callable接口

    import java.util.concurrent.Callable;import java.util.concurrent.ExecutionException;import java.util ...

随机推荐

  1. Java网络通信协议、UDP、TCP类加载整理

    网络通信协议 网络通信协议 网络通信协议有很多种,目前应用最广泛的是TCP/IP协议(Transmission Control Protocal/Internet Protoal传输控制协议/英特网互 ...

  2. function Language

    什么是函数式语言: 函数式语言(functional language)一类程序设计语言.是一种非冯·诺伊曼式的程序设计语言.函数式语言主要成分是原始函数.定义函数和函数型.这种语言具有较强的组织数据 ...

  3. Matlab怎么修改显示数值格式/精度/小数位数

    参考:https://jingyan.baidu.com/article/7f41ecec1ad029593c095c70.html

  4. Ubuntu16下Hive 安装

    0.安装环境和版本 Ubuntu16,hadoop版本是2.7.2 ,选择Hive版本为  hive-2.1.17 1. Hive安装包下载 地址: https://mirrors.tuna.tsin ...

  5. HDU 1051(处理木棍 贪心)

    题意是处理一批木棍,如果当前处理的木棍长度和重量均大于前一根木棍的长度和重量,则处理当前木棍花费为 0,否则花费为 1. 用结构体存储木棍信息,将木棍按照长度从小到大排序,若长度相等则按照重量从小到大 ...

  6. [物理学与PDEs]第3章习题2 仅受重力作用的定常不可压流理想流体沿流线的一个守恒量

    设定常 (即 $\cfrac{\p {\bf u}}{\p t}={\bf 0}$).不可压缩 (设 $\rho=1$) 的理想流体所受的体积力仅为重力. 又设磁场满足条件: $({\bf H}\cd ...

  7. Concurrent下的线程安全集合

    1.ArrayBlockingQueue ArrayBlockingQueue是由数组支持的线程安全的有界阻塞队列,此队列按 FIFO(先进先出)原则对元素进行排序.这是一个典型的“有界缓存区”,固定 ...

  8. iPhone8再MacOS上修改手机铃声

    1 选择下载好的mp3铃声文件,导入到itunes 2 将音乐改成AAA模式, 设置你的铃声时长 3 show in finder 找到文件,将mpr后缀修改成m4r,并删除掉mp3文件,将m4r文件 ...

  9. 新加坡100M带宽,国内延迟70ms,仅800元

    ▇ 新加坡100M带宽,延迟80msE3_8G_1TB_100M_5IP_800元促:E3_32G_1TB SSD_1200元 ▇ 马来西亚,独享带宽,延迟70msL5630_16G_1TB_15M_ ...

  10. mysql-8.0.11安装步骤

    1.下载好安装包:mysql-8.0.11-winx64.zip 2.解压到合适的目录,例如:C:\XQ\Soft\mysql-8.0.11-winx64 3.在目录下创建my.ini文件,配置bas ...