详解三种java实现多线程的方式
java中实现多线程的方法有两种:继承Thread类和实现runnable接口。
1.继承Thread类,重写父类run()方法
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
public class thread1 extends Thread { public void run() { for (int i = 0; i < 10000; i++) { System.out.println("我是线程"+this.getId()); } } public static void main(String[] args) { thread1 th1 = new thread1(); thread1 th2 = new thread1(); th1.run(); th2.run(); } } |
run()方法只是普通的方法,是顺序执行的,即th1.run()执行完成后才执行th2.run(),这样写只用一个主线程。多线程就失去了意义,所以应该用start()方法来启动线程,start()方法会自动调用run()方法。上述代码改为:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
public class thread1 extends Thread { public void run() { for (int i = 0; i < 10000; i++) { System.out.println("我是线程"+this.getId()); } } public static void main(String[] args) { thread1 th1 = new thread1(); thread1 th2 = new thread1(); th1.start(); th2.start(); }} |
通过start()方法启动一个新的线程。这样不管th1.start()调用的run()方法是否执行完,都继续执行th2.start()如果下面有别的代码也同样不需要等待th2.start()执行完成,而继续执行。(输出的线程id是无规则交替输出的)
2.实现runnable接口
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
public class thread2 implements Runnable { public String ThreadName; public thread2(String tName){ ThreadName = tName; } public void run() { for (int i = 0; i < 10000; i++) { System.out.println(ThreadName); } } public static void main(String[] args) { thread2 th1 = new thread2("线程A"); thread2 th2 = new thread2("线程B"); th1.run(); th2.run(); }} |
和Thread的run方法一样Runnable的run只是普通方法,在main方法中th2.run()必须等待th1.run()执行完成后才能执行,程序只用一个线程。要多线程的目的,也要通过Thread的start()方法(注:runnable是没有start方法)。上述代码修改为:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
public class thread2 implements Runnable { public String ThreadName; public thread2(String tName){ ThreadName = tName; } public void run() { for (int i = 0; i < 10000; i++) { System.out.println(ThreadName); } } public static void main(String[] args) { thread2 th1 = new thread2("线程A"); thread2 th2 = new thread2("Thread-B"); Thread myth1 = new Thread(th1); Thread myth2 = new Thread(th2); myth1.start(); myth2.start(); }} |
3.使用ExecutorService、Callable、Future实现有返回结果的多线程(JDK5.0以后)
可返回值的任务必须实现Callable接口,类似的,无返回值的任务必须Runnable接口。执行Callable任务后,可以获取一个Future的对象,在该对象上调用get就可以获取到Callable任务返回的Object了,再结合线程池接口ExecutorService就可以实现传说中有返回结果的多线程了。下面提供了一个完整的有返回结果的多线程测试例子,在JDK1.5下验证过没问题可以直接使用。代码如下:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
|
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 + "毫秒】"; } } |
代码说明:
上述代码中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多线程的2种方式,runable是接口,thread是类,runnable只提供一个run方法,建议使用runable实现
java多线程,不管如何,最终都需要通过thread.start()来使线程处于可运行状态。第三种方法是听群里的兄弟们介绍的,所以就百度补上了。
以上就是本文的全部内容,希望对大家的学习有所帮助。
详解三种java实现多线程的方式的更多相关文章
- 详解三种缓存过期策略LFU,FIFO,LRU(附带实现代码)
在学操作系统的时候,就会接触到缓存调度算法,缓存页面调度算法:先分配一定的页面空间,使用页面的时候首先去查询空间是否有该页面的缓存,如果有的话直接拿出来,如果没有的话先查询,如果页面空间没有满的时候, ...
- Spring事务之详解--三种实现方式
实现购买股票案例: 一.引入JAR文件: 二.开始搭建分层架构---创建账户(Account)和股票(Stock)实体类 Account: /* * 账户 */ public class Accoun ...
- Java 枚举(enum) 详解7种常见的用法
Java 枚举(enum) 详解7种常见的用法 来源 https://blog.csdn.net/qq_27093465/article/details/52180865 JDK1.5引入了新的类型— ...
- Java网络编程和NIO详解开篇:Java网络编程基础
Java网络编程和NIO详解开篇:Java网络编程基础 计算机网络编程基础 转自:https://mp.weixin.qq.com/s/XXMz5uAFSsPdg38bth2jAA 我们是幸运的,因为 ...
- Java网络编程和NIO详解5:Java 非阻塞 IO 和异步 IO
Java网络编程和NIO详解5:Java 非阻塞 IO 和异步 IO Java 非阻塞 IO 和异步 IO 转自https://www.javadoop.com/post/nio-and-aio 本系 ...
- 转:Windows下的PHP开发环境搭建——PHP线程安全与非线程安全、Apache版本选择,及详解五种运行模式。
原文来自于:http://www.ituring.com.cn/article/128439 Windows下的PHP开发环境搭建——PHP线程安全与非线程安全.Apache版本选择,及详解五种运行模 ...
- Java网络编程和NIO详解2:JAVA NIO一步步构建IO多路复用的请求模型
Java网络编程与NIO详解2:JAVA NIO一步步构建IO多路复用的请求模型 知识点 nio 下 I/O 阻塞与非阻塞实现 SocketChannel 介绍 I/O 多路复用的原理 事件选择器与 ...
- 图文详解两种算法:深度优先遍历(DFS)和广度优先遍历(BFS)
参考网址:图文详解两种算法:深度优先遍历(DFS)和广度优先遍历(BFS) - 51CTO.COM 深度优先遍历(Depth First Search, 简称 DFS) 与广度优先遍历(Breath ...
- Android 之窗口小部件详解(三) 部分转载
原文地址:http://blog.csdn.net/iefreer/article/details/4626274. (一) 应用程序窗口小部件App Widgets 应用程序窗口小部件(Widget ...
随机推荐
- Luogu P2624 [HNOI2008]明明的烦恼 Prufer+组合+高精
好的我把标准版过了... 设$ r_i$为$i$的度数 首先,我们设 $ sum = \Sigma r_i-1$,$ tot $ 为所有能够确定度数的点 所以我们有 $ C ^ {sum} _{n-2 ...
- spring不同环境下用不同的配置文件
@Configuration @PropertySource("xx-${spring.profiles.active}.properties") public class Top ...
- ip route,ip rule, iptables和docker的端口映射
iptables 默认5个表, 不可增加其他表 raw 用于配置数据包,raw 中的数据包不会被系统跟踪. filter 是用于存放所有与防火墙相关操作的默认表. nat 用于 网络地址转换(例如:端 ...
- Subversion Server Edge用户权限设置简介
Subversion Server Edge用户权限可分为两种,一种为按用户权限,一种为按组权限设置 1.按用户设置权限 [codeLibrary:/] //对整个代码库 *=r //所有用户 ...
- (转)Linux之split命令详解
Linux之split命令详解 原文:http://m.jb51.net/article/73632.htm Linux split命令用于将一个文件分割成数个,该指令将大文件分割成较小的文件,在默认 ...
- Spark编程模型(下)
创建Pair RDD 什么是Pair RDD 包含键值对类型的RDD类型被称作Pair RDD: Pair RDD通常用来进行聚合计算: Pair RDD通常由普通RDD做ETL转化而来. Pytho ...
- 解决Spring JdbcTemplate调用queryForObject()方法结果集为空时报异常
JdbcTemplate用的时候发现一个问题:调用queryForObject()方法,如果没有查到东西则会抛一个异常:org.springframework.dao.EmptyResultDataA ...
- 关于IE的一些hack
TIPS:对于完全放弃IE的幸福开发者,以下内容全是废话,建议跳转到 博客园 着页,寻找更优质的文章. 对于前端开发来说IE一直是心里的痛,不管你觉得做的多好的网页,放到它上面总会有一些意想不到的问题 ...
- mysql登陆远程数据库
1.登陆mysql 2.e mysql; 3.比如用户名密码为root/root. 你想root使用root从任何主机连接到mysql服务器的话. @’ ’后面加ip地址一般般为localhost或者 ...
- Google常用拓展插件
1.web前端助手(FEhelper)提供一些实用的前端小工具,功能十分贴心 2.bookMarks Manager 一个书签管理工具 3.Clear Cache 清除浏览器的缓存,有很多供选择的条目 ...