[转帖]  并发基础(Runnable、Thread、Executor)_Android, Python及开发编程讨论区_Weblogic技术|Tuxedo技术|中间件技术|Oracle论坛|JAVA论坛|Linux/Unix技术|hadoop论坛_联动北方技术论坛  
网站首页 | 关于我们 | 服务中心 | 经验交流 | 公司荣誉 | 成功案例 | 合作伙伴 | 联系我们 |
联动北方-国内领先的云技术服务提供商
»  游客             当前位置:  论坛首页 »  自由讨论区 »  Android, Python及开发编程讨论区 »
总帖数
1
每页帖数
101/1页1
返回列表
0
发起投票  发起投票 发新帖子
查看: 12429 | 回复: 0   主题: [转帖]  并发基础(Runnable、Thread、Executor)        下一篇 
    本主题由 522080330 于 2013-1-23 16:01:45 置为精华
522080330
注册用户
等级:少校
经验:1478
发帖:79
精华:20
注册:2013-1-18
状态:离线
发送短消息息给522080330 加好友    发送短消息息给522080330 发消息
发表于: IP:您无权察看 2013-1-23 14:42:51 | [全部帖] [楼主帖] 楼主

     与顺序编程不同,并发使程序可以在“同一时间”执行多个操作。
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());
            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<V> {
      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<Future<String>> results = new ArrayList<Future<String>>();
             6 for (int i = 0; i < 5; i++) {
                   7 results.add(exec.submit(new TaskWithResult(i)));
             8 }
             9 exec.shutdown();
            10 for (Future<String> f : results) {
                  11 System.out.println(f.get());
            12 }
      13 }
14 }
15
16 class TaskWithResult implements Callable<String> {
      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
             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


上面是运行结果。从运行结果中可以看出休眠可以让出执行权限,但是不能保证精确的执行顺序。
枚举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()


守护进程
所谓守护线程,也可以叫后台线程,是指在程序运行的时候后台提供一种通用服务的线程,并且这种线程不属于程序中不可或缺的部分(只要有非后台线程还在运行,程序就不会终止。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
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方法判断一个线程是否是后台线程。由后台线程创建的任何线程都将自动设置为后台线程。




赞(0)    操作        顶端 
总帖数
1
每页帖数
101/1页1
返回列表
发新帖子
请输入验证码: 点击刷新验证码
您需要登录后才可以回帖 登录 | 注册
技术讨论