Java线程的创建方法
来源:互联网 发布:wap论坛源码 编辑:程序博客网 时间:2024/05/01 19:18
在实际的多线程编程中,有哪些创建线程的方式?本文从JDK提供的各种API进行讲述,水平有限,还请指正。
1 基础方式
之所以称之为基础方式,是因为后面的方式都是基于基础方式,做一些封装完成的。很多文章也称之为传统方式,主要是相对于JDK1.5之后的并发工具包而言。
1.1 继承Thread类
继承Thread类,重写run方法,将线程的执行逻辑放到run方法中,完毕!通过实例的start方法启动。
public class MyThread extends Thread{ @Override public void run(){ System.out.println("拜托,你倒是在这写上业务逻辑啊!"); } public static void main(String[] args) { new MyThread().start(); }}output:拜托,你倒是在这写上业务逻辑啊!
1.2 实现Runnable接口
实现Runnable接口,并将其实例mt作为参数传入到Thread的构造函数中生成Thread实例。start方法实际上是调用了mt的run方法。
public class MyThread implements Runnable(){ @Override public void run(){ System.out.println("拜托,你倒是在这写上业务逻辑啊!"); } public static void main(String[] args){ MyThread mt = new MyThread(); new Thread(mt).start(); }}output:拜托,你倒是在这写上业务逻辑啊!
1.3 两者对比
(1)继承性:Java是单继承机制,所以采用 1.1 形式创建线程后,则不能再继承其它类,而 1.2 形式则仍可以继承其它类。
(2)共享性:在多线程开发中,每个线程都可以传入同一个Runnable实例,以达到资源共享的目的,并且可以在该共享实例中做好并发的逻辑控制。
2 线程工具类创建
工具类创建线程的方式,底层还是由上述基础方式实现的。不同的需求诞生不同的工具,使用工具类主要有以下好处。
(1)满足特定需求。开发者可以根据不同的应用场景选择不同的线程工类。
(2)简化编程模型。不用从基础方式开始构建模型,实际应用往往是多线程的,因此用基础方式创建的线程,开发者经常要为共享资源(例如,抢火车票,票数就是所有用户共享的)设计并发策略,并且要进行大量测试,而工具类则是已经经过验证的,可靠性高。
(3)封装线程生命周期。线程工具类中,更多关注的是可执行的任务,任务是更容易理解的抽象概念,开发者将很少关注线程的各种状态(例如中断、阻塞、就绪等状态)了,工具类已经这些逻辑封装好,暴露给开发者的是任务。
2.1 Timer和TimerTask
Timer和TimerTask位于java.util包下,是JDK1.3提供的。Timer是定时器类,主要是负责调度,TimerTask则是实现了Runnable接口的抽象类,是负责执行任务的类,被Timer调度。
public class Timer{ ...... //Timer类中有一个私有线程实例,用于调用Runnable实例 private TimerThread thread = new TimerThread(queque) ......}public abstract class TimerTask implements Runnable{...}
我认为该组合也是线程的一种创建方式,不过更符合特定的应用场景,如定时、周期执行等,所以列入文章。
public class Test{ public static void main(String[] args){ //调度器 Timer timer = new Timer(); //被调度的任务 TimerTask task = new TimerTask(){ @Override public void run(){ System.out.println("Do my task!"); } }; //执行调度方法,1秒后执行任务 timer.schedule(task, 1000); }}output:Do my task!
2.2 Callable、Future和FutureTask
通过基础方式创建线程的一个不足之处就是:无法直接获得(可以通过共享变量等方式间接获得)执行的结果,因为run方法是void类型的。
public interface Runnable{ public abstract void run();}
而在JDK1.5后提供的Callable接口和Future接口则弥补了这一缺陷。线程执行结果由Future的实例接收,通过get方法获取。V表示的是返回类型,可在实现接口时自定义。
public interface Callable<V>{ V call() throws Exception;}public interface Future<V>{ ...... V get() throws InterruptedException,ExecutionException; ......}
Callable和Future是一对儿,那FutureTask又是个啥呢?咱先理解字面意思,回顾上面,TimerTask和FutureTask,很明显,都是Task,负责执行任务的类。那么FutureTask怎么被调用呢?先看定义:
public interface RunnableFuture<V> extends Runnable,Future<V>{...}public class FutureTask<V> implements RunnableFuture<V>{...... //constructor Future和Callable是一对儿~ public FutureTask(Callable<V> callable){...}......}
原来FutureTask终究也是个Runnable,所以在调度的角色中,属于执行任务的类。但是FutureTask和普通的任务执行类有些区别,它是既可执行,又可获取执行结果,因为它实现了Future接口。
既然FutureTask是个Runnable,参照1.2的形式,可以直接传入Thread的构造函数中。被Thread实例直接调用了,如下。
public class CallableThreadTest{ public static void main(String args[]){ //创建Callable实例 Callable<Integer> ct = new Callable<Integer>(){ @Override public Integer call(){ System.out.println(Thread.currentThread().getName() + "执行中"); return 100; } }; //封装Callable实例 FutureTask<Integer> task = new FutureTask(ct); new Thread(task,"有返回值的线程").start(); //输出任务执行的返回结果 try{ System.out.println(task.get()); }catch(Exception e){ e.printStackTrace(); } }}output:有返回值的线程执行中100
上面的调用顺序是:Thread的start -》FutureTask的run-》Callable的call。细节就不多讲了,本文主要是介绍线程创建方式。
2.3 线程池
在实际应用中经常是多个线程并发执行任务的,为了节省创建线程和销毁线程的时间,当需要执行任务的时候,从池里取一个线程,把任务传给线程,去执行就Okay了,任务执行完了,该线程暂不销毁,返回到池里,等待下一个执行任务。
Executors类提供了四种创建线程池的静态方法,如下。
Executors.newFixedThreadPool(int nThreads) Executors.newCachedThreadPool() Executors.newSingleThreadExecutor() Executors.newScheduledThreadPool(int corePoolSize)
那么问题来了,本文的主题是:如何创建线程?线程池中预先创建好的一些Thread实例,某种意义上只能算是空创建,因为执行任务的逻辑并没有交给Thread,需要传入进去,该线程实例才赋予了意义。
线程池提交任务方法如下,第四种不常用。
public void execute(Runnable task)public Future<?> submit(Runnable task)public <T>Future<T> submit(Callable task)public <T>Future<T> submit(Runnable task, T result)
很明显,提交的任务有两种,一种是无返回结果的,如下。
//创建线程池ExecutorService pool = Executors.newCachedThreadPool();//创建任务Runnable task = new Runnable(){ @Override public void run(){ System.out.println("一去不复还"); }};//提交任务pool.execute(task);output:一去不复还
另一种是有返回结果的,如下。
//创建线程池ExecutorService pool = Executors.newCachedThreadPool();//创建任务Callable<Integer> task = new Callable<Integer>(){ @Override public Integer call(){ System.out.println("线程池调用我了!给你100块!"); return 100; } };//提交任务Future<Integer> future = pool.submit(task);//获取任务执行结果try { System.out.println(future.get());} catch (Exception e) { e.printStackTrace();}output:线程池调用我了!给你100块!100
至此,总结完毕,如有描述不当,或者更好的表达方式,欢迎交流。转载请注明出处:http://blog.csdn.net/chen6581669/article/details/51579644
- Java 线程的创建方法
- Java 创建线程的方法
- java 线程创建的方法
- Java线程的创建方法
- Java 创建线程的方法
- java中创建线程的的方法
- Java线程创建方法
- java线程创建方法
- Java创建线程的两种方法
- JAVA 创建线程的2种方法
- Java创建线程的两种方法
- Java创建线程的两种方法
- Java创建线程的两个方法
- java线程的两种创建方法
- java创建线程的两个方法
- Java创建线程的两个方法
- java创建线程的两个方法
- Java创建线程的两种方法
- WINDOWS下cocostudio头文件报错解决办法
- Git命令操作GitHub和Visual Studio Online
- 计时器定时播放音乐
- Android系统架构中常用模块清单
- java swing 隐藏jtable 列的方法
- Java线程的创建方法
- 冒泡排序
- Spring整合JMS基于ActiveMQ实现
- UNIX网络编程笔记(4)—TCP客户/服务器程序示例
- 重构
- version `GFORTRAN_1.4' not found (required by /usr/lib/liblapack.so.3) 问题的解决方案
- ionic提示/usr/bin/env: node: 没有那个文件或目录
- 华为机试---计算日期到天数转换
- redis集群扩容