【多线程 二】线程创建和启动的四种方式(以及详解Thread和Runnable方式的优缺点)

1、继承Thread
public class Threadextend extends Thread{
    @Override
    public void run() {
        for(int i=0;i<=100;i++){
            System.out.print("A"+i+ "\t");
        }
    }
}
public class Main {
    public static void main(String[] args) {
        Threadextend trea=new Threadextend("小强");
        //start 表示开启线程
        trea.start();
        for(int i=0 ;i <100;i++){
            System.out.print("B"+i +"\t");
        }
        System.out.println(trea.getName());
    }
}

结果如下,随机打印
在这里插入图片描述

2、实现runnable接口

实现Runnable接口比继承Thread类所具有的优势:
1. 适合多个相同的程序代码的线程去共享同一个资源。 (注意我说的是适合,这里我从网上看到了一些人的回答(比如 https://www.cnblogs.com/fxust/p/8998696.html ),都说thread不能资源共享,可能是断章取义吧,Thread可以实现资源共享的,只不过是现实开发中它不适合资源共享,因为它如果想资源共享的话可以将共享的资源设置成静态的,因为静态资源的生命周期是和类绑在一起的,和对象没有关系。但是这样做的坏处就是,如果此时有两个不一样的任务的话,同时都调用这个类,那么就不能实现了,因为静态变量不能区分这两个任务了 )

2、 可以避免java中的单继承的局限性。 (就是说接口的好处了,实现完接口后还可以继承)

3、增加程序的健壮性,实现解耦操作,代码可以被多个线程共享,代码和线程独立。
从代码架构角度:具体的任务(run方法)应该和“创建和运行线程的机制(Thread类)”解耦

4、线程池只能放入实现Runable或Callable类线程,不能直接放入继承Thread的类。

class ThreadRunnable implements Runnable{
    @Override
    public void run() {
        for(int i=0;i<=100;i++){
            System.out.print("A"+i+ "\t");
        }
    }
}
 public static void main(String[] args) {
    ThreadRunnable threadRunnable=new ThreadRunnable();
        Thread thread = new Thread(threadRunnable, "小强");
        thread.start();
        for(int i=0 ;i <100;i++){
            System.out.print("B"+i +"\t");
        }
        System.out.println(thread.getName());
    }

结果同上

3、jdk1.5后,实现callable接口

和以上相比,callable更加强大一些
1:相比run()方法,可以有返回值
2:方法可以抛出异常
3:支持泛型的返回值
4:需要借助FutureTask类,比如获取返回结果

class ThreadCallable implements Callable<Integer>{
    int result=0;
    @Override
    public Integer call() throws Exception {
        for(int i=0;i<=100;i++){
            System.out.println(i);
            result +=i;
        }
        return result;
    }
}
    public static void main(String[] args) {
        ThreadCallable threadCallable = new ThreadCallable();
        FutureTask<Integer> result= new FutureTask<>(threadCallable);
        new Thread(result).start();
        try {
            System.out.println(result.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
4、jdk1.5后,线程池

提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中。可以避免频繁创建销毁、实现重复利用。提高响应速度 (减少了创建新线程的时间) 降低资源消耗(重复利用线程池中线程,不需要每次都创建) 便于线程管理。

4.1体系结构

在这里插入图片描述
如上图,顶级接口为Executor,真正的线程池接口是ExecutorService,下面是对上图的说明
在这里插入图片描述

4.2 工具类:Executors

Executors.newCachedThreadPool():创建一个可根据需要创建新线程的线程池

Executors.newFixedThreadPool(n); 创建一个可重用固定线程数的线程池

Executors.newSingleThreadExecutor() :创建一个只有一个线程的线程池

Executors.newScheduledThreadPool(n):创建一个线程池,它可安排在给定延迟后运
行命令或者定期地执行。 返回值类型为ScheduleThreadPoolExeccutor。

class ThreadRunnable implements Runnable{
    @Override
    public void run() {
        for(int i=0;i<=100;i++){
            System.out.print(Thread.currentThread().getName()+ "\t"+ "A"+i+ "\t");
        }
    }
}

线程池与Runnable的使用方式

 public static void main(String[] args) {
        // 创建线程池
         ExecutorService pool = Executors.newFixedThreadPool(5);

         ThreadRunnable trb=new ThreadRunnable();
        //为线程池分配任务
        for(int i=0;i<10;i++){
            pool.submit(trb);
        }
        //关闭线程池,shutdown只是将线程池的状态设置为SHUTWDOWN状态,
        //正在执行的任务会继续执行下去,没有被执行的则中断。而shutdownNow
        //则是将线程池的状态设置为STOP,正在执行的任务则被停止,没被执行任务的则返回。
        pool.shutdown(); 
 }

线程池与Callable的使用方式, 带有返回值Future

 // 创建线程池
        ExecutorService pool = Executors.newFixedThreadPool(5);
        List<Future<Integer>> list = new ArrayList<>();

        //为线程池分配任务
        for(int i=0;i<10;i++){
            Future<Integer> future = pool.submit(new Callable<Integer>() {
                @Override
                public Integer call() throws Exception {
                    int sum=0;
                    for (int j = 0; j <100 ; j++) {
                        sum+=j;
                    }
                    return sum;
                }
            });
            list.add(future);
        }
        //关闭线程池
        pool.shutdown();
        for (Future<Integer> future : list) {
            System.out.println(future.get());
        }
5、延申:start() 和run()有什么区别呢?

run():只是调用了一个普通方法,并没有启动另一个线程,程序还是会按照顺序执行相应的代码。线程被调度的时候,执行的操作
start():重新开启一个线程,此线程进入到就绪状态,不必等待其他线程运行完,只要得到cup资源就可以运行该线程,如下图,这个图在后面的博文中会有详细的讲解

在这里插入图片描述

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值