以项目为牵引   全程实训   以就业为导向  以动手能力为目标

一样的教育    不一样的结果     全国咨询热线:400-078-8286

Java 使用new Thread和线程池的区别

2018-12-27 来源:桂林志成IT教育中心  点击量:863
摘要:Java 使用new Thread和线程池的区别

1.new Thread的弊端
执行一个异步任务你还只是如下new Thread吗

new Thread(new Runnable() {
 
    @Override
   
public void run() {
       
// TODO Auto-generated method stub
    }
}).start();

 

 说说弊端:

a. 每次new Thread新建对象性能差。
b. 线程缺乏统一管理,可能无限制新建线程,相互之间竞争,及可能占用过多系统资源导致死机或oom。
c. 缺乏更多功能,如定时执行、定期执行、线程中断。
相比new Thread,Java提供的四种线程池的好处在于:
a. 重用存在的线程,减少对象创建、消亡的开销,性能佳。
b. 可有效控制最大并发线程数,提高系统资源的使用率,同时避免过多资源竞争,避免堵塞。
c. 提供定时执行、定期执行、单线程、并发数控制等功能。

2.Executors提供四种线程池

newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。线程池的规模不存在限制。
newFixedThreadPool 创建一个固定长度线程池,可控制线程最大并发数,超出的线程会在队列中等待。
newScheduledThreadPool 创建一个固定长度线程池,支持定时及周期性任务执行。
newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。

 下面代码说明:

(1). newCachedThreadPool
创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。示例代码如下:

ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
for (int i = 0; i < 10; i++) {
   
final int index = i;
   
try {
        Thread.sleep(index
* 1000);
    }
catch (InterruptedException e) {
        e.printStackTrace();
    }
 
    cachedThreadPool.execute(
new Runnable() {
 
        @Override
       
public void run() {
            System.out.println(index);
        }
    });
}

 线程池为无限大,当执行第二个任务时第一个任务已经完成,会复用执行第一个任务的线程,而不用每次新建线程。

(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();
            }
        }
    });
}

因为线程池大小为3,每个任务输出index后sleep 2秒,所以每两秒打印3个数字。

定长线程池的大小最好根据系统资源进行设置。

 

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

ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
scheduledThreadPool.schedule(
new Runnable() {
 
    @Override
   
public void run() {
        System.out.println(
"delay 3 seconds");
    }
},
3, TimeUnit.SECONDS);

 表示延迟3秒执行。

定期执行示例代码如下:

scheduledThreadPool.scheduleAtFixedRate(new Runnable() {
 
    @Override
   
public void run() {
        System.out.println(
"delay 1 seconds, and excute every 3 seconds");
    }
},
1, 3, TimeUnit.SECONDS);

表示延迟1秒后每3秒执行一次。

ScheduledExecutorService比Timer更安全,功能更强大。

(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();
            }
        }
    });
}

 结果依次输出,相当于顺序执行各个任务。

2.ExecutorService中submit和execute的区别

以下这是submit 的源码:

public abstract class AbstractExecutorService implements ExecutorService {
   
   
protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) {
       
return new FutureTask<T>(runnable, value);
    }

   
protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
       
return new FutureTask<T>(callable);
    }

   
public Future<?> submit(Runnable task) {
       
if (task == null) throw new NullPointerException();
        RunnableFuture
<Void> ftask = newTaskFor(task, null);
        execute(ftask);
       
return ftask;
    }

   
public <T> Future<T> submit(Runnable task, T result) {
       
if (task == null) throw new NullPointerException();
        RunnableFuture
<T> ftask = newTaskFor(task, result);
        execute(ftask);
       
return ftask;
    }

   
public <T> Future<T> submit(Callable<T> task) {
       
if (task == null) throw new NullPointerException();
        RunnableFuture
<T> ftask = newTaskFor(task);
        execute(ftask);
       
return ftask;
    }
  
//....
}

可以看出submit最终返回的是FutureTask对象,而execute:

public interface Executor {
    
    void execute(Runnable command);
}

具体的实现在ThreadPoolExecutor类中

public void execute(Runnable command) {
       
if (command == null)
           
throw new NullPointerException();

       
int c = ctl.get();
       
if (workerCountOf(c) < corePoolSize) {
           
if (addWorker(command, true))
               
return;
            c
= ctl.get();
        }
       
if (isRunning(c) && workQueue.offer(command)) {
           
int recheck = ctl.get();
           
if (! isRunning(recheck) && remove(command))
                reject(command);
           
else if (workerCountOf(recheck) == 0)
                addWorker(
null, false);
        }
       
else if (!addWorker(command, false))
            reject(command);
    }

所以,submit内部调用execute,且submit有返回值,方便exception处理。

submit Demo:

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class Main {
   
public static void main(String[] args) {
        ExecutorService executorService
= Executors.newCachedThreadPool();
        List
<Future<String>> resultList = new ArrayList<Future<String>>();

       
// 创建10个任务并执行
       
for (int i = 0; i < 10; i++) {
           
// 使用ExecutorService执行Callable类型的任务,并将结果保存在future变量中
            Future<String> future = executorService.submit(
new TaskWithResult(i));
           
// 将任务执行结果存储到List中
            resultList.add(future);
        }
        executorService.shutdown();

       
// 遍历任务的结果
       
for (Future<String> fs : resultList) {
           
try {
                System.out.println(fs.get());
// 打印各个线程(任务)执行的结果
            }
catch (InterruptedException e) {
                e.printStackTrace();
            }
catch (ExecutionException e) {
                executorService.shutdownNow();
                e.printStackTrace();
               
return;
            }
        }
    }
}
class TaskWithResult implements Callable<String> {
   
private int id;

   
public TaskWithResult(int id) {
       
this.id = id;
    }

   
/**
     * 任务的具体过程,一旦任务传给ExecutorService的submit方法,则该方法自动在一个线程上执行。
     *
     *
@return
     *
@throws Exception
    
*/
   
public String call() throws Exception {
        System.out.println(
"call()方法被自动调用,干活!!!             " + Thread.currentThread().getName());
       
if (new Random().nextBoolean())
           
throw new TaskException("Meet error in task." + Thread.currentThread().getName());
       
// 一个模拟耗时的操作
       
for (int i =9; i > 0; i--)
            ;
       
return "call()方法被自动调用,任务的结果是:" + id + "    " + Thread.currentThread().getName();
    }
}

class TaskException extends Exception {
   
public TaskException(String message) {
       
super(message);
    }
}

 

Runnable和Callable的区别是,
(1)Callable规定的方法是call(),Runnable规定的方法是run().
(2)Callable的任务执行后可返回值,而Runnable的任务是不能返回值得
(3)call方法可以抛出异常,run方法不可以
(4)运行Callable任务可以拿到一个Future对象,表示异步计算的结果。它提供了检查计算是否完成的方法,以等待计算的完成,并检索计算的结果。通过Future对象可以了解任务执行情况,可取消任务的执行,还可获取执行结果。

热门标签:使用 线程 区别

热门课程

相关文章>

合作伙伴