多线程JAVA

线程中常见的方法:

    Thread.yeild();   建议线程调度器,我已经执行完了任务的重要部分,此刻正是把cpu切换给其他任务的最佳时期
    Thread t;
    t.join();   //当前线程等待t线程执行完后在执行当前线程;
    t.join(millsecond);     //等待多长时间后产生中断异常

线程池:

JAVA5后推荐使用这种,大致分为四种类别的线程池:
1. ExecutorService es = Executors.newCachedThreadPool();
缓存线程池,该线程池都会创建于所需线程数量一样的线程,就停止创建新的线程

2. ExecutorService es = Executors.newFixedThreadPool(15);
固定数量的线程池,当你可预先知道运行所需线程数量,可以使用该线程池,一次性创建完所线程,节省时间;

3. ExecutorService es = Executors.newScheduledThreadPool();
创建一个定长线程池,支持定时及周期性任务执行

4. ExecutorService es = Executors.newSingleThreadExecutor();
拥有一个线程的线程池,多用于长期存活的任务,可以使用该线程;对它提交多个线程任务时,则运行线程必须等上一个线程结束后才能执行下一个线程;它可以很方便的解决同步共享资源,例如,多个线程将要访问同一文件系统,使用singleThreadExecutor就会让多个线程一个一个访问系统,很方便的解决了多线程同步问题

线程池用法:
1. new一个类型的线程池
2. 线程池的submit方法提交线程任务
3. 等待cpu轮询执行任务
4. 使用完毕后要shutdown关闭线程池
关闭线程池有两种方法
shutdown和shutdownnow,前者调用后,会等待当前任务线程并且等候对列里面的线程都执行完后才关闭;而shutdownnow则会任务线程和等待队列将都不会得到执行;实质上关闭的方法都是在里面跑出一个中断异常interrupt

线程池工作原理

以上大致是线程池的四种常用类型,同时我们也可以自定义自己的线程池ThreadPoolExecutor,上面的四种也是由它引化而出的,想要了解该线程池,必须了解线程池的工作原理,ThreadPoolExecutor构造方法如下:

public ThreadPoolExecutor(int corePoolSize,

                                 int maximumPoolSize,

                                 long keepAliveTime,

                                 TimeUnit unit,

                                 BlockingQueue<Runnable> workQueue,

                                 ThreadFactory threadFactory,

                               RejectedExecutionHandler handler)

corePoolSize: 线程池核心数量,也就是线程池最小数量

maximumPoolSize: 线程池线程最大数量

keepAliveTime: 报活时间,简而言之就是空闲线程存活时间,如果任务过多并且处理时间短而频繁,就可以调大这个值,减少销毁空闲线程

uint: keepAliveTime的时间单位,有:天(DAYS),小时(HOURS),分钟(MINUTES),毫秒(MILLISECONDS),微秒(MICROSECONDS)和纳秒(NANOSECONDS)

workQueue: 缓存任务队列,当线程数大于核心线程时,而缓冲队列没有满时,将会把新来的任务加入到缓存队列里面去;缓存队列有多种:
  (1)ArrayBlockingQueue,是一种基于数组的有界阻塞队列,此队列按 FIFO(先进先出)原则对元素进行操作;
  (2)LinkedBlockingQueue,是一个基于链表的阻塞队列,此队列也按FIFO (先进先出)对元素进行操作,吞吐量通常要高于ArrayBlockingQueue, Executors.newFixedThreadPool()使用了这种队列;  
  (3)SynchronousQueue;是一种不存储元素的阻塞队列,每个插入操作必须等另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于LinkedBlockingQueue,Executors.newCachedThreadPool使用了这个队列;
  (4)PriorityBlockingQueue,是一种具有优先权的阻塞队列,优先级大的任务可以先执行,用户由此可以控制任务的执行顺序。这四种阻塞队列都有自己的使用场景,用户可以根据需要自己决定使用。
  
threadFactory: threadFactory 创建新线程时使用的工厂,threadFactory有两种选择:
  (1)DefaultThreadFactory,将创建一个同线程组且默认优先级的线程;
  (2)PrivilegedThreadFactory,使用访问权限创建一个权限控制的线程。ThreadPoolExecutor默认采用DefaultThreadFactory

handler: 当超出最大线程数和工作队列最大容量时,线程池采用的处理策略:
  (1)ThreadPoolExecutor.AbortPolicy(),将抛出RejectedExecutionException异常;
  (2)ThreadPoolExecutor.CallerRunsPolicy(),将重试添加当前的任务,重复调用execute()方法;
  (3)ThreadPoolExecutor.DiscardOldestPolicy(),将抛弃旧任务;
  (4)ThreadPoolExecutor.DiscardPolicy,将直接抛弃任务。ThreadPoolExecutor默认采用AbortPolicy。
工作原理:
  一个任务必须通过execute(Runnable)提交任务,执行是Runnable里面的run方法,当一个任务被添加到线程池里面去的时候,其处理:
  1. 如果线程数量小于corePoolSize,则创建新线程处理任务
  2. 如果线程数等于corePoolSize,但是WorkQueue没有装满,则会把任务加入到缓存队列
  3. 如果线程数等于corePoolSize并且WorkQueue已经装满,数量小于maximumPoolSize,则会创建新线程来处理任务
  4. 如果corePoolSize、workQueue和maximumPoolSize都满了,则采取handler的处理策略
  5. 当线程池中的线程数量大于corePoolSize时,如果某线程空闲时间超过keepAliveTime,线程将被终止。这样,线程池可以动态的调整池中的线程数。
  

Callable接口:

Java5版本以后,提供了可以返回String参数的线程接口Callable;实质上返回参数是从它的call()方法中返回的
使用方法:
    1. 用法类似于Runnable接口,编写类实现该接口;
    2. 使用ExecutorService.Submit()提交线程并执行;   
    3. 返回结果将保存在Future对象里面,通过它的get方法可以获取结果

    get是一个阻塞函数,它里面还有其他方法idDone判断是否执行完成
private static class myThread implements Callable<String>{

        @Override
        public String call() throws Exception {
            // TODO Auto-generated method stub
            return "String";
        }

    }

    public static void main(String[] args){
        ExecutorService es = Executors.newSingleThreadExecutor();
        Future<String> str = es.submit(new myThread());
        try {
            String s = str.get();
            System.out.println(s);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (ExecutionException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

后台线程:
前台线程结束,后台线程就结束;反之则不然;

设置后台线程方法:
1. 设置当前线程为后台线程:Thread里面的方法 public final void setDaemon(boolean on);凡是后台线程创建的线程均属于后台线程
2. 通过ThreadFactory设置线程池里面产生的都是后台线程
ExecutorService es = Executors.newCachedThreadPool(new DaemonThreadFactory());没看见在哪个包

可以使用public final boolean isDaemon()查看是否为后台程序;
在后台线程程序中放置try-catch-finally代码块,不一定能保证finally能够执行,因为你并不能确定程序何时停止,一旦非后程序全部结束,JVM就会关闭掉所有的后台程序

线程中捕获异常:
一般情况下,自己线程产生的异常只能自己捕获处理;但是有时候当前线程的异常逃逸后,其他线程也无法捕获处理,只能任由异常传输至控制台导致程序挂掉;为了解决这一问题,Java5提供了一套线程当中捕获异常的方法;该方法必须配合Executors使用也就是线程池,主要是在创建线程时,要设置thread的异常处理器
1. 创建一个异常处理器,该处理器中重写异常处理方法(该方法即是当前线程因为异常逃逸后导致线程死亡时使用它)

public class MyUncatchThreadHandler implements Thread.UncaughtExceptionHandler{

        //异常逃逸时调用
        @Override
        public void uncaughtException(Thread t, Throwable e) {
            // TODO Auto-generated method stub
            System.out.println("Thread: " + t.toString());
            System.out.println("Exception: " + e.toString());
        }

    }   
2. 创建一个异常工厂来实现上面的异常处理器
public class MyUncatchThreadFactory implements ThreadFactory{

        @Override
        public Thread newThread(Runnable arg0) {
            // TODO Auto-generated method stub
            Thread t = new Thread(arg0);
            //设置线程的异常处理器
            t.setUncaughtExceptionHandler(new MyUncatchThreadHandler());

            return t;
    }
3. 编写测试线程(注释掉try-catch后产生异常逃逸,会执行撒谎那个面的uncaughtException方法)
    public class ThreadTest implements Runnable{
        @Override
        public void run() {
            // TODO Auto-generated method stub
            Thread t = Thread.currentThread();
            System.out.println("ThreadTest: curretThread = " + t);
            try {
                throw new RuntimeException("我抛出了一个异常");
            } catch (Exception e) {
                // TODO Auto-generated catch block
                System.out.println("抓到异常了");
            }
        }

    }
4. 最后测试:
ExecutorService es = Executors.newCachedThreadPool(new MyUncatchThreadFactory());
        es.execute(new ThreadTest());;

        es.shutdown();
5. 如果要许多线程都要设置成相同的异常处理器的话,那建议在Thread里面设置一个默认的控制器,第四步骤可以改为:
Thread.setDefaultUncaughtExceptionHandler(new ExecutorTest.MyUncatchThreadHandler());

        ExecutorService es = Executors.newCachedThreadPool();

        es.execute(new ExecutorTest.ThreadTest());;

        es.shutdown();
posted @ 2015-09-24 23:45  帅气好男人_jack  阅读(2)  评论(0编辑  收藏  举报  来源