java实现/创建线程的几种方式小结
进程与线程
进程可以简单理解成一个可执行程序例如.exe,在Windows中的任务管理器中可以查看每一个进程,进程是一次程序的执行,是程序在数据集合上运行的过程,是系统资源调度的一个单位。进程主要负责向操作系统申请资源。然而一个进程中,多个线程可以共享进程中相同的内存或文件资源。线程就是一个进程一个程序要完成所依赖的子任务,这些子任务便可以看作是一个线程。
第一种方式继承Thread类
从java源码可以看出Thread类本质上实现了Runnable接口的实例类,代表了线程的一个线程的实例,启动的线程唯一办法就是通过Thread类调用start()方法,start()方法是需要本地操作系统的支持,它将启动一个新的线程,并且执行run()方法。
继承Thread类实现线程代码如下
创建一个Thread类,对象直接调用run方法会出现什么问题?
package cn.thread.线程; public class MyThread extends Thread{ public MyThread(String name){ super(null,null,name); } int piao =10; @Override public void run() { while(piao>0){ System.out.println(Thread.currentThread().getName()+"......"+piao--); } } public static void main(String[] args) { MyThread mt = new MyThread("x"); mt.run(); } }
结果:
可以发现是主线程执行了run方法,并不是用户线程执行的run方法,此时可以得出用户线程并没有启动,所以并不会执行run里面的方法,且执行完run方法便结束线程。
第二种创建线程的方法,实现Runnable接口
相比继承Thread类而言,实现接口的可扩展性得到了提升,Runnable接口也必须要封装到Thread类里面,才可以调用start方法,启动线程。
实现代码
package cn.thread.线程; public class MyRunnable implements Runnable{ int piao = 10; @Override public void run() { while(piao>0){ System.out.println(Thread.currentThread().getName()+"-----"+piao--); } } public static void main(String[] args) { Runnable r =new MyRunnable(); Thread t =new Thread(r); t.start(); } }
结果
第三种创建线程的方法实现Callable接口
Callable接口使用方法和Runnable接口的方法类似不同的一点是Callable接口具有返回值,返回结果并且可能抛出异常的任务。实现者定义了一个不带任何参数的叫做 call 的方法。 Callable 接口类似于 Runnable,两者都是为那些其实例可能被另一个线程执行的类设计的。但是 Runnable 不会返回结果,并且无法抛出经过检查的异常。
实现代码
对下列代码进行分析
首先callable是接口不能直接创建对象,也不能创建线程。并且要实现call方法类似run方法的功能,call方法有返回值,会计算结果,如果无法计算结果,则抛出一个异常。
执行callable任务之后,可以获得一个future的对象,future基本上是主线程可以跟踪进度以及获取其他线程结果的一种方式。在这里Test1()方法主要利用线程池和future的方法,去启动实现Callable接口的线程,具有返回值。而Test2()主要是采用FutureTask类去实现创建一个实现callable接口的线程,futuretask实现了future接口。
package com.openlab.test; import java.util.Random; import java.util.concurrent.Callable; public class CallableTest implements Callable{ @Override public Object call() throws Exception { Random generator = new Random(); Integer randomNumber = generator.nextInt(5); Thread.sleep(randomNumber*1000); return randomNumber; } }
综合练习代码:
package cn.thread.线程; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Random; import java.util.concurrent.*; public class CallableTest implements Callable<Object> {//不能直接创建线程 int taskNum; public CallableTest(int taskNum){ this.taskNum = taskNum; } @Override public Object call() throws Exception { System.out.println(">>>"+taskNum+"任务启动"); Date dataTemp = new Date(); Thread.sleep(1000); Date dataTemp2 = new Date(); long time = dataTemp2.getTime() - dataTemp.getTime(); System.out.println(">>>>"+taskNum+"任务终止"); return taskNum+"任务返回运行结果"+time; // Random generator = new Random(); // Integer randomNumber = generator.nextInt(5); // Thread.sleep(randomNumber*1000); // return randomNumber; } /*test1方法采用Executors的静态方法newFixedThreadPool(taskSize) 创建一个可重用固定线程集合的 线程池,以共享的无界队列方式来运行这些线程,获取线程池。ExecutorService的submit方法提交一个 callable实例,得到一个future对象,最终将future对象存储在list数组中,加入线程池的过程中就代表 着线程已经开始执行,相当于一个线程池代理过程,就不需要采用start方法启动线程。最后对future进行 打印输出。切记一定要关闭线程池!*/ static void test1() throws ExecutionException, InterruptedException { System.out.println("程序开始"); Date data1 = 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 CallableTest(i); Future f = pool.submit(c); list.add(pool.submit(c)); } //关闭线程池 pool.shutdown(); for(Future f:list){ System.out.println(">>>"+f.get().toString()); } Date date2 = new Date(); System.out.println("程序运行结束-----"+(date2.getTime()-data1.getTime())+"毫秒"); } /*test2方法主要是采用futuretask类,可以直接把callable作为参数来申明futuretask对象, 这里相当于把线程池换成了futuretask数组,因为test1线程池可以对callable进行封装, 在这里可以直接采用futuretask就行封装,在加上futuretask又实现了runnable接口, 所以可以直接创建线程采用start的方式进行启动线程。*/ static void test2() throws ExecutionException, InterruptedException { System.out.println("----程序开始-----"); Date date1 =new Date(); int taskSize = 5; FutureTask[] randNumber = new FutureTask[taskSize]; List<Future> list =new ArrayList<Future>(); for(int i=0;i<taskSize;i++){ Callable c = new CallableTest(i); randNumber[i] = new FutureTask(c); Thread t = new Thread(randNumber[i]); t.start(); } for(Future f:randNumber){ System.out.println(">>>"+f.get().toString()); } Date date2 = new Date(); System.out.println("程序运行结束-----"+(date2.getTime()-date1.getTime())+"毫秒"); } public static void main(String[] args) throws Exception { // CallableTest c = new CallableTest(); // Integer i = (Integer) c.call(); test1(); test2(); } }
执行结果
第四种实现线程的方法,基于线程池
其实在第三种的方法中就提到了两种实现方法,一种线程池+future,另一种futuretask的方法。线程和数据库连接这些资源都是非常宝贵的资源。那么每次需要的时候创建,不需要的时候销毁,是非常浪费资源的。那么我们就可以使用缓存的策略,也就是使用线程池。
// 创建线程池 ExecutorService threadPool = Executors.newFixedThreadPool(10); while(true) { threadPool.execute(new Runnable() { // 提交多个线程任务,并执行 @Override public void run() { System.out.println(Thread.currentThread().getName() + " is running .."); try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } } }); } }
总结
理论上实现线程的方法还有一些,本文所提及到的,基本都是一些创建线程常用的方法。希望本文对大家在学习线程的过程中有所帮助。
相关文章
- 这篇文章主要介绍了如何利用java语言实现经典《复杂迷宫》游戏,文中采用了swing技术进行了界面化处理,感兴趣的小伙伴可以动手试一试...2022-02-01
C# WinForm多线程解决界面卡死问题的完美解决方案,使用BeginInvoke
问题描述:当我们的界面需要在程序运行中不断更新数据时,当一个textbox的数据需要变化时,为了让程序执行中不出现界面卡死的现像,最好的方法就是多线程来解决一个主线程来创建界...2020-06-24java 运行报错has been compiled by a more recent version of the Java Runtime
java 运行报错has been compiled by a more recent version of the Java Runtime (class file version 54.0)...2021-04-01- 这篇文章主要介绍了在java中获取List集合中最大的日期时间操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-08-15
- 这篇文章主要介绍了教你怎么用Java获取国家法定节假日,文中有非常详细的代码示例,对正在学习java的小伙伴们有非常好的帮助,需要的朋友可以参考下...2021-04-23
- 这篇文章主要介绍了Java如何发起http请求的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-03-31
- 这篇文章主要介绍了C#停止线程的方法,实例分析了C#正确停止线程的实现技巧,具有一定参考借鉴价值,需要的朋友可以参考下...2020-06-25
- 今天小编就为大家分享一篇关于C#开启线程的四种方式示例详解,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧...2020-06-25
- 说起C#和Java这两门语言(语法,数据类型 等),个人以为,大概有90%以上的相似,甚至可以认为几乎一样。但是在工作中,我也发现了一些细微的差别...2020-06-25
- 这篇文章主要介绍了解决Java处理HTTP请求超时的问题,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2021-03-29
- 这篇文章主要介绍了C# 线程相关知识,文中讲解非常细致,代码帮助大家更好的理解和学习,感兴趣的朋友可以了解下...2020-11-03
- 这篇文章主要介绍了c# 多线程处理多个数据的方法,帮助大家更好的理解和学习使用c#,感兴趣的朋友可以了解下...2021-03-31
- 这篇文章主要介绍了C#实现跨线程操作控件方法,主要采用异步访问方式实现,需要的朋友可以参考下...2020-06-25
- 这篇文章主要介绍了java 判断两个时间段是否重叠的案例,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-08-15
java 画pdf用itext调整表格宽度、自定义各个列宽的方法
这篇文章主要介绍了java 画pdf用itext调整表格宽度、自定义各个列宽的方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2021-01-31- 这篇文章主要介绍了超简洁java实现双色球若干注随机号码生成(实例代码),本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2021-04-02
- 这篇文章主要介绍了Java生成随机姓名、性别和年龄的实现示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-10-01
- 这篇文章主要介绍了C#基于委托实现多线程之间操作的方法,实例分析了C#的委托机制与多线程交互操作的相关技巧,具有一定参考借鉴价值,需要的朋友可以参考下...2020-06-25
- 这篇文章主要介绍了java正则表达式判断前端参数修改表中另一个字段的值,需要的朋友可以参考下...2021-05-07
Java使用ScriptEngine动态执行代码(附Java几种动态执行代码比较)
这篇文章主要介绍了Java使用ScriptEngine动态执行代码,并且分享Java几种动态执行代码比较,需要的朋友可以参考下...2021-04-15