java:多线程基础之Runnable、Callable与Thread
java.lang包下有二个非常有用的东西:Runnable接口与Thread类,Thread实现了Runnable接口(可以认为Thread是Runnable的子类),利用它们可以实现最基本的多线程开发。
一、Runnable入门示例
public class RunnableDemo1 { public static void main(String[] args) {
new Runnable() {
public void run() {
for (int i = 0; i < 5; i++) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("r1 -> i=" + i);
} }
}.run(); new Runnable() {
public void run() {
for (int i = 0; i < 5; i++) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("r2 -> i=" + i);
}
}
}.run(); } }
代码很简单,每个线程依次输出0-4这5个数字,运行结果:
r1 -> i=0
r1 -> i=1
r1 -> i=2
r1 -> i=3
r1 -> i=4
r2 -> i=0
r2 -> i=1
r2 -> i=2
r2 -> i=3
r2 -> i=4
二、向Runnable传递参数
实际应用中,线程开始处理前,通常会有一些初始参数,如果要传入参数,可以参考下面的方法,先定义一个Runnable的子类
package com.cnblogs.yjmyzz; public class MyRunnable implements Runnable{ private String name;
private int max; public MyRunnable(String name,int max){
this.name = name;
this.max = max;
} public void run() {
for (int i = 1; i <= max; i++) {
try {
Thread.sleep(5);
System.out.println(name + ".i=" + i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} }
然后这样使用:
package com.cnblogs.yjmyzz; public class RunnableDemo2 { public static void main(String[] args) { new MyRunnable("A", 5).run(); new MyRunnable("B", 5).run();
} }
运行结果:
A.i=1
A.i=2
A.i=3
A.i=4
A.i=5
B.i=1
B.i=2
B.i=3
B.i=4
B.i=5
三、利用Thread并行处理
刚才的二个例子,相当大家也发现了问题,虽然是有二个线程,但是始终是按顺序执行的,上一个线程处理完成前,下一个线程无法开始,这其实跟同步处理没啥二样,可以通过Thread类改变这种局面:
public class RunnableDemo3 { public static void main(String[] args) { Runnable r1 = new MyRunnable("A", 5);
Runnable r2 = new MyRunnable("B", 5); Thread t1 = new Thread(r1);
Thread t2 = new Thread(r2); t1.start();
t2.start(); } }
Thread通过start方法,可以让多个线程并行处理,运行结果如下:
B.i=1
A.i=1
B.i=2
A.i=2
B.i=3
A.i=3
B.i=4
A.i=4
B.i=5
A.i=5
从输出结果上看,二个线程已经在并行处理了。
四、通过在线抢购示例理解资源共享
双十一刚过,每到这个时候,通常是狼多肉少,下面的OrderRunnable类模拟这种抢购情况,假设产品数只有10个,抢购的客户却有100个
package com.cnblogs.yjmyzz; public class OrderRunnable implements Runnable{ String taskName; public OrderRunnable(String taskName){
this.taskName=taskName;
} private int productNum = 10; private int customerNum = 100; public void run() { for (int i = 0; i < customerNum; i++) {
if (productNum > 0) {
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(taskName + " -> order success!");
productNum -= 1;
}
} } }
现在想用二个线程来处理:
package com.cnblogs.yjmyzz; public class RunnableDemo4 { public static void main(String[] args) { Runnable r1 = new OrderRunnable("A");
Runnable r2 = new OrderRunnable("B"); new Thread(r1).start();
new Thread(r2).start(); } }
运行结果:
A -> order success!
B -> order success!
B -> order success!
A -> order success!
B -> order success!
A -> order success!
A -> order success!
B -> order success!
B -> order success!
A -> order success!
B -> order success!
A -> order success!
A -> order success!
B -> order success!
A -> order success!
B -> order success!
A -> order success!
B -> order success!
A -> order success!
B -> order success!
显然,这个结果不正确,只有10个产品,却生成了20个订单!
正确的做法,让多个Thread共同使用一个Runnable:
package com.cnblogs.yjmyzz; public class RunnableDemo5 { public static void main(String[] args) { Runnable r1 = new OrderRunnable("A"); new Thread(r1).start();
new Thread(r1).start(); } }
A -> order success!
A -> order success!
A -> order success!
A -> order success!
A -> order success!
A -> order success!
A -> order success!
A -> order success!
A -> order success!
A -> order success!
A -> order success!
五、ThreadPoolExecutor
如果有大量线程,建议使用线程池管理,下面是ThreadPoolExecutor的示例用法:
package com.cnblogs.yjmyzz; import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit; public class RunnableDemo7 { public static void main(String[] args) { ThreadPoolExecutor threadPool = new ThreadPoolExecutor(2, 10, 1,
TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(3)); for (int i = 0; i < 6; i++) {
threadPool.execute(new MyRunnable("R"+i, 5));
} } }
运行结果:
R5.i=1
R0.i=1
R1.i=1
R5.i=2
R1.i=2
R0.i=2
R5.i=3
R1.i=3
R0.i=3
R5.i=4
R1.i=4
R0.i=4
R5.i=5
R0.i=5
R1.i=5
R2.i=1
R3.i=1
R4.i=1
R2.i=2
R3.i=2
R4.i=2
R2.i=3
R3.i=3
R4.i=3
R2.i=4
R4.i=4
R3.i=4
R2.i=5
R4.i=5
R3.i=5
agapple在ITeye上有一篇旧贴子,写得很好,推荐大家去看看,特别是下面这张图:
还有这篇 http://jiaguwen123.iteye.com/blog/1017636,也值得参考
六、ThreadPoolTaskExecutor
终于轮到我大Spring出场了,Spring框架提供了org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor类,可以用注入的形式生成线程池
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:jdbc="http://www.springframework.org/schema/jdbc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-3.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"
default-autowire="byName"> <bean id="threadPoolTaskExecutor"
class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
<property name="corePoolSize" value="2" />
<property name="maxPoolSize" value="10" />
<property name="queueCapacity" value="1000" />
<property name="keepAliveSeconds" value="15" />
<property name="rejectedExecutionHandler">
<bean class="java.util.concurrent.ThreadPoolExecutor$CallerRunsPolicy" />
</property>
</bean> </beans>
配置好以后,就可以直接使用了
package com.cnblogs.yjmyzz; import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; public class RunnableDemo8 { @SuppressWarnings("resource")
public static void main(String[] args) { ApplicationContext applicationContext = new ClassPathXmlApplicationContext(
"spring.xml");
ThreadPoolTaskExecutor taskExecutor = applicationContext.getBean(
"threadPoolTaskExecutor", ThreadPoolTaskExecutor.class); for (int i = 0; i < 6; i++) {
taskExecutor.execute(new MyRunnable("R" + i, 5));
} } }
七、FutureTask<T>
如果某些线程的处理非常耗时,不希望它阻塞其它线程,可以考虑使用FutureTask,正如字面意义一样,该线程启用后,马上开始,但是处理结果将在"未来"某一时刻,才真正需要,在此之前,其它线程可以继续处理自己的事情
package com.cnblogs.yjmyzz; import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask; public class RunnableDemo9 { public static void main(String[] args) throws InterruptedException,
ExecutionException { FutureTask<String> task = new FutureTask<String>(
new Callable<String>() {
public String call() throws InterruptedException {
System.out.println("FutureTask开始处理...");
Thread.sleep(1000);
return "hello world";
}
});
System.out.println("FutureTask准备开始...");
new Thread(task).start();
System.out.println("其它处理开始...");
Thread.sleep(1000);
System.out.println("其它处理完成...");
System.out.println("FutureTask处理结果:" + task.get());
System.out.println("全部处理完成");
} }
二个注意点:
a) FutureTask使用Callable接口取得返回值,因为结果可能并不需要立刻返回,而是等到未来真正需要的时候,而Runnable并不提供返回值
b) FutureTask通过Thread的start()调用后,马上就开始处理,但并不阻塞后面的线程,在真正需要处理结果的时候,调用get()方法,这时如果FutureTask本身的处理尚未完成,才会阻塞,等待处理完成
刚才的运行结果:
FutureTask准备开始...
FutureTask开始处理...
其它处理开始...
其它处理完成...
FutureTask处理结果:hello world
全部处理完成
可以看到,“其它处理”并未被FutureTask阻塞,但FutureTask其实已经在后台处理了。
java:多线程基础之Runnable、Callable与Thread的更多相关文章
- Java 多线程实现接口Runnable和继承Thread区别(转)
Java 多线程实现接口Runnable和继承Thread区别 Java中有两种实现多线程的方式.一是直接继承Thread类,二是实现Runnable接口.那么这两种实现多线程的方式在应用上有什么区别 ...
- [转]Java多线程干货系列—(一)Java多线程基础
Java多线程干货系列—(一)Java多线程基础 字数7618 阅读1875 评论21 喜欢86 前言 多线程并发编程是Java编程中重要的一块内容,也是面试重点覆盖区域,所以学好多线程并发编程对我们 ...
- Java基础16:Java多线程基础最全总结
Java基础16:Java多线程基础最全总结 Java中的线程 Java之父对线程的定义是: 线程是一个独立执行的调用序列,同一个进程的线程在同一时刻共享一些系统资源(比如文件句柄等)也能访问同一个进 ...
- Java 多线程——基础知识
java 多线程 目录: Java 多线程——基础知识 Java 多线程 —— synchronized关键字 java 多线程——一个定时调度的例子 java 多线程——quartz 定时调度的例子 ...
- Java 多线程基础(一)基本概念
Java 多线程基础(一)基本概念 一.并发与并行 1.并发:指两个或多个事件在同一个时间段内发生. 2.并行:指两个或多个事件在同一时刻发生(同时发生). 在操作系统中,安装了多个程序,并发指的是在 ...
- Java 多线程基础(三) start() 和 run()
Java 多线程基础(三) start() 和 run() 通过之前的学习可以看到,创建多线程过程中,最常用的便是 Thread 类中的 start() 方法和线程类的 run() 方法.两个方法都包 ...
- Java 多线程基础(五)线程同步
Java 多线程基础(五)线程同步 当我们使用多个线程访问同一资源的时候,且多个线程中对资源有写的操作,就容易出现线程安全问题. 要解决上述多线程并发访问一个资源的安全性问题,Java中提供了同步机制 ...
- Java 多线程基础(十)interrupt()和线程终止方式
Java 多线程基础(十)interrupt()和线程终止方式 一.interrupt() 介绍 interrupt() 定义在 Thread 类中,作用是中断本线程. 本线程中断自己是被允许的:其它 ...
- Java多线程基础知识总结
2016-07-18 15:40:51 Java 多线程基础 1. 线程和进程 1.1 进程的概念 进程是表示资源分配的基本单位,又是调度运行的基本单位.例如,用户运行自己的程序,系统就创建一个进程, ...
随机推荐
- linux 学习随笔-vim
在自己的home/username目录下 更改vim的配置文件 如果没这个文件 copy其他人的配置文件 然后拖到此目录下 执行mv vimrc ~/.vimrc 更改名字 即可生效 只对当前用户生效 ...
- 发现一个国内牛逼的maven仓库,速度真的太快了
前天网上下了一个项目,在公司还好,网络比较流畅,很快就把依赖下好了:回家的时候,想耍耍,结果下了一天也没把依赖下好,速度是几k每秒,甚至一k每秒,哎~心都碎了,网上一搜,结果发现了一个惊天的用nexu ...
- JavaScript Patterns 6.6 Mix-ins
Loop through arguments and copy every property of every object passed to the function. And the resul ...
- C#语言基础——数组
数组 一.一位数组 数组初始化,创建数组,数组长度为5 int[] array = new int[5]; array[0] = 1; array[1] = 2; array[2] = 3; arra ...
- 使用Source Safe for SQL Server解决数据库版本管理问题(转载)
简介 在软件开发过程中,版本控制是一个广为人知的概念.因为一个项目可能会需要不同角色人员的参与,通过使用版本控制软件,可以使得项目中不同角色的人并行参与到项目当中.源代码控制使得代码可以存在多个版本, ...
- Seq_file文件系统实例剖析
http://blog.chinaunix.net/uid-24432676-id-2607766.html 另 http://www.cnblogs.com/qq78292959/archive/2 ...
- git忽略某些文件
通常项目中编译过程产生的文件或者编译器生成的临时备份文件,不需要通过Git系统去追逐(track). 在项目顶层工作目录中添加一个叫".gitignore"的文件,来告诉Git系统 ...
- fiddler抓包工具1
名称 含义 # 抓取HTTP Request的顺序,从1开始,以此递增 Result HTTP状态码 Protocol 请求使用的协议,如HTTP/HTTPS/FTP等 Host 请求地址的主机名 U ...
- Android常见的按钮监听器实现方式
为按钮(Button)添加响应事件,需要为其设置监听器(Listener).本文总结了Android中常用的几种Button Listener. 第一种:匿名内部类作为事件监听器类 1 2 3 4 5 ...
- 平面直接坐标系线段相交问题(小Q(钟神)的问题)
[问题描述] 小 Q 对计算几何有着浓厚的兴趣.他经常对着平面直角坐标系发呆,思考一些有趣的问题.今天,他想到了一个十分有意思的题目:首先,小 Q 会在?轴正半轴和?轴正半轴分别挑选?个点.随后,他将 ...