多线程学习之路


线程是由操作系统来进行调度的

1、实现多线程的四种方式
  1. 继承Thread类

    Tread类本质上就是一个实现了Runnable接口的一个实例,代表一个线程的实例。启动线程的唯一方法是通过Thread的类的start()实例方法。start()是一个本地方法,它将启动一个新线程,并执行run()方法。

    通过自己的类直接继承Tread类,并重写run()方法。

  2. 实现Runnable接口

    因为继承是单继承,所以当自己的类已经继承了另一个类的时候,就无法使用extends Tread了,可以实现Runnable接口,并实现run()方法。使用时先实例化一个Tread,并传入自己的对象实例,当执行Tread.start()的时候,Tread的run()方法就会调用目标对象的run()方法

    public void run() {  
      if (target != null) {  
       target.run();  
      }  
    }
    
  3. 实现callable接口,通过futureTask包装器来创建Tread线程

    public interface Callable<V>   { 
      V call() throws Exception;   
    }
    
    public class SomeCallable<V> extends OtherClass implements Callable<V> {
        @Override
        public V call() throws Exception { 
            return null;
        }
    }
    //第一种,使用Tread
    Callable<V> oneCallable = new SomeCallable<V>();     
    FutureTask<V> oneTask = new FutureTask<V>(oneCallable);      
    Thread oneThread = new Thread(oneTask);   
    oneThread.start(); 
    //第二种,使用线程池
    ExecutorService executor = Executors.newCachedThreadPool();
    Callable<V> oneCallable = new SomeCallable<V>();
    FutureTask<Integer> futureTask = new FutureTask<Integer>(oneCallable);
    executor.submit(futureTask);
    executor.shutdown();
    
  4. 使用ExecutorService、Callable、Future实现有返回结果的线程

    public interface Future<V> {
        boolean cancel(boolean mayInterruptIfRunning);
        boolean isCancelled();//表示任务是否被取消成功,如果任务正常完成前被取消成功,则返回true
        boolean isDone();//表示任务是否已经完成,若任务完成,返回true
        V get() throws InterruptedException, ExecutionException;//会产生阻塞,直到任务执行完毕
        V get(long timeout, TimeUnit unit)//指定时间内没获取到结果,直接返回null
            throws InterruptedException, ExecutionException, TimeoutException;
    }
    // 创建一个线程池  
    ExecutorService pool = Executors.newFixedThreadPool(5);  
    // 创建多个有返回值的任务  
    List<Future> list = new ArrayList<Future>();  
    for (int i = 0; i < 5; i++) {  
        Callable c = new MyCallable(i + " ");  
        // 执行任务并获取Future对象  
        Future f = pool.submit(c);  
        // System.out.println(">>>" + f.get().toString());  
        list.add(f);  
    }  
    // 关闭线程池  
    pool.shutdown();  
    // 获取所有并发任务的运行结果  
    for (Future f : list) {  
        // 从Future对象上获取任务的返回值,并输出到控制台  
        System.out.println(">>>" + f.get().toString());  
    }  
    
2、线程池
  1. java.uitl.concurrent.ThreadPoolExecutor类是线程池最核心的类
  //构造函数
  public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,
  	long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,
  	ThreadFactory threadFactory,RejectedExecutionHandler handler);
  • corePoolSize:核心池大小,当线程池中的线程数目达到corePoolSize大小后,就会把任务放到缓存队列

  • maximumPoolSize:线程池的最大线程数,表示线程池最多能创建多少线程

    当一个新的任务被提交到线程池的时候,1>,先判断核心池是否已满,未满则创建一个新的线程来执行该任务;满了则去判断任务队列是否已满,未满则将新提交的任务存储在工作队列中;满了则去判断最大线程数是否已满,未满则创建一个新的工作线程去执行任务;满了则交给饱和策略来处理这个任务。如果线程池中的线程数大于核心池大小,则当某个线程空闲时间超过keepAliveTime时,该线程会被终止
    线程池处理流程

  • keepAliveTime:默认情况下,只有当线程池中的线程数量大于核心池时,这个参数才会起作用

  • unit :是keepAliveTime的时间单位

  • workQueue:是一个阻塞队列,用来存储等待执行的任务

  • threadFactory:线程工厂,主要用来创建线程

  • handler:表示当拒绝任务处理时的策略

    • AbortPolicy:丢弃任务并抛出RejectedExecutionException异常
    • DiscardPolicy:丢弃任务,但不抛出异常
    • DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
    • CallerRunsPolicy:由调用线程处理该任务
  1. ThreadPoolExecutor有几个重要的方法:

    • execute():向线程池提交一个任务,交由线程池去执行
    • submit():也是向线程池提交任务,和execute()的区别是submit()能够返回任务的执行结果,实际上调用的还是execute(),只不过利用Future()来获取任务的执行结果
    • shutdown():关闭线程池,此时线程池不能够接受新的任务,他会等待所有任务执行完毕
    • shutdownNow():关闭线程池,此时线程池不能接受新的任务,并且尝试终止正在进行的任务
  2. 线程池的状态:

    volatile int runState;
    static final int RUNNING = 0;
    static final int SHUTDOWN = 1static final int STOP = 2;
    static final int TERMINATED = 3

    runState:表示当前线程池的状态,使用volatile修饰来保证线程之前的可见性

  3. 线程池中的线程初始化

    默认情况下,创建线程池之后,线程池中是没有线程的,需要提交任务之后才会创建线程

    在实际中如果需要线程池创建之后 立即创建线程,可以通过两个方法来实现:

    • prestartCoreThread():初始化一个核心线程
    • prestartAllCoreThreads():初始化核心池数量的线程
  4. 任务缓存队列及排队策略

    workQueue的类型为BlockingQueue,通常有三种类型:

    • ArrayBlockingQueue:基于数组的先进先出队列,此队列创建时必须指定大小
    • LinkedBlockingQueue:基于链表的阻塞队列,如果创建时没有指定此队列的大小,默认为Integer.MAX_VALUE;
    • SynchronousQueue:这个队列比较特殊,不会保存提交的任务,而是直接新建一个线程来执行新来的任务
  5. 具体使用

    Executors.newCachedThreadPool();      //创建一个缓冲池,缓冲池容量大小为Integer.MAX_VALUE
    Executors.newSingleThreadExecutor();  //创建容量为1的缓冲池
    Executors.newFixedThreadPool(int);    //创建固定容量大小的缓冲池
    

    具体实现:

    public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }
    public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }
    public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }
    

    从它们的具体实现来看,它们实际上也是调用了ThreadPoolExecutor,只不过参数都已配置好了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值