Java实现多线程四种方式

  • Post author:
  • Post category:java



Java

多线程实现方式主要有四种:继承Thread类、实现Runnable接口、实现Callable接口通过FutureTask包装器来创建Thread线程、使用ExecutorService、Callable、Future实现有返回结果的多线程。

1、继承Thread类

Thread类本质上是实现了Runnable接口的一个类,代表一个线程的实例,Runnable接口中只定义了一个方法run()方法。启动线程的唯一方法就是通过Thread类的start()实例方法。start()方法是一个native方法,它将启动一个新线程,并执行run()方法。这种方式实现多线程很简单,通过自己的类直接extend Thread,并复写run()方法,就可以启动新线程并执行自己定义的run()方法。例如:

public class MyThread extends Thread {  
  public void run() {  
   System.out.println("MyThread.run()");  
  }  
}  
 
MyThread myThread1 = new MyThread();  
MyThread myThread2 = new MyThread();  
myThread1.start();  
myThread2.start();

2、实现Runnable接口

Java语言是单继承,所以在一些类继承了其他类之后,可以通过实现Runnable接口实现多线程

public class TestThread {

    public static void main(String[] args) {
        
        //匿名方法通过直接实现Runnable接口实现多线程
        new Thread(new Runnable(){
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName());
            }
        }).start();
    }
}

3、实现Callable接口

实现Callable接口可以返回线程执行结果,通过FutureTask实现类

public class DemoCallable {
    public static void main(String[] args) {
        MyCallable myCallable = new MyCallable();
        FutureTask<Integer> result = new FutureTask<Integer>(myCallable);
        new Thread(result).start();

        try {
            System.out.println(result.get());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

class MyCallable implements Callable<Integer>{

    @Override
    public Integer call() throws Exception {
        int sum = 0;
        for(int i = 0;i < 10;i ++){
            sum ++;
        }
        return sum;
    }
}
  1. (1)Callable规定的方法是call(),而Runnable规定的方法是run().
  2. (2)Callable的任务执行后可返回值,而Runnable的任务是不能返回值的。
  3. (3)call()方法可抛出异常,而run()方法是不能抛出异常的。
  4. (4)运行Callable任务可拿到一个Future对象, Future表示异步计算的结果。
  5. 它提供了检查计算是否完成的方法,以等待计算的完成,并检索计算的结果。
  6. 通过Future对象可了解任务执行情况,可取消任务的执行,还可获取任务执行的结果。
  7. Callable是类似于Runnable的接口,实现Callable接口的类和实现Runnable的类都是可被其它线程执行的任务。

4、线程池

Java通过Executors提供四种线程池,分别为:

newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。

newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。

newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。

newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。

4.1、newCachedThreadPool

public class CachedTreadPool {

    public static void main(String[] args) {
        ExecutorService cachedPool = Executors.newCachedThreadPool();

        for(int i = 0; i< 10; i++){
            final int index = i;
            try {
                Thread.sleep(index * 1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            cachedPool.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName() + index);
                }
            });
        }
    }
}

4.2、newFixedThreadPool

ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
for (int i = 0; i < 10; i++) {
    final int index = i;
    fixedThreadPool.execute(new Runnable() {


        @Override
        public void run() {
            try {
                System.out.println(index);
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    });
}

4.3、newScheduledThreadPool

创建一个定长线程池,支持定时及周期性任务执行。延迟执行示例代码如下:

ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
scheduledThreadPool.schedule(new Runnable() {

    @Override
    public void run() {
        System.out.println("delay 3 seconds");
    }
}, 3, TimeUnit.SECONDS);

4.4、newSingleThreadExecutor

创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。示例代码如下:

ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
for (int i = 0; i < 10; i++) {
    final int index = i;
    singleThreadExecutor.execute(new Runnable() {

        @Override
        public void run() {
            try {
                System.out.println(index);
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    });
}

5、线程池的核心参数

1、corePoolSize:核心线程数

* 核心线程会一直存活,及时没有任务需要执行

* 当线程数小于核心线程数时,即使有线程空闲,线程池也会优先创建新线程处理

* 设置allowCoreThreadTimeout=true(默认false)时,核心线程会超时关闭

2、queueCapacity:任务队列容量(阻塞队列)

* 当核心线程数达到最大时,新任务会放在队列中排队等待执行

3、maxPoolSize:最大线程数

* 当线程数>=corePoolSize,且任务队列已满时。线程池会创建新线程来处理任务

* 当线程数=maxPoolSize,且任务队列已满时,线程池会拒绝处理任务而抛出异常

4、 keepAliveTime:线程空闲时间

* 当线程空闲时间达到keepAliveTime时,线程会退出,直到线程数量=corePoolSize

* 如果allowCoreThreadTimeout=true,则会直到线程数量=0

5、allowCoreThreadTimeout:允许核心线程超时

6、rejectedExecutionHandler:任务拒绝处理器

* 两种情况会拒绝处理任务:

– 当线程数已经达到maxPoolSize,切队列已满,会拒绝新任务

– 当线程池被调用shutdown()后,会等待线程池里的任务执行完毕,再shutdown。如果在调用shutdown()和线程池真正shutdown之间提交任务,会拒绝新任务

* 线程池会调用rejectedExecutionHandler来处理这个任务。如果没有设置默认是AbortPolicy,会抛出异常

* ThreadPoolExecutor类有几个内部实现类来处理这类情况:

– AbortPolicy 丢弃任务,抛运行时异常

– CallerRunsPolicy 执行任务

– DiscardPolicy 忽视,什么都不会发生

– DiscardOldestPolicy 从队列中踢出最先进入队列(最后一个执行)的任务

* 实现RejectedExecutionHandler接口,可自定义处理器

ThreadPoolExecutor执行顺序

线程池按以下行为执行任务

1. 当线程数小于核心线程数时,创建线程。

2. 当线程数大于等于核心线程数,且任务队列未满时,将任务放入任务队列。

3. 当线程数大于等于核心线程数,且任务队列已满

– 若线程数小于最大线程数,创建线程

– 若线程数等于最大线程数,抛出异常,拒绝任务



版权声明:本文为GM_115原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。