易妖游戏网
您的当前位置:首页并发基础(runnable、thread、executor)

并发基础(runnable、thread、executor)

来源:易妖游戏网
少年易学老难成,一寸光阴不可轻 -

并发基础(Runnable、Thread、Executor)

与顺序编程不同,并发使程序可以在“同一时间”执行多个操作。

Java对并发编程提供了语言级别的支持。Java通过线程来实现并发程序。一个线程通常实现一个特定的任务,多个线程一起执行的时候就实现了并发。 定义任务的最简单的方式就是实现Runnable接口。 1 public interface Runnable { 2 public abstract void run(); 3 }

Runable只定义了一个run()方法。 下面是一个监听用户输入的任务。

1 public class Monitor implements Runnable { 2

3 @Override

4 public void run() {

5 BufferedReader reader = new BufferedReader(new InputStreamReader( 6 System.in)); 7 while (true) { 8 String str; 9 try {

10 str = reader.readLine(); 11 if (str.equals(\"quit\")) { 12 return; 13 } else {

14 System.out.println(str); 15 }

16 } catch (IOException e) { 17 e.printStackTrace(); 18 } 19

20 } 21 } 22 }

执行一个任务最简单的方式是把它交给一个Thread构造器。

1 public class Test {

2 public static void main(String[] args) { 3 System.out.println(\"Main Start\");

4 Thread task = new Thread(new Monitor());

1

少年易学老难成,一寸光阴不可轻 -

5 task.start();

6 System.out.println(\"Main End\"); 7 } 8 }

执行上面的程序可以看到类似下面这样的结果:

可以看到Main方法一次执行各语句到最后输出“Main End”,但Monitor依旧在运行,因为它在另一个线程中。

除了Thread的方式,Java还提供了执行器Executor简化并发编程。

Executor使用execute(Runnable command)方法执行一个任务,使用shutdown()方法防止新任务被提交给Executor,当前线程将继续执行shutdown()方法调用之前提交的任务。像这样: 1 public static void main(String[] args) { 2 System.out.println(\"Use Executor...\");

3 ExecutorService exec = Executors.newCachedThreadPool(); 4 exec.execute(new Monitor()); 5 exec.shutdown(); 6 }

Executor的详细内容见《Java Executor框架分析》。

Runnable只是执行一个任务,但是并不能获取任务的执行结果(准确的说应该是run方法是一个void的方法,没有返回值)。如果希望获取任务的执行结果,那么可以选择实现Callable接口。 1 public interface Callable { 2 V call() throws Exception; 3 }

它是一个接收泛型,且具有返回内容的“任务接口”。下面是一个通过Callable执行任务并获取返回结果的例子。

1 public class Test {

2 public static void main(String[] args) throws InterruptedException, 3 ExecutionException {

4 ExecutorService exec = Executors.newCachedThreadPool();

5 List> results = new ArrayList>(); 6 for (int i = 0; i < 5; i++) {

2

少年易学老难成,一寸光阴不可轻 -

7 results.add(exec.submit(new TaskWithResult(i))); 8 }

9 exec.shutdown();

10 for (Future f : results) { 11 System.out.println(f.get()); 12 } 13 } 14 } 15

16 class TaskWithResult implements Callable { 17 private int id; 18

19 public TaskWithResult(int id) { 20 this.id = id; 21 } 22

23 @Override

24 public String call() throws Exception { 25 return \"result of TaskWithResult#\" + id; 26 } 27 }

休眠

Java线程调度是Java多线程的核心,只有良好的调度,才能充分发挥系统的性能,提高程序的执行效率。线程休眠是使线程让出CPU的最简单的做法之一。当线程休眠一定时间后,线程会苏醒,进入准备状态等待执行。

Thread提供了两个sleep方法:Thread.sleep(long millis) 和Thread.sleep(long millis, int nanos)。 线程可以通过休眠让出CPU的执行权限,但是这也是无法保证线程精确的执行次序的。下面是一个线程休眠的例子。 枚举

TimeUnit中也提供了sleep方法,可以通过它的实例去调用,如

TimeUnit.MICROSECONDS.sleep(timeout)。

sleep例子 1 package com.skyjoo.test; 2 3 public class SleepingTask { 4 5 static class SimpleThread extends Thread { 6 @Override 3

少年易学老难成,一寸光阴不可轻 -

7 public void run() {

8 for (int i = 0; i < 5; i++) {

9 System.out.println(\"Thread run\" + i); 10 try {

11 Thread.sleep(100);

12 } catch (InterruptedException e) { 13 } 14 } 15 } 16 } 17

18 static class SimpleRunnable implements Runnable { 19 @Override

20 public void run() {

21 for (int i = 0; i < 5; i++) {

22 System.out.println(\"Runnable run\" + i); 23 try {

24 Thread.sleep(100);

25 } catch (InterruptedException e) { 26 } 27 } 28 } 29 30 }

31 public static void main(String[] args) { 32 Thread simpleThread = new SimpleThread();

33 Thread thread = new Thread(new SimpleRunnable()); 34 simpleThread.start(); 35 thread.start(); 36 } 37 }

Thread run0 Runnable run0 Runnable run1 Thread run1 Thread run2 Runnable run2 Thread run3 Runnable run3 Runnable run4 Thread run4

4

少年易学老难成,一寸光阴不可轻 -

上面是运行结果。从运行结果中可以看出休眠可以让出执行权限,但是不能保证精确的执行顺序。 枚举

TimeUnit中也提供了sleep方法,可以通过它的实例去调用,如

TimeUnit.MICROSECONDS.sleep(timeout)。

让步

如果已经完成了run()方法的循环和一次迭代过程中所需的工作,就可以给线程调度机制一个暗示:工作已经做的差不多了,可以让别的线程占用CPU。这个暗示是通过调用yield()方法实现的。这只是一个暗示,未必会被采用。当调用yield()时,也是在建议具有相同优先级的线程可以运行。 任何重要的调度都不能依赖于yield()。 优先级

线程的优先级将该线程的重要性传递给调度器。尽管CPU处理线程的顺序是不确定的,但是调度器将倾向于让优先级高的线程先执行。优先级较低的线程执行的频率较低。 可以通过setPriority和getPriority来设置和获取线程的优先级。 线程的优先级从1~10.超出这个值将报异常。

join()

Waits for this thread to die.

这是Thread中对join()方法的注释。

线程可以在其他线程之上调用join()方法,效果等同于等待一段时间直到第二个线程结束才继续执行。如在t1中调用t2.join()则需要t2线程执行完后继续执行t1线程。

join()方法还有其他形式,如带上超时时间,这样目前线程在这段时间内没有结束join方法也能返回。(其实join()方法调用的是join(0)) join(long millis)

join(long millis, int nanos) join()

5

少年易学老难成,一寸光阴不可轻 -

守护进程

所谓守护线程,也可以叫后台线程,是指在程序运行的时候后台提供一种通用服务的线程,并且这种线程不属于程序中不可或缺的部分(只要有非后台线程还在运行,程序就不会终止。main就是一个非后台线程)。

SimpleDaemons

1 public class SimpleDaemons implements Runnable { 2

3 @Override

4 public void run() { 5 try {

6 while (true) {

7 TimeUnit.MILLISECONDS.sleep(100);

8 System.out.println(Thread.currentThread() + \" \" + this); 9 }

10 } catch (Exception e) { 11 // TODO: handle exception 12 } 13 } 14

15 public static void main(String[] args) throws InterruptedException { 16 for(int i=0;i<10;i++){

17 Thread daemon = new Thread(new SimpleDaemons()); 18 // 必须在线程启动之前调用setDaemon方法 19 daemon.setDaemon(true); 20 daemon.start(); 21 }

22 System.out.println(\"All daemons started\"); 23 TimeUnit.MILLISECONDS.sleep(175); 24 } 25 }

All daemons started

Thread[Thread-8,5,main] com.skyjoo.test.SimpleDaemons@47b480 Thread[Thread-1,5,main] com.skyjoo.test.SimpleDaemons@1bf216a Thread[Thread-2,5,main] com.skyjoo.test.SimpleDaemons@10d448 Thread[Thread-3,5,main] com.skyjoo.test.SimpleDaemons@6ca1c Thread[Thread-6,5,main] com.skyjoo.test.SimpleDaemons@6ca1c Thread[Thread-9,5,main] com.skyjoo.test.SimpleDaemons@e0e1c6 Thread[Thread-7,5,main] com.skyjoo.test.SimpleDaemons@19b49e6 Thread[Thread-4,5,main] com.skyjoo.test.SimpleDaemons@47b480

6

少年易学老难成,一寸光阴不可轻 -

Thread[Thread-0,5,main] com.skyjoo.test.SimpleDaemons@156ee8e Thread[Thread-5,5,main] com.skyjoo.test.SimpleDaemons@156ee8e

注意观察main中的sleep(175),如果设置成更长的时间将看到更多的输出结果,因为每个线程都在不断的输出结果。一旦main结束了,就没有非后台线程了,所以程序就终止了,所以就不会在有输出了。如果main中设置的sleep之间为0将看不到线程输出的结果,因为程序会马上结束掉。

注意:可以通过isDaemon方法判断一个线程是否是后台线程。由后台线程创建的任何线程都将自动设置为后台线程。

7

因篇幅问题不能全部显示,请点此查看更多更全内容