创建线程的几种方法

继承Thread类

(1)创建一个类继承Thread类,重写run()方法,将所要完成的任务代码写进run()方法中;
(2)创建Thread类的子类的对象;
(3)调用该对象的start()方法

start()方法表示启用该线程,之后会自动调用run()方法,程序打印的就是Thread;如果直接调用run方法,那程序打印的就是main

实例代码

public class Main2 {

    public static void main(String[] args) throws Exception {
        new Thread1().start();
        //new Thread1().run();
    }
}

class Thread1 extends Thread {

    public void run() {
            System.out.println(Thread.currentThread().getName());
        }
    }
}

实现Runnable接口

(1)创建一个类并实现Runnable接口
(2)重写run()方法,将所要完成的任务代码写进run()方法中
(3)创建实现Runnable接口的类的对象,将该对象当做Thread类的构造方法中的参数传进去
(4)使用Thread类的构造方法创建一个对象,并调用start()方法即可运行该线程


public class Main2 {

    public static void main(String[] args) throws Exception {

        new Thread(new Thread2()).start();
    }    
}

class Thread2 implements Runnable{

    @Override
    public void run() {
        System.out.println("Runnable...."+Thread.currentThread().getName());
    }
}

实现Callable接口

(1)创建一个类并实现Callable接口
(2)重写call()方法,将所要完成的任务的代码写进call()方法中,需要注意的是call()方法有返回值,并且可以抛出异常
(3)如果想要获取运行该线程后的返回值,需要创建Future接口的实现类的对象,即FutureTask类的对象,调用该对象的get()方法可获取call()方法的返回值
(4)使用Thread类的有参构造器创建对象,将FutureTask类的对象当做参数传进去,然后调用start()方法开启并运行该线程。

public class Main2 {

    public static void main(String[] args) throws Exception {

        FutureTask<String> task = new FutureTask<String>(new Thread4());
        Thread thread = new Thread(task);
        thread.start();

        String s = task.get();
        System.out.println(s);
    }
}

class Thread4 implements Callable{

    @Override
    public Object call() throws Exception {
        System.out.println(Thread.currentThread().getName()+"-----打印的");
        return Thread.currentThread().getName()+"----返回的";
    }
}

Callable和Runnable的异同点

相同点

都是接口
都可以编写多线程程序
都采用Thread.start()启动线程

不同点

1 Callable规定的方法是call(),而Runnable规定的方法是run().
2 Callable的任务执行后可返回值,而Runnable的任务是不能返回值的。
3 call()方法可抛出异常,而run()方法是不能抛出异常的。–run()方法异常只能在内部消化,不能往上继续抛
4 运行Callable任务可拿到一个Future对象, Future表示异步计算的结果。
5 它提供了检查计算是否完成的方法,以等待计算的完成,并检索计算的结果。
6 通过Future对象可了解任务执行情况,可取消任务的执行,还可获取任务执行的结果。
7 Callable是类似于Runnable的接口,实现Callable接口的类和实现Runnable的类都是可被其它线程执行的任务。
注:Callalbe接口支持返回执行结果,需要调用FutureTask.get()得到,此方法会阻塞主进程的继续往下执行,如果不调用不会阻塞。

使用线程池Executors

(1)使用Executors类中的newFixedThreadPool(int num)方法创建一个线程数量为num的线程池
(2)调用线程池中的execute()方法执行由实现Runnable接口创建的线程;调用submit()方法执行由实现Callable接口创建的线程
(3)调用线程池中的shutdown()方法关闭线程池

public class ThreadPoolTest {
    public static void main(String[] args) {
        //test1();
        //test2();
        //test3();
        //test4();
        //test5();
    }

    public static void test1(){
        ExecutorService executorService = Executors.newCachedThreadPool();
        for (int i=0 ; i < 100 ; i++){
            int temp = i;
            executorService.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println("threadName;"+Thread.currentThread().getName()+",i"+temp);
                }
            });
        }
    }

    public static void test2(){
        ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(3);
        for (int i=0 ; i < 3 ; i++){
            int temp = i;
            scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName()+"------------------------"+temp);
                }

            }, 1,3, TimeUnit.SECONDS);
        }
    }

    public static void test3(){
        ExecutorService executorService = Executors.newFixedThreadPool(3);
        for (int i=0 ; i < 10 ; i++){
            int temp = i;
            executorService.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println("delay 3 seconds");
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
        }
    }

    public static void test4(){
        ExecutorService executorService = Executors.newSingleThreadExecutor();
        for (int i=0 ; i < 10 ; i++){
            int temp = i;
            executorService.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName()+"------------------------"+temp);
                }
            });
        }
    }

    public void test5(){
        ExecutorService executorService = Executors.newWorkStealingPool();
        for (int i=0 ; i < 10 ; i++){
            int temp = i;
            executorService.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName()+"------------------------"+temp);
                }
            });
        }
    }

}

  • newSingleThreadPool,为单核心线程池,最大线程也只有一个它的线程数存活时间是无限的

    创建一个执行器,该执行器使用一个工作线程操作一个无界队列。(但是请注意,如果这个线程在关闭之前的执行过程中由于失败而终止,那么如果需要执行后续任务,将会有一个新的线程替代它。)任务保证按顺序执行,并且在任何给定时间都不会有多个任务处于活动状态。与其他等价的newFixedThreadPool(1)不同,返回的执行器保证不可重新配置以使用其他线程

  • newFixedThreadPool,我们需要传入一个固定的核心线程数,并且核心线程数等于最大线程数,而且它们的线程数存活时间都是无限的

  • newCachedThreadPool,可以进行缓存的线程池,意味着它的线程数是最大的,无限的。但是核心线程数为 0,这没关系。这里要考虑线程的摧毁,因为不能够无限的创建新的线程,所以在一定时间内要摧毁空闲的线程。但是由于这种线程池创建时初始化的都是无界的值,一个是最大线程数,一个是任务的阻塞队列,都没有设置它的界限,这可能会出现问题。

  • newScheduledThreadPool,这个表示的是有计划性的线程池,就是在给定的延迟之后运行,或周期性地执行。很好理解,大家应该用过 Timer 定时器类吧,这两个差不多的意思

  • newWorkStealingPool,这个是 JDK1.8 版本加入的一种线程池,stealing 翻译为抢断、窃取的意思,它实现的一个线程池和上面4种都不一样,用的是 ForkJoinPool 类。最明显的用意就是它是一个并行的线程池,参数中传入的是一个线程并发的数量,这里和之前就有很明显的区别,前面4种线程池都有核心线程数、最大线程数等等,而这就使用了一个并发线程数解决问题。从介绍中,还说明这个线程池不会保证任务的顺序执行,也就是 WorkStealing 的意思,抢占式的工作。

对比了以上 5 种线程池,我们看到每个线程池都有自己的特点,这也是为我们封装好的一些比较常用的线程池。当然,我建议你在使用(3)可缓存的线程池时,尽量的不要用默认的那个来创建,因为默认值都是无界的,可能会出现一些问题,这时我们可以参考源码中的线程池初始化参数的设置,可以尽可能的避免错误发生。
newSingleThreadPool()与newFixedThreadPool(1)的不同,前者线程池中的线程数量是不可重新配置的,也即不能去加入额外的线程。

corePoolSize : 表示线程池核心线程数,当初始化线程池时,会创建核心线程进入等待状态,即使它是空闲的,核心线程也不会被摧毁,从而降低了任务一来时要创建新线程的时间和性能开销。
maximumPoolSize : 表示最大线程数,意味着核心线程数都被用完了,那只能重新创建新的线程来执行任务,但是前提是不能超过最大线程数量,否则该任务只能进入阻塞队列进行排队等候,直到有线程空闲了,才能继续执行任务。
keepAliveTime : 表示线程存活时间,除了核心线程外,那些被新创建出来的线程可以存活多久。意味着,这些新的线程一但完成任务,而后面都是空闲状态时,就会在一定时间后被摧毁。
unit : 存活时间单位,没什么好解释的,一看就懂。
workQueue : 表示任务的阻塞队列,由于任务可能会有很多,而线程就那么几个,所以那么还未被执行的任务就进入队列中排队,队列我们知道是 FIFO 的,等到线程空闲了,就以这种方式取出任务。这个一般不需要我们去实现。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值