java 多线程实现的四种方式
一个线程的生命周期
线程是一个动态执行的过程,它也有一个从产生到死亡的过程。
下图显示了一个线程完整的生命周期。

- 新建状态:
使用 new 关键字和 Thread 类或其子类建立一个线程对象后,该线程对象就处于新建状态。它保持这个状态直到程序 start() 这个线程。
- 就绪状态:
当线程对象调用了start()方法之后,该线程就进入就绪状态。就绪状态的线程处于就绪队列中,要等待JVM里线程调度器的调度。
- 运行状态:
如果就绪状态的线程获取 CPU 资源,就可以执行 run(),此时线程便处于运行状态。处于运行状态的线程最为复杂,它可以变为阻塞状态、就绪状态和死亡状态。
- 阻塞状态:
如果一个线程执行了sleep(睡眠)、suspend(挂起)等方法,失去所占用资源之后,该线程就从运行状态进入阻塞状态。在睡眠时间已到或获得设备资源后可以重新进入就绪状态。可以分为三种:
等待阻塞:运行状态中的线程执行 wait() 方法,使线程进入到等待阻塞状态。
同步阻塞:线程在获取 synchronized 同步锁失败(因为同步锁被其他线程占用)。
其他阻塞:通过调用线程的 sleep() 或 join() 发出了 I/O 请求时,线程就会进入到阻塞状态。当sleep() 状态超时,join() 等待线程终止或超时,或者 I/O 处理完毕,线程重新转入就绪状态。
- 死亡状态:
一个运行状态的线程完成任务或者其他终止条件发生时,该线程就切换到终止状态。
线程的优先级
每一个 Java 线程都有一个优先级,这样有助于操作系统确定线程的调度顺序。
Java 线程的优先级是一个整数,其取值范围是 1 (Thread.MIN_PRIORITY ) - 10 (Thread.MAX_PRIORITY )。
默认情况下,每一个线程都会分配一个优先级 NORM_PRIORITY(5)。
具有较高优先级的线程对程序更重要,并且应该在低优先级的线程之前分配处理器资源。但是,线程优先级不能保证线程执行的顺序,而且非常依赖于平台。
Java多线程实现的方式有四种
- 1.继承Thread类,重写run方法
- 2.实现Runnable接口,重写run方法,实现Runnable接口的实现类的实例对象作为Thread构造函数的target
- 3.通过Callable和FutureTask创建线程
4.通过线程池创建线程
前面两种可以归结为一类:无返回值,原因很简单,通过重写run方法,run方式的返回值是void,所以没有办法返回结果
后面两种可以归结成一类:有返回值,通过Callable接口,就要实现call方法,这个方法的返回值是Object,所以返回的结果可以放在Object对象中
方式1:继承Thread类的线程实现方式如下:
public class ThreadDemo1 extends Thread {
public ThreadDemo1() {
// 编写子类的构造方法,可缺省
}
public void run() {
// 编写自己的线程代码
System.out.println(Thread.currentThread().getName());
}
public static void main(String[] args) {
ThreadDemo1 threadDemo1 = new ThreadDemo1();
threadDemo1.setName("我是自定义的线程1");
threadDemo1.start();
System.out.println(Thread.currentThread().getName());
}
}
控制台输出:
main
我是自定义的线程1
下表列出了Thread类的一些重要方法:
| 序号 | 方法描述 |
|---|---|
| 1 | public void start() 使该线程开始执行;Java 虚拟机调用该线程的 run 方法。 |
| 2 | public void run() 如果该线程是使用独立的 Runnable 运行对象构造的,则调用该 Runnable 对象的 run 方法;否则,该方法不执行任何操作并返回。 |
| 3 | public final void setName(String name) 改变线程名称,使之与参数 name 相同。 |
| 4 | public final void setPriority(int priority) 更改线程的优先级。 |
| 5 | public final void setDaemon(boolean on) 将该线程标记为守护线程或用户线程。 |
| 6 | public final void join(long millisec) 等待该线程终止的时间最长为 millis 毫秒。 |
| 7 | public void interrupt() 中断线程。 |
| 8 | public final boolean isAlive() 测试线程是否处于活动状态。 |
测试线程是否处于活动状态。 上述方法是被Thread对象调用的。下面的方法是Thread类的静态方法。
| 序号 | 方法描述 |
|---|---|
| 1 | public static void yield() 暂停当前正在执行的线程对象,并执行其他线程。 |
| 2 | public static void sleep(long millisec) 在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。 |
| 3 | public static boolean holdsLock(Object x) 当且仅当当前线程在指定的对象上保持监视器锁时,才返回 true。 |
| 4 | public static Thread currentThread() 返回对当前正在执行的线程对象的引用。 |
| 5 | public static void dumpStack() 将当前线程的堆栈跟踪打印至标准错误流。 |
方式2:通过实现Runnable接口,实现run方法,接口的实现类的实例作为Thread的target作为参数传入带参的Thread构造函数,通过调用start()方法启动线程:
public class ThreadDemo2 implements Runnable {
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println(Thread.currentThread().getName()
+ "-->我是通过实现接口的线程实现方式!");
}
public static void main(String[] args) {
System.out.println(Thread.currentThread().getName());
Thread t1 = new Thread(new ThreadDemo2());
t1.start();
}
}
控制台输出:
main
Thread-0-->我是通过实现接口的线程实现方式!
方式3:通过Callable和FutureTask创建线程
a:创建Callable接口的实现类 ,并实现Call方法
b:创建Callable实现类的实现,使用FutureTask类包装Callable对象,该FutureTask对象封装了Callable对象的Call方法的返回值
c:使用FutureTask对象作为Thread对象的target创建并启动线程
d:调用FutureTask对象的get()来获取子线程执行结束的返回值
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
public class ThreadDemo3 {
public static void main(String[] args) {
// TODO Auto-generated method stub
System.out.println(Thread.currentThread().getName());
Callable<Object> oneCallable = new Tickets<Object>();
FutureTask<Object> oneTask = new FutureTask<Object>(oneCallable);
Thread t = new Thread(oneTask);
t.start();
}
}
class Tickets<Object> implements Callable<Object> {
// 重写call方法
@Override
public Object call() throws Exception {
// TODO Auto-generated method stub
System.out.println(Thread.currentThread().getName()
+ "-->我是通过实现Callable接口通过FutureTask包装器来实现的线程");
return null;
}
}
控制台输出:
main
Thread-0-->我是通过实现Callable接口通过FutureTask包装器来实现的线程
方式4:通过线程池创建线程
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadDemo4 {
private static int POOL_NUM = 10; // 线程池数量
public static void main(String[] args) throws InterruptedException {
// TODO Auto-generated method stub
System.out.println(Thread.currentThread().getName());
ExecutorService executorService = Executors.newFixedThreadPool(5);
for (int i = 0; i < POOL_NUM; i++) {
RunnableThread thread = new RunnableThread();
executorService.execute(thread);
}
// 关闭线程池
executorService.shutdown();
}
}
控制台输出:
main
通过线程池方式创建的线程:pool-1-thread-1
通过线程池方式创建的线程:pool-1-thread-3
通过线程池方式创建的线程:pool-1-thread-2
通过线程池方式创建的线程:pool-1-thread-4
通过线程池方式创建的线程:pool-1-thread-1
通过线程池方式创建的线程:pool-1-thread-5
通过线程池方式创建的线程:pool-1-thread-3
通过线程池方式创建的线程:pool-1-thread-3
通过线程池方式创建的线程:pool-1-thread-3
通过线程池方式创建的线程:pool-1-thread-3
ExecutorService、Callable都是属于Executor框架。返回结果的线程是在JDK1.5中引入的新特征,还有Future接口也是属于这个框架,有了这种特征得到返回值就很方便了。
通过分析可以知道,他同样也是实现了Callable接口,实现了Call方法,所以有返回值。这也就是正好符合了前面所说的两种分类
执行Callable任务后,可以获取一个Future的对象,在该对象上调用get就可以获取到Callable任务返回的Object了。get方法是阻塞的,即:线程无返回结果,get方法会一直等待。
再介绍Executors类:提供了一系列工厂方法用于创建线程池,返回的线程池都实现了ExecutorService接口。
- public static ExecutorService newFixedThreadPool(int nThreads)
创建固定数目线程的线程池。 - public static ExecutorService newCachedThreadPool()
创建一个可缓存的线程池,调用execute 将重用以前构造的线程(如果线程可用)。如果现有线程没有可用的,则创建一个新线程并添加到池中。终止并从缓存中移除那些已有 60 秒钟未被使用的线程。 - public static ExecutorService newSingleThreadExecutor()
创建一个单线程化的Executor。 - public static ScheduledExecutorService newScheduledThreadPool(int
corePoolSize)
创建一个支持定时及周期性的任务执行的线程池,多数情况下可用来替代Timer类。 - ExecutoreService提供了submit()方法,传递一个Callable,或Runnable,返回Future。如果Executor后台线程池还没有完成Callable的计算,这调用返回Future对象的get()方法,会阻塞直到计算完成。
java 多线程实现的四种方式的更多相关文章
- JAVA多线程实现的四种方式
Java多线程实现方式主要有四种:继承Thread类.实现Runnable接口.实现Callable接口通过FutureTask包装器来创建Thread线程.使用ExecutorService.Cal ...
- 【转】JAVA多线程实现的四种方式
原文地址:http://www.cnblogs.com/felixzh/p/6036074.html Java多线程实现方式主要有四种:继承Thread类.实现Runnable接口.实现Callabl ...
- JAVA多线程实现的四种方式(转自https://www.cnblogs.com/felixzh/p/6036074.html)
Java多线程实现方式主要有四种:继承Thread类.实现Runnable接口.实现Callable接口通过FutureTask包装器来创建Thread线程.使用ExecutorService.Cal ...
- 关于Java多线程(JAVA多线程实现的四种方式)
Java多线程实现方式主要有四种:继承Thread类.实现Runnable接口.实现Callable接口通过FutureTask包装器来创建Thread线程.使用ExecutorService.Cal ...
- JAVA解析XML的四种方式
java解析xml文件四种方式 1.介绍 1)DOM(JAXP Crimson解析器) DOM是用与平台和语言无关的方式表示XML文档的官方W3C标准.DOM是以层次结构组织的节点或信息片断的集合.这 ...
- java 遍历Map的四种方式
java 遍历Map的四种方式 CreationTime--2018年7月16日16点15分 Author:Marydon 一.迭代key&value 第一种方式:迭代entrySet 1 ...
- Java创建线程的四种方式
Java创建线程的四种方式 1.继承Thread类创建线程 定义Thread类的子类,并重写该类的run方法,run()方法的内容就是该线程执行的内容 创建Thread子类的实例,即创建了线程对象. ...
- 如何实现有返回值的多线程 JAVA多线程实现的三种方式
可返回值的任务必须实现Callable接口,类似的,无返回值的任务必须Runnable接口.执行Callable任务后,可以获取一个Future的对象,在该对象上调用get就可以获取到Callable ...
- java线程实现的四种方式
java多线程的实现可以通过以下四种方式 1.继承Thread类,重写run方法 2.实现Runnable接口,重写run方法 3.通过Callable和FutureTask创建线程 4.通过线程池创 ...
随机推荐
- Mybatis 单表 常用增删改查
1.编写sql表,插入原始数据 -- 删除表 DROP TABLE testA; -- 创建表 CREATE TABLE testA( id INT AUTO_INCREMENT PRIMARY KE ...
- Spring-quartz定时系统多任务配置
<!-- 启动触发器的配置开始 --> <bean name="startQuertz" lazy-init="false" autowire ...
- UVA10779 Collectors Problem 【迁移自洛谷博客】
这是一道不错的练最大流建模的基础题. 这种题目审题是关键. Bob's friends will only exchange stickers with Bob, and they will give ...
- Codeforces 958F2 Lightsabers (medium) 尺取法
题目大意: 输入n,m,分别表示人的个数和颜色的个数,下一行输入n个数,对应每个人的颜色,最后一行输入对应每个颜色的人应有的数量: 问是否能找出一个区间,满足条件但有多余的人,输出多余的人最少的个数, ...
- 前端每日实战:53# 视频演示如何用纯 CSS 创作一个文本淡入淡出的 loader 动画
效果预览 按下右侧的"点击预览"按钮可以在当前页面预览,点击链接可以全屏预览. https://codepen.io/comehope/pen/ERwpeG 可交互视频 此视频是可 ...
- js中require()的用法----JS如何连接数据库执行sql语句或者建立数据库连接池
var vue = require('vue'); 引入vue的意思,commonjs的写法.node都是用require来载入模块的,可以看看webpack+vue. require()可以调用模块 ...
- sublime text 3 快捷操作
快捷键:1.通用 ↑↓← → 上下左右移动光标 Alt 调出菜单 Ctrl + Shift + P 调出命令板(Command Palette) Ctrl + ` 调出控制台 2.编辑 Ctrl + ...
- docker Error response from daemon
docker 在拉取镜像的时候报错 Using default tag: latest Error response from daemon: Get https://registry-1.docke ...
- Jmeter下载文件和保存文件
Jmeter下载文件: 任意在网上搜索一张图片,地址为https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&a ...
- 十条服务器端优化Web性能的技巧
服务器 远程桌面连接工具 提高web应用的性能从来没有比现在更重要过.网络经济的比重一直在增长;全球经济超过5%的价值是在因特网上产生的(数据参见下面的资料).这个时刻在线的超连接世界意味着用户对其 ...