![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsISPrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdsATOfd3bkFGazxCMx8VesATMfhHLlN3XnxCMwEzX0xiRGZkRGZ0Xy9GbvNGLpZTY1EmMZVDUSFTU4VFRR9Fd4VGdsYTMfVmepNHLrJXYtJXZ0F2dvwVZnFWbp1zczV2YvJHctM3cv1Ce-cmbw5iZmZTOxY2NwEWYkF2Y5ITOjhTN1UTZ1cDOmRGOwcDZx8CXxMzLcdDMxIDMy8CXn9Gbi9CXzV2Zh1WavwVbvNmLvR3YxUjL3M3Lc9CX6MHc0RHaiojIsJye.png)
java中的多線程入門 Runnable與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 }
然後這樣使用:
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 }
運作結果:
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 }
Thread通過start方法,可以讓多個線程并行處理,運作結果如下:
從輸出結果上看,二個線程已經在并行處理了。
四、通過線上搶購示例了解資源共享
雙十一剛過,每到這個時候,通常是狼多肉少,下面的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 }
現在想用二個線程來處理:
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 }
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 }
五、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 }
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上有一篇舊貼子,寫得很好,推薦大家去看看,特别是下面這張圖:
六、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>
配置好以後,就可以直接使用了
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 }
七、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 }
二個注意點:
a) FutureTask使用Callable接口取得傳回值,因為結果可能并不需要立刻傳回,而是等到未來真正需要的時候,而Runnable并不提供傳回值
b) FutureTask通過Thread的start()調用後,馬上就開始處理,但并不阻塞後面的線程,在真正需要處理結果的時候,調用get()方法,這時如果FutureTask本身的處理尚未完成,才會阻塞,等待處理完成
剛才的運作結果:
FutureTask準備開始...
FutureTask開始處理...
其它處理開始...
其它處理完成...
FutureTask處理結果:hello world
全部處理完成
可以看到,“其它處理”并未被FutureTask阻塞,但FutureTask其實已經在背景處理了。
作者:菩提樹下的楊過