java:多线程基础之Runnable、Callable与Thread

java.lang包下有二个非常有用的东西:Runnable接口与Thread类,Thread实现了Runnable接口(可以认为Thread是Runnable的子类),利用它们可以实现最基本的多线程开发。

一、Runnable入门示例

 1 public class RunnableDemo1 {
 2
 3     public static void main(String[] args) {
 4         new Runnable() {
 5             public void run() {
 6                 for (int i = 0; i < 5; i++) {
 7                     try {
 8                         Thread.sleep(100);
 9                     } catch (InterruptedException e) {
10                         e.printStackTrace();
11                     }
12                     System.out.println("r1 -> i=" + i);
13                 }
14
15             }
16         }.run();
17
18         new Runnable() {
19             public void run() {
20                 for (int i = 0; i < 5; i++) {
21                     try {
22                         Thread.sleep(100);
23                     } catch (InterruptedException e) {
24                         e.printStackTrace();
25                     }
26                     System.out.println("r2 -> i=" + i);
27                 }
28             }
29         }.run();
30
31     }
32
33 }

View Code

代码很简单,每个线程依次输出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的子类

 1 package com.cnblogs.yjmyzz;
 2
 3 public class MyRunnable implements Runnable{
 4
 5     private String name;
 6     private int max;
 7
 8     public MyRunnable(String name,int max){
 9         this.name = name;
10         this.max = max;
11     }
12
13     public void run() {
14         for (int i = 1; i <= max; i++) {
15             try {
16                 Thread.sleep(5);
17                 System.out.println(name + ".i=" + i);
18             } catch (InterruptedException e) {
19                 e.printStackTrace();
20             }
21         }
22     }
23
24 }

View Code

 然后这样使用:

 1 package com.cnblogs.yjmyzz;
 2
 3 public class RunnableDemo2 {
 4
 5     public static void main(String[] args) {
 6
 7         new MyRunnable("A", 5).run();
 8
 9         new MyRunnable("B", 5).run();
10     }
11
12 }

View Code

运行结果:

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类改变这种局面:

 1 public class RunnableDemo3 {
 2
 3     public static void main(String[] args) {
 4
 5         Runnable r1 = new MyRunnable("A", 5);
 6         Runnable r2 = new MyRunnable("B", 5);
 7
 8         Thread t1 = new Thread(r1);
 9         Thread t2 = new Thread(r2);
10
11         t1.start();
12         t2.start();
13
14     }
15
16 }

View Code

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个

 1 package com.cnblogs.yjmyzz;
 2
 3 public class OrderRunnable implements Runnable{
 4
 5     String taskName;
 6
 7     public OrderRunnable(String taskName){
 8         this.taskName=taskName;
 9     }
10
11     private int productNum = 10;
12
13     private int customerNum = 100;
14
15     public void run() {
16
17         for (int i = 0; i < customerNum; i++) {
18             if (productNum > 0) {
19                 try {
20                     Thread.sleep(50);
21                 } catch (InterruptedException e) {
22                     e.printStackTrace();
23                 }
24                 System.out.println(taskName + " -> order success!");
25                 productNum -= 1;
26             }
27         }
28
29     }
30
31 }

View Code

现在想用二个线程来处理:

 1 package com.cnblogs.yjmyzz;
 2
 3 public class RunnableDemo4 {
 4
 5     public static void main(String[] args) {
 6
 7         Runnable r1 = new OrderRunnable("A");
 8         Runnable r2 = new OrderRunnable("B");
 9
10         new Thread(r1).start();
11         new Thread(r2).start();
12
13     }
14
15 }

View Code

运行结果:

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:

 1 package com.cnblogs.yjmyzz;
 2
 3 public class RunnableDemo5 {
 4
 5     public static void main(String[] args) {
 6
 7         Runnable r1 = new OrderRunnable("A");
 8
 9         new Thread(r1).start();
10         new Thread(r1).start();
11
12     }
13
14 }

View Code

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的示例用法:

 1 package com.cnblogs.yjmyzz;
 2
 3 import java.util.concurrent.ArrayBlockingQueue;
 4 import java.util.concurrent.ThreadPoolExecutor;
 5 import java.util.concurrent.TimeUnit;
 6
 7 public class RunnableDemo7 {
 8
 9     public static void main(String[] args) {
10
11         ThreadPoolExecutor threadPool = new ThreadPoolExecutor(2, 10, 1,
12                 TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(3));
13
14         for (int i = 0; i < 6; i++) {
15             threadPool.execute(new MyRunnable("R"+i, 5));
16         }
17
18     }
19
20 }

View Code

运行结果:

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类,可以用注入的形式生成线程池

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <beans xmlns="http://www.springframework.org/schema/beans"
 3     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
 4     xmlns:tx="http://www.springframework.org/schema/tx" xmlns:jdbc="http://www.springframework.org/schema/jdbc"
 5     xmlns:context="http://www.springframework.org/schema/context"
 6     xsi:schemaLocation="
 7      http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
 8      http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
 9      http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-3.0.xsd
10      http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
11      http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"
12     default-autowire="byName">
13
14     <bean id="threadPoolTaskExecutor"
15         class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
16         <property name="corePoolSize" value="2" />
17         <property name="maxPoolSize" value="10" />
18         <property name="queueCapacity" value="1000" />
19         <property name="keepAliveSeconds" value="15" />
20         <property name="rejectedExecutionHandler">
21             <bean class="java.util.concurrent.ThreadPoolExecutor$CallerRunsPolicy" />
22         </property>
23     </bean>
24
25 </beans>

View Code

配置好以后,就可以直接使用了

 1 package com.cnblogs.yjmyzz;
 2
 3 import org.springframework.context.ApplicationContext;
 4 import org.springframework.context.support.ClassPathXmlApplicationContext;
 5 import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
 6
 7 public class RunnableDemo8 {
 8
 9     @SuppressWarnings("resource")
10     public static void main(String[] args) {
11
12         ApplicationContext applicationContext = new ClassPathXmlApplicationContext(
13                 "spring.xml");
14         ThreadPoolTaskExecutor taskExecutor = applicationContext.getBean(
15                 "threadPoolTaskExecutor", ThreadPoolTaskExecutor.class);
16
17         for (int i = 0; i < 6; i++) {
18             taskExecutor.execute(new MyRunnable("R" + i, 5));
19         }
20
21     }
22
23 }

View Code

 

七、FutureTask<T>

如果某些线程的处理非常耗时,不希望它阻塞其它线程,可以考虑使用FutureTask,正如字面意义一样,该线程启用后,马上开始,但是处理结果将在"未来"某一时刻,才真正需要,在此之前,其它线程可以继续处理自己的事情

 1 package com.cnblogs.yjmyzz;
 2
 3 import java.util.concurrent.Callable;
 4 import java.util.concurrent.ExecutionException;
 5 import java.util.concurrent.FutureTask;
 6
 7 public class RunnableDemo9 {
 8
 9     public static void main(String[] args) throws InterruptedException,
10             ExecutionException {
11
12         FutureTask<String> task = new FutureTask<String>(
13                 new Callable<String>() {
14                     public String call() throws InterruptedException {
15                         System.out.println("FutureTask开始处理...");
16                         Thread.sleep(1000);
17                         return "hello world";
18                     }
19                 });
20         System.out.println("FutureTask准备开始...");
21         new Thread(task).start();
22         System.out.println("其它处理开始...");
23         Thread.sleep(1000);
24         System.out.println("其它处理完成...");
25         System.out.println("FutureTask处理结果:" + task.get());
26         System.out.println("全部处理完成");
27     }
28
29 }

View Code

二个注意点:

a) FutureTask使用Callable接口取得返回值,因为结果可能并不需要立刻返回,而是等到未来真正需要的时候,而Runnable并不提供返回值

b) FutureTask通过Thread的start()调用后,马上就开始处理,但并不阻塞后面的线程,在真正需要处理结果的时候,调用get()方法,这时如果FutureTask本身的处理尚未完成,才会阻塞,等待处理完成

刚才的运行结果:

FutureTask准备开始...
FutureTask开始处理...
其它处理开始...
其它处理完成...
FutureTask处理结果:hello world
全部处理完成

可以看到,“其它处理”并未被FutureTask阻塞,但FutureTask其实已经在后台处理了。

 

时间: 2024-11-03 21:45:15

java:多线程基础之Runnable、Callable与Thread的相关文章

Java多线程基础总结七:ReentrantLock

之前总结了部分无锁机制的多线程基础,理想的状态当然是利用无锁同步解决多线程程序设计的问题.但是实际碰到的问题使得很多情 况下,我们不得不借助锁同步来保证线程安全.自从JDK5开始,有两种机制来屏蔽代码块在并行访问的干扰,synchronized关键字已经介绍 过了部分内容,所以这次简单的说说另一种锁机制:ReentrantLock. 对于synchronized的缺点之前也简单的说了一些,实际使用中比较烦扰的几点是:a.只有一个"条件"与锁相关联,这对于大量并发线程 的情况是很难管理(

Java多线程基础总结二: Thread

对于Thread来说只想说两个方法,一个是setDaemon(false|true),另一个是join().首先说说守护线程,这么东西是干什么用的?对于 Java应用我们都知道main方法是入口,它的运行代表着主线程开始工作了,我们也知道JVM里面有垃圾回收器的存在使得我们放心让main飞 奔,然而这背后的故事是垃圾回收线程作为守护着主线程的守护线程默默的付出着.很像那个啥啊,呵呵.令人发指的是main这个畜生背后 其实有好几个守护线程默默的付出!当然如果硬是要把守护线程比做女人,非守护线程比做

Java多线程基础总结八:ReentrantReadWriteLock

说到ReentrantReadWriteLock,首先要做的是与ReentrantLock划清界限.它和后者都是单独的实现,彼此之间没有继承或实现的关系. 然后就是总结这个锁机制的特性了: (a).重入方面其内部的WriteLock可以获取ReadLock,但是反过来ReadLock想要获得WriteLock则永远都不要想. (b).WriteLock可以降级为ReadLock,顺序是:先获得WriteLock再获得ReadLock,然后释放WriteLock,这时候线程将保持Readlock的

java多线程基础(synchronize关键字)

基础知识 多线程实现方法 使用Thread创建线并启动线程 使用Runnable创建并启动线程 使用内部类创建线程 线程的方法 线程优先级 守护线程 sleep方法 yield方法 join方法 线程同步 基础知识 线程:进程(process)就是一块包含了某些资源的内存区域.操作系统利用进程把它的工作划分为一些功能单元. 线程:进程中所包含的一个或多个执行单元称为线程(thread).进程还拥有一个私有的虚拟地址空间,该空间仅能被它所包含的线程访问. 线程和进程的区别如下: 1)一个进程至少有

Java多线程基础总结九:Mina窥探(1)

一直以来的多线程的基础总结都是脱离应用的,但是要说多线程的应用就不能不说Mina.Apache Mina作为一个高性能的Java异步并发网 络通讯框架,其内部的多线程的设计和实现可谓是学习多线程的良药.手上的Mina源码是svn剪下来的最新的代码,mvn转化成eclipse项目 后导入mina-core的源码看看多线程的应用吧. 首先简单的介绍在org.apache.mina.core.service包里的核心接口之一:IoService.这个接口是对于服务器端接收连接和客户端发起连 接这两种服

Java多线程基础总结六:synchronized(2)

早在总结一时,我就尽量的把synchronized的重点说的简单:它就是配和对象的隐式锁使用的,注意一定是对象的隐式锁!那么下面的 这个例子又怎么解释呢? Java代码 /** * User: yanxuxin * Date: Dec 17, 2009 * Time: 9:38:27 PM */ public class ImplicitLockSample { public static void main(String[] args) { final ImplicitLock sample

Java多线程基础总结四:ThreadLocal

说到ThreadLocal,首先说说这个类的命名.直观上看好像是个Thread的什么亲戚,但其实它想表达的意思是线程本地变量,也就是说每 个线程自己的变量.它作为一个JDK5以后支持范型的类,主要是想利用范型把非线程安全的共享变量,封装成绑定线程的安全不共享变量. 这样的解释我想我们多半能猜出它的实现思路:把一个共享变量在每个线程使用时,初始化一个副本,并且和线程绑定.以后所有的线程对 共享变量的操作都是对线程内部那个副本,完全的线程内部变量的操作. 要实现这样功能类的设计,主要技术点是要能把副

Java多线程基础总结三: volatile

前面的两篇总结简单的说明了同步的一些问题,在使用基础的同步机制中还有两个可以分享的技术:volatile关键字和ThreadLocal.合 理的根据场景利用这些技术,可以有效的提高并发的性能,下面尝试结合自己的理解叙述这部分的内容,应该会有理解的偏差,我也会尽量 的在完善自己理解的同时同步更新文章的错误. 或许在知道synchronized配和对象内部锁的机制以后,可以提高写出正确同步的并发程序成功率,但是这时候会遇到另一个大问题:性 能!是的,对于 synchronized带来的可能庞大的性能

Java多线程基础总结一: synchronized(1)

最近写关于并发的小应用,才发现真的该好好的正视java的多线程了.之前没有深入的掌握,用起来也是那么的吃力.作为J2SE里面为 数不多的重要难点之一,多线程应用一直是我以敬畏的心态去尽量避开的,只是通过一些实例掌握一些简单的应用.这段时间会多用点时间 去掌握,有需要写下来的我也会通过这种方式既分享又加深理解. 首先这篇只涉及基础的知识整理,对于并发包java.util.concurrent内的线程池和锁我会看情况在之后的总结中写点东西.对于进程的 概念我们都很熟悉,它是应用程序级的隔离,不同的应