查看: 1198  |  回复: 0
创建线程的四种方法以及区别
GuoHui
2
主题
6
回复
发表于2019-01-18 18:55:56 | 只看该作者
1# 电梯直达
一:java中创建线程的四种方法以及区别
 Java使用Thread类代表线程,所有的线程对象都必须是Thread类或其子类的实例。Java可以用四种方式来创建线程,如下所示:

继承Thread类创建线程
实现Runnable接口创建线程
使用Callable和Future创建线程
使用线程池例如用Executor框架
下面让我们分别来看看这四种创建线程的方法。

1.通过继承Thread类来创建并启动多线程的一般步骤如下
定义Thread类的子类,并重写该类的run()方法,该方法的方法体就是线程需要完成的任务,run()方法也称为线程执行体。
创建Thread子类的实例,也就是创建了线程对象
启动线程,即调用线程的start()方法
代码实例

public class MyThread extends Thread{//继承Thread类
 
  public void run(){
 
  //重写run方法
 
  }
}
 
public class Main {
 
  public static void main(String[] args){
 
    new MyThread().start();//创建并启动线程
 
  }
}
2.实现Runnable接口创建线程
通过实现Runnable接口创建并启动线程一般步骤如下:

定义Runnable接口的实现类,一样要重写run()方法,这个run()方法和Thread中的run()方法一样是线程的执行体
创建Runnable实现类的实例,并用这个实例作为Thread的target来创建Thread对象,这个Thread对象才是真正的线程对象
第三部依然是通过调用线程对象的start()方法来启动线程
代码实例:
 

public class MyThread2 implements Runnable {//实现Runnable接口
  public void run(){
  //重写run方法
  }
}
 
public class Main {
 
  public static void main(String[] args){
    //创建并启动线程
    MyThread2 myThread=new MyThread2();
    Thread thread=new Thread(myThread);
    thread().start();
    //或者    new Thread(new MyThread2()).start();
  }
}
三:使用CallableFuture创建线程
和Runnable接口不一样,Callable接口提供了一个call()方法作为线程执行体,call()方法比run()方法功能要强大。

call()方法可以有返回值
call()方法可以声明抛出异常
Java5提供了Future接口来代表Callable接口里call()方法的返回值,并且为Future接口提供了一个实现类FutureTask,这个实现类既实现了Future接口,还实现了Runnable接口,因此可以作为Thread类的target。在Future接口里定义了几个公共方法来控制它关联的Callable任务。

>boolean cancel(boolean mayInterruptIfRunning):视图取消该Future里面关联的Callable任务

>V get():返回Callable里call()方法的返回值,调用这个方法会导致程序阻塞,必须等到子线程结束后才会得到返回值

>V get(long timeout,TimeUnit unit):返回Callable里call()方法的返回值,最多阻塞timeout时间,经过指定时间没有返回抛出TimeoutException

>boolean isDone():若Callable任务完成,返回True

>boolean isCancelled():如果在Callable任务正常完成前被取消,返回True

介绍了相关的概念之后,创建并启动有返回值的线程的步骤如下:

创建Callable接口的实现类,并实现call()方法,然后创建该实现类的实例(从java8开始可以直接使用Lambda表达式创建Callable对象)。
使用FutureTask类来包装Callable对象,该FutureTask对象封装了Callable对象的call()方法的返回值
使用FutureTask对象作为Thread对象的target创建并启动线程(因为FutureTask实现了Runnable接口)
调用FutureTask对象的get()方法来获得子线程执行结束后的返回值
代码实例:

public class Main {
 
  public static void main(String[] args){
   MyThread3 th=new MyThread3();
    //使用Lambda表达式创建Callable对象
    //使用FutureTask类来包装Callable对象
   FutureTask<Integer> future=new FutureTask<Integer>(
    (Callable<Integer>)()->{
      return 5;
    }
    );
   new Thread(task,"有返回值的线程").start();//实质上还是以Callable对象来创建并启动线程
    try{
    System.out.println("子线程的返回值:"+future.get());//get()方法会阻塞,直到子线程执行结束才返回
    }catch(Exception e){
    ex.printStackTrace();
   }
  }
}
四:使用线程池例如用Executor框架
    ExecutorService的生命周期包括三种状态:运行、关闭、终止。创建后便进入运行状态,当调用了shutdown()方法时,便进入关闭状态,此时意味着ExecutorService不再接受新的任务,但它还在执行已经提交了的任务,当素有已经提交了的任务执行完后,便到达终止状态。如果不调用shutdown()方法,ExecutorService会一直处在运行状态,不断接收新的任务,执行新的任务,服务器端一般不需要关闭它,保持一直运行即可。

  Executors提供了一系列工厂方法用于创先线程池,返回的线程池都实现了ExecutorService接口。  

   1.创建固定数目线程的线程池。

   public static ExecutorService newFixedThreadPool(int nThreads)

   2.创建一个可缓存的线程池,调用execute将重用以前构造的线程(如果线程可用)。如果现有线程没有可用的,则创建一个新线   程并添加到池中。终止并从缓存中移除那些已有 60 秒钟未被使用的线程。

   public static ExecutorService newCachedThreadPool()

   3.创建一个单线程化的Executor。

   public static ExecutorService newSingleThreadExecutor()

   4.创建一个支持定时及周期性的任务执行的线程池,多数情况下可用来替代Timer类。

   public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)

具体分析
  newCachedThreadPool()                                                                                                                                          
-缓存型池子,先查看池中有没有以前建立的线程,如果有,就 reuse.如果没有,就建一个新的线程加入池中
-缓存型池子通常用于执行一些生存期很短的异步型任务
 因此在一些面向连接的daemon型SERVER中用得不多。但对于生存期短的异步任务,它是Executor的首选。
-能reuse的线程,必须是timeout IDLE内的池中线程,缺省     timeout是60s,超过这个IDLE时长,线程实例将被终止及移出池。
  注意,放入CachedThreadPool的线程不必担心其结束,超过TIMEOUT不活动,其会自动被终止。
 


   newFixedThreadPool(int)                                                       
-newFixedThreadPool与cacheThreadPool差不多,也是能reuse就用,但不能随时建新的线程
-其独特之处:任意时间点,最多只能有固定数目的活动线程存在,此时如果有新的线程要建立,只能放在另外的队列中等待,直到当前的线程中某个线程终止直接被移出池子
-和cacheThreadPool不同,FixedThreadPool没有IDLE机制(可能也有,但既然文档没提,肯定非常长,类似依赖上层的TCP或UDP IDLE机制之类的),所以FixedThreadPool多数针对一些很稳定很固定的正规并发线程,多用于服务器
-从方法的源代码看,cache池和fixed 池调用的是同一个底层 池,只不过参数不同:
fixed池线程数固定,并且是0秒IDLE(无IDLE)    
cache池线程数支持0-Integer.MAX_VALUE(显然完全没考虑主机的资源承受能力),60秒IDLE  
 
newScheduledThreadPool(int) 
-调度型线程池
-这个池子里的线程可以按schedule依次delay执行,或周期执行
 
SingleThreadExecutor() 
-单例线程,任意时间池中只能有一个线程
-用的是和cache池和fixed池相同的底层池,但线程数目是1-1,0秒IDLE(无IDLE)
具体代码实现

一:newCachedThreadPool方式
public static void main(String[] args) {
 ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
 for (int i = 0; i < 10; i++) {
 final int index = i;
 try {
 Thread.sleep(10);
 } catch (InterruptedException e) {
 e.printStackTrace();
 }
 cachedThreadPool.execute(new Runnable() {
 public void run() {
 System.out.println(index);
 }
 });
 }
 }
   创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。

这里的线程池是无限大的,当一个线程完成任务之后,这个线程可以接下来完成将要分配的任务,而不是创建一个新的线程,

java api 1.7 will reuse previously constructed threads when they are available.

二:newFixedThreadPool 方式
public static void main(String[] args) {
     ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
 for (int i = 0; i < 10; i++) {
 final int index = i;
 fixedThreadPool.execute(new Runnable() {
 public void run() {
 try {
 System.out.println(index);
 Thread.sleep(10);
 } catch (InterruptedException e) {
 e.printStackTrace();
 }
 }
 });
 }
 }
创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待

定长线程池的大小最好根据系统资源进行设置。如Runtime.getRuntime().availableProcessors()

三:ScheduledThreadPool
public static void main(String[] args) {
 ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
 for (int i = 0; i < 10; i++) {
 scheduledThreadPool.schedule(new Runnable() {
 public void run() {
 System.out.println("delay 3 seconds");
 }
 }, 3, TimeUnit.SECONDS);
 }
 }
四:newSingleThreadExecutor 
public static void main(String[] args) {
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
 for (int i = 0; i < 10; i++) {
 final int index = i;
 singleThreadExecutor.execute(new Runnable() {
 public void run() {
 /*System.out.println(index);*/
 try {
 System.out.println(index);
 Thread.sleep(2000);
 } catch (InterruptedException e) {
 e.printStackTrace();
 }
 }
 });
 }
 }
 

主题

回复
  • 温馨提示: 标题不合格、重复发帖、发布广告贴,将会被删除帖子或禁止发言。 详情请参考: 社区发帖规则
  • 您当前输入了 0 个文字。还可以输入 8000 个文字。 已添加复制上传图片功能,该功能目前仅支持chrome和火狐

禁言/删除

X
请选择禁言时长:
是否清除头像:
禁言/删除备注:
昵 称:
 
温馨提示:昵称只能设置一次,设置后无法修改。
只支持中文、英文和数字。

举报

X
请选择举报类型:
请输入详细内容:

顶部