Java多线程/并发01、新建线程的3种方法
来源:互联网 发布:怎样申请淘宝店 编辑:程序博客网 时间:2024/06/07 06:36
引子
首先要理解并发(Concurrency)和并行(Parallelism)的区别:
- 并发是在同一时段发生。多线程就是分时利用CPU,宏观上让所有线程一起执行,也叫并发。但在微观上不是同时执行的,只是把时间分成若干段,使多个进程快速交替的执行。这就好像两个人用同一把铁锨,轮流挖坑,一小时后,两个人各挖一个小一点的坑,要想挖两个大一点得坑,一定会用两个小时。
- 并行是在同一时刻发生。无论从微观还是宏观,二者都是一起执行的,就好像两个人各拿一把铁锨在挖坑,一小时后,每人一个大坑。
从以上本质不难看出,“并发”执行,在多个进程存在资源冲突时,并没有从根本提高执行效率。
异步和多线程也不是一个同等关系,异步是最终目的,多线程只是我们实现异步的一种手段。异步是当一个调用请求发送给被调用者,而调用者不用等待其结果的返回而可以做其它的事情。实现异步可以采用多线程技术。
下面看看新建线程的3种方法 :
一、继承Thread,实现run()方法
只要两步即可创建并开启一个线程:
继承Thread类,并实现run()方法;
调用start()方法开启线程。
package twm.JConcurrence.demo;public class NewThreadDemo { public static void main(String[] args) { /*在主线程中开启10个线程,每个线程需要耗时2秒。但因为线程并发,所以总体运行时间也在2秒左右,并非20秒。*/ for (int i = 0; i < 10; i++) { demothread abc= new demothread("chapter1_thread"+String.valueOf(i)); abc.start(); } } static class demothread extends Thread { String put; public demothread(String name) { super(name); this.put = name; } /*覆写run()方法*/ public void run() { try { /*模拟耗时操作,让线程休眠2秒*/ Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } System.out .println("Class name is : "+this.put+",thread name is :" + Thread.currentThread().getName()); } }}
输出:
Class name is : chapter1_thread0,thread name is chapter1_thread0
Classname is : chapter1_thread2,thread name is chapter1_thread2
Class name is : chapter1_thread1,thread name is chapter1_thread1
Class name is : chapter1_thread4,thread name is chapter1_thread4
Class name is : chapter1_thread6,thread name is chapter1_thread6
Class name is : chapter1_thread8,thread name is chapter1_thread8
Class name is : chapter1_thread9,thread name is chapter1_thread9
Class name is : chapter1_thread3,thread name is chapter1_thread3
Class name is : chapter1_thread7,thread name is chapter1_thread7
Class name is : chapter1_thread5,thread name is chapter1_thread5
由于只要实现一个run()方法即可,所以可以改写一下上面程序,让代码更简洁。
使用Java中的匿名内部类(原型:new Thread(){ void run{ 实现 } }.start()
)来实现,代码如下:
package JConcurrence.Study;public class chapter1 { public static void main(String[] args) { for (int i = 0; i < 10; i++) { final int m=i; new Thread(){ /*覆写run()方法*/ public void run() { try { /*模拟耗时操作,让线程休眠2秒*/ Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } System.out .println("thread name is" + Thread.currentThread().getName()+"输出:"+m); } }.start(); } }}
在for循环体内加了一句final int m=i;这是因为使用了匿名内部类,在类的内部隐式调用外部变量i,外部变量需要final修饰,不可修改值。否则会报错:Cannot refer to a non-final variable i inside an inner class defined in a different method
二 、实现Runnable接口
同样只要两步即可创建并开启一个线程:
创建一个实现了Runnable接口的类,并重写run()方法;
将该类的实例化对像作为参数传入Thread类的构造方法中,并调用Thread类的start()方法启动。
package JConcurrence.Study;public class chapter1 { public static void main(String[] args) { /*在主线程中开启10个线程,每个线程需要耗时2秒,但因为线程并发,所以总体运行时间也在2秒左右,并非20秒。*/ for (int i = 0; i < 10; i++) { Runnable demoRunnable=new DemoRunable("chapter1runable"+String.valueOf(i)); new Thread(demoRunnable).start(); } } static class DemoRunable implements Runnable { String put; public DemoRunable(String name) { super(); this.put = name; } /*覆写run()方法*/ public void run() { try { /*模拟耗时操作,让线程休眠2秒*/ Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } System.out .println("Class name is : "+this.put+",thread name is" + Thread.currentThread().getName()); } }}
由于只要实现Runnable的一个run()方法,所以可以改写一下上面程序,让代码更简洁。
使用Java中的匿名内部类(原型:new Thread( new Runnable(){ void run{ 实现 } } ).start()
)来实现,代码如下:
package JConcurrence.Study;public class chapter1 { public static void main(String[] args) { /* 在主线程中开启10个线程,每个线程需要耗时2秒,但因为线程并发,所以总体运行时间也在2秒左右,并非20秒。 */ for (int i = 0; i < 10; i++) { new Thread(new Runnable() { @Override public void run() { try { /* 模拟耗时操作,让线程休眠2秒 */ Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("thread name is" + Thread.currentThread().getName()); } }).start(); } }}
三、实现Callable接口
使用方法和Runable类似,但应用场景不同,会在后面的Callable和Future接口中详细说到Callable。
Runnable和Callable接口的区别:
- Callable重写的方法是call(),Runnable重写的方法是run();
- Callable的任务执行后可返回值,而Runnable不能返回值;
- call方法可以抛出异常,run()不可以;
- 运行Callable任务可以拿到一个future对象,表示异步计算的结果,
它供检查计算是否完成的方法,以等待计算完成,并检索计算的结果。通过Future对象可以了解任务的执行情况,可取消任务的执行,还可以获取执行的结果。
四、总结Run()方法
无论继承Thread类还是实现Runnable接口,代码基本高度一致,核心就是在run()方法的实现中。
通过Thread源码中run()方法,可以看到它自己并不做什么(只是简单的判断一下是否可以调用),而是调用了target的run()方法
@Overridepublic void run() { if (target != null) { target.run(); }}
这个target到底是哪位大侠呢?跟踪发现是这样定义的:
private Runnable target;target就是Runnable。Runable的定义是:@FunctionalInterfacepublic interface Runnable { public abstract void run();}
我了个去,满山遍野都是run,是不是感觉有零乱。
其实梳理一下就很好理解了。
1、如果我们要让Thread听我们指挥,做我们安排的工作,那么就直接重写Thread类run函数。甭管他里面什么target.run乱七八糟的,你自己想怎么写就怎么写,空着也行。这就是“1、继承Thread,实现run()方法”说讲的内容。
2、如果我们实现Runable接口,并重写他的run方法。而后将这个对像作为参数传入Thread,那么在Thread类内部,会把该参数赋值到target。线程启动后调用Thread.run时运行的代码是target.run(),最终调用了runable实现类中的run方法。所以可以你在runable中的run方法中自己想怎么写就怎么写,空着也行。这就是“实现Runnable接口”讲的内容
3、如果有个哥们他硬要把两样都加上,即:同时把将runable传入Thread,然后又实现Thread中的run方法,那么程序最终会运行runable中的run(),还是Thread中的run()呢?….舌头差点打结了。很明显因为重写Thread中的run方法后,就不会再执行target.run了。因此无论你传什么runable进来,Thread都不会理睬了。看代码:
package JConcurrence.Study;public class chapter1 { public static void main(String[] args) { new Thread(new Runnable() { @Override public void run() { try { /* 模拟耗时操作,让线程休眠2秒 */ Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Executive agent:Runable 's run()"); } }) { public void run() { try { /* 模拟耗时操作,让线程休眠2秒 */ Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } System.out .println("Executive agent:Thread 's Override run()"); } }.start(); }}
执行结果:
Executive agent:Thread ‘s Override run()
- Java多线程/并发01、新建线程的3种方法
- Java新建线程的3种方法
- Java新建线程的3种方法
- Java多线程/并发02、线程的五种状态
- Java并发线程--多线程的创建
- java并发多线程,线程的创建启动
- (java多线程并发)控制并发线程数的Semaphore
- 【Java并发编程】实现多线程的两种方法
- 【java并发】基础(1)--创建线程的两种方法
- Java多线程/并发03、实现定时任务的3种方法
- Qt新建线程的方法
- Qt 新建线程的方法
- Java多线程的深入讲解 -- 并发库,线程池等
- Java面试题--多线程、并发及线程的基础问题
- 【Java多线程与并发库】8.java5线程并发库之线程池的应用
- 【Java多线程与并发库】8.java5线程并发库之线程池的应用
- java多线程㈠—实现线程的两种方法
- Java多线程 创建线程的两种方法
- Prometheus 实战于源码分析之webHandler
- 数据结构—栈
- 单应性变换(Homography)
- PHP设计模式系列(一):策略模式
- 除了cocoapods导入工程中以外的方法,尤其针对直接拖拽到工程里报错的三方库
- Java多线程/并发01、新建线程的3种方法
- hdu 5128 The E-pang Palace(几何暴力)
- 解决GAT项目Bug:ETC费用统计问题
- 冒泡算法和交换算法
- ToolBar初步
- 输出eclipse控制台日志到指定目录
- hdu 2955_背包经典
- linux sed查询满足条件在2行记录
- shell脚本中单引号和双引号的区别