并发编程

来源:互联网 发布:linux安装apache 编辑:程序博客网 时间:2024/05/16 23:41

基本线程机制
并发编程将程序划分为多个分离的、独立运行的任务。通过多线程机制,这些独立的任务中的每一个都将由执行线程来驱动。(就是Runnable的实现类与Thread的关系)一个线程就是在进程中的一个单一的顺序控制流。单个进程可以拥有多个并发执行的任务。

在使用线程的时候,CPU将轮流给每个任务分配其占用时间,每个任务都觉得自己一直在占用CPU,但事实上,CPU的时间被划分为一个个小片段分配给所有任务。简单的讲,就是CPU在这个很短的时间段内执行这个线程,在那个短时间段内执行哪一个线程,从微观上不同时,宏观上是同时执行多个任务。

Runnable和Thread
线程(Thread)可以驱动任务(Runnable实现类),这时就需要一种描述任务的方式,即实现Runnable接口,实现其run()方法。
通常,任务中的run()方法被写成无限循环的形式。除非某个条件使得run()终止,否则将永远运行下去。该线程任务未执行结束,代表线程不能死亡,垃圾回收器就无法清除它。(Thread区别于其他对象,当普通的对象失去引用的时候,垃圾回收器将会回收内存)

示例代码

public class ThreadAndRunnable implements Runnable {    private int countDown = 10;    private static int taskCount = 0;  // 任务数    private final int id = taskCount++;   // id用于区分多个任务实例    public String toString() {        return "id " + "(" + id + ")   a    " + (countDown > 0 ? countDown : "归零");    }       public void run() {        while(countDown-- > 0) {            System.out.println(this.toString());            Thread.yield();        }       }    public static void main(String[] args) {        Thread mThread = new Thread(new ThreadAndRunnable());        mThread.start();    }}

注:开辟线程的能力不仅仅main()线程拥有,其他子线程也可以开辟其他线程。

Executor的使用
java.util.concurrent包中的Executor可以用于管理Thread对象。

public ThreadPoolExecutor(int corePoolSize, // 核心线程数                        int maximunPoolSize, // 线程池容纳最大线程数                        long keepAliveTime, // 非核心线程闲置的超时时长                        TimeUnit unit, // 时间单位                        BlockingQueue<Runnable> workQueue, //线程池的任务队列, execute提交的runnable对象                        ThreadFactory threadFactory // 为线程池提供创建线程的功能)

注意,ExecutorService对象是使用静态的Executor方法创建的。
①CachedThreadPool将为每个任务创建线程 。 它是一种线程数量不定的线程池, 他只有非核心线程, 并且最大数目为 Integer.MAX_VALUE。当线程池的所有线程都处于活动状态时, 线程池会创建新的线程来处理新的任务, 否则利用空闲线程来处理新任务。 而空闲线程超时60后, 将会回收。 也就是说,当整个线程池都置于空闲状态后, 线程池的线程都会超时而被停止, 这个时候 CachedThreadExecutor 几乎不占用任何系统资源。
适合执行大量耗时较少的任务

示例代码

import java.util.concurrent.*;public class CachedThreadPool {    public static void main(String[] args) {        ExecutorService exec = Executors.newCachedThreadPool();  // 创建管理Thread的Executor        for(int i = 0; i < 3; i++) {            exec.execute(new MyRunnable());  // 创建3个任务,扔进线程池        }        // 调用shutdown()将不能再往里面添加任务,否则抛RejectedExecutionException异常        exec.shutdown();       }}

执行结果

②FixedThreadPool,有限线程集,在创建线程池的时候指定线程数量,即限制在线程池中同时运行的线程数。若指定为1的话,同时又有99个任务,最后会造成99个任务按顺序执行,即执行完一个,下一个才开始执行。
线程固定的线程池,线程处于空闲状态时,并不会回收,除非线程池被关闭。 由于核心线程都不会被回收, 所以能更加快速响应外界请求。

public class FixedThreadPool {    public static void main(String[] args) {        ExecutorService exec = Executors.newFixedThreadPool(3);  // 指定线程数量        for(int i = 0; i < 99; i++) {            exec.execute(new MyRunnable());        }        exec.shutdown();    }}

③SingleThreadExecutor,即线程数量限制为1的FixedThreadPool,通常放入的是长期存活的任务。如果向SingleThreadExecutor提交多个任务,就像上面说的,这些任务将会排队。所有任务使用的是相同的线程。(代码与上面类似,不再重复贴出)

摘自书本,假设有大量的线程,它们运行的任务将使用文件系统,你可以用SingleThreadExecutor来运行这些线程,以确保任意时刻在任何线程中都只有唯一任务在运行,所以在这种方式中,你不需要在共享资源上处理同步。

④ ScheduleThreadPool
通过 newScheduledThreadPool 创建, 核心线程数量固定, 而非核心线程是没有限制的, 并且闲置时会被回收。 这类线程池主要用于执行定时任务和具有固定周期的重复任务

上面三个线程池使用的Runnable实现类

class MyRunnable implements Runnable {    private int countDown = 5;    private static int taskCount = 0;    private int id = taskCount++;    public String toString() {        return "Runnable id: " + "("  + id + ")" + "   countDown:" + (countDown > 0 ? countDown : "归零");    }    public void run() {        while(countDown-- > 0) {            System.out.println(this.toString());        }        Thread.yield();  // 让步其他线程执行    }}

从任务中产生返回值
Runnable是执行工作的任务,但它没有返回值。若希望任务完成时返回一个值,则需要实现Callable接口,而不是Runnable接口。指定实现Callable接口的范型(以确定call的返回值),并重写call方法。最后必须使用ExecutorService.submit()方法调用它。
submit()方法会产生Future对象,可以调用isDone()来查询Future是否完成,调用get()方法,获取执行结果。如果没有用isDone()检查就直接调用get()方法,将产生阻塞,直至结果被计算出来。
示例代码:

import java.util.concurrent.*;import java.util.*;public class CallableDemo {    public static void main(String[] args) {        ExecutorService exec = Executors.newCachedThreadPool();        // 存放Future对象        ArrayList<Future<String>> futureList = new ArrayList<Future<String>>();        for(int i = 0; i < 10; i++) {            futureList.add(exec.submit(new MyCallable(i)));        }        for(Future<String> future: futureList) {            try            {                System.out.println(future.get());                       }            catch (InterruptedException e) {                System.out.println(e);            }            catch (ExecutionException e) {                System.out.println(e);            } finally {                exec.shutdown();            }        }    }}class MyCallable implements Callable<String> {    private int id;    public MyCallable (int id) {        this.id = id;    }    public String call() {        return "result of MyCallable " + id;      }}

结果截图:

0 0
原创粉丝点击