四种创建线程的方式

最近一直在刷面试题,刷到“如何创建进程”,所以记录下来

  进程一直是面试的热点问题,创建进程的方式有四种,分别是继承Thread类创建线程实现Runnable接口创建线程使用Callable和Future创建线程使用线程池例如用Executor框架

1 继承Thread类创建线程

  通过继承Thread类来创建并启动多线程,其代码如下:

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接口创建并启动线程,其代码如下:

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

3 使用Callable和Future创建线程

  和Runnable接口不一样,Callable接口提供了一个call()方法作为线程执行体,call()方法比run()方法功能要强大。call()方法可以有返回值;call()方法可以声明抛出异常。相关代码如下:

public class ThreadDemo implements Callable<Integer> {
    @Override
    public Integer call() throws Exception {
        int sum = 0;
        /**
         * 求得0到100000的自然数相加,并输出过程值。
         */
        for (int i = 0; i <= 100000; i++) {
            System.out.println(i);
            sum += i;
        }
        return sum;
    }
}

public class Main {
  public static void main(String[] args){
    ThreadDemo threadDemo =new ThreadDemo();
        // 1.执行Callable方式,需要FutureTask实现类的支持,用于接收运算结果
        FutureTask<Integer> task = new FutureTask<Integer>(threadDemo);
        new Thread(task).start();
        // 2.接收线程运算后的结果
        Integer sum;
        try {
            //等所有线程执行完,获取值,因此FutureTask 可用于 闭锁
            sum = task.get();
            System.out.println("-----------------------------");
            System.out.println(sum);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
  }
}

4 使用线程池例如用Executor框架

  1.5后引入的Executor框架的最大优点是把任务的提交和执行解耦。要执行任务的人只需把Task描述清楚,然后提交即可。

4.1 Executor执行Runnable任务

  通过Executors的以上四个静态工厂方法获得 ExecutorService实例,而后调用该实例的execute(Runnable command)方法即可。一旦Runnable任务传递到execute()方法,该方法便会自动在一个线程上,相关代码如下:

public class TestRunnable implements Runnable{
    @Override
    public void run() {
        System.out.println("第四种创建线程的方式");
    }
}

public class Main {
  public static void main(String[] args){
  		ExecutorService executorService = Executors.newCachedThreadPool();
	        for (int i = 0; i < 5; i++){
	            // 接收Runnable实例
	            executorService.execute(new TestRunnable());
	            System.out.println("************* a" + i + " *************");
	        }
	        // 平滑关闭ExecutorService
	        executorService.shutdown();
  }
}

4.2 Executor执行Callable任务

  在Java 5之后,任务分两类:一类是实现了Runnable接口的类,一类是实现了Callable接口的类。两者都可以被ExecutorService执行,但是Runnable任务没有返回值,而Callable任务有返回值。并且Callable的call()方法只能通过ExecutorService的submit(Callable task) 方法来执行,并且返回一个 Future,是表示任务等待完成的 Future。相关代码如下:

public class TaskWithResult implements Callable<String> {
    private int id;
    /**
     * 构造方法
     * @param id
     */
    public TaskWithResult(int id){   
        this.id = id;   
    }   
    /**  
     * 任务的具体过程,一旦任务传给ExecutorService的submit方法, 
     * 则该方法自动在一个线程上执行 
     */   
    @Override
    public String call() throws Exception {
        System.out.println("call()方法被自动调用!!!    " + Thread.currentThread().getName());   
        //该返回结果将被Future的get方法得到  
        return "call()方法被自动调用,任务返回的结果是:" + id + "    " + Thread.currentThread().getName();   
    }   
}

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);
	        }
	        //遍历任务的结果
	        for (Future<String> fs : resultList){
	            try{
	                while(!fs.isDone()) {
	                    ;//Future返回如果没有完成,则一直循环等待,直到Future返回完成
	                }
	                //打印各个线程(任务)执行的结果
	                System.out.println(fs.get());
	            }catch(InterruptedException e){
	                e.printStackTrace();
	            }catch(ExecutionException e){
	                e.printStackTrace();
	            }finally{
	                //启动一次顺序关闭,执行以前提交的任务,但不接受新任务
	                executorService.shutdown();
	            }
	        }
  }
}

总结

  以上方法的代码都可以运行,并能执行成功,前三种方式理解起来非常简单,最后一种,要理解就必须看一下Executor框架知识。链接如下

发布了39 篇原创文章 · 获赞 5 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/a1920135141/article/details/100667642