从代码认识线程的创建方式

从代码认识线程的创建方法

一般我们将线程的创建分为四种方式:

1.继承Thread类创建线程

2.实现Runnable接口创建线程

3.使用Callable和Future创建线程(Future接口是操作线程的接口)

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

一、非并发类的实现Runnable的创建方式

public class MyThread0 {
    public static void main(String[] args) {
        //第一种,继承Thread类重写run方法
        new MyThreadOne();
        //第二种,直接实现Runnable接口
        new MyThreadTwo();
        //第三种 Runnable接口是函数式接口,这种方法是利用Thread类中的构造方法创建一个重写run方法后的线程,最推荐
        new Thread(()-> System.out.println("线程执行"));
    }
}
第一种 继承Thread类重写run方法

第一种,创建一个自定义线程类,该类继承Thread类

这里的run方法虽然不是必须实现,但是继承Thread创建线程的目的就是重写他的run方法

Thread里面默认的run方法是调用自己属性中的Runnable,这个Runnable在没有赋值时候是空的,赋值通过构造函数赋值也就是Thread(new Runnable(){run方法})

class MyThreadOne extends Thread{
    //重写run方法
    @Override
    public void run() {
        System.out.println("线程执行");
    }
}
public class MyThread0 {
    public static void main(String[] args) {
        //第一种,继承Thread类重写run方法
        new MyThreadOne();
    }
}
第二种,直接实现Runnable接口

直接实现Runnable接口重写其中的run方法,简单暴力,

这种方法个人不太推荐,这样创建的线程会失去好多Thread类中对线程操作的方法

class MyThreadTwo implements Runnable{

    @Override
    public void run() {
        System.out.println("线程执行");
    }
}
public class MyThread0 {
    public static void main(String[] args) {
        //第二种,直接实现Runnable接口
        new MyThreadTwo();
    }
}
第三种 Runnable接口是函数式接口

这种方法是利用Thread类中的构造方法创建一个重写run方法后的线程,最推荐

public class MyThread0 {
    public static void main(String[] args) {
        //第三种 Runnable接口是函数式接口,这种方法是利用Thread类中的构造方法创建一个重写run方法后的线程,最推荐
        new Thread(()-> System.out.println("线程执行"));
    }
}

二、并发类线程创建

方法1,直接实现Callable接口

简单粗暴的实现Callable接口的线程创建

class MyThread1One implements Callable<String> {

    @Override
    public String call() throws Exception {
    //注意这里可以抛出个异常交给虚拟机处理,而Runnable不能抛异常
        System.out.println("线程执行");
        return null;//还具有返回值返回值类型为Callable的泛型
    }
}
public class MyThread1 {
    public static void main(String[] args) {
        //1.方法1,直接实现Callable<T>接口
        new MyThread1One();
    }
}
方法2,实现FutureTask(未来任务)接口

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

boolean cancel(boolean mayInterruptIfRunning):尝试取消执行此任务,如果任务已经完成,则返回false,如果任务还没有开始,则任务就不会开始了,如果任务正在执行,则会根据参数判断是否要取消此任务

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

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

boolean isDone():若Callable任务完成,返回True
//这里简单的说就是线程执行call方法执行完成得到返回值之后说明完成任务,返回true

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

class MyThread1Two extends FutureTask<String> {
    //下面两个方法必须重写一个,方法类似于Thread的构造方法只是参数由Runnable改成Callable
    public MyThread1Two(Callable<String> callable) {
        super(callable);
    }
    //第二个参数表示:设置成功完成后返回的结果,也可以设置null,这样就和上面这个方法一样了,只不过这里操作的是Runnable接口的线程,
    public MyThread1Two(Runnable runnable, String result) {
        super(runnable, result);
    }
}
public class MyThread1 {
    public static void main(String[] args) {
        //2.方法2,实现FutureTask<T>(未来任务)接口
        new MyThread1Two(()->"");      
   }
}
方法3,直接创建FutureTask类
public class MyThread1 {
    public static void main(String[] args) {
        //3.方法3,直接创建FutureTask<T>类,注意这是个类不是抽象类也不是接口,没有空参构造方法,只能传参
        new FutureTask<String>(()->"");
    }
}

三、线程池创建线程

这里列出四个线程池创建线程的方法
第一种:ExecutorServer newCachedThreadPool()

这个一般是最好用的也是最普遍常用的

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

		ExecutorService executorService = Executors.newCachedThreadPool();
		//没有参数需要后面调用execute方法执行线程
        Executors.newCachedThreadPool((runnable)->new Thread();//不够用会自动创建一个线程
第二种:ExecutorServer newFixedThreadPool(int nThreads)

一般用户服务器类型的线程池,活动的线程数固定,也会自己创建线程,但是多出的线程执行完成之后就会被移出线程池,并且没有IDLE,不会自己终止

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

		Executors.newFixedThreadPool(3);//没有参数需要后面调用execute方法执行线程
        Executors.newFixedThreadPool(3,(s)->thread);//指定线程工厂
第三种:ScheduledExecutorService newSingleThreadExecutor(int corePoolSize)

*调度型线程池
*这个池子里的线程可以按schedule(时间表)依次delay(延迟)执行,或周期执行

*ScheduledExecutorService newScheduledThreadPool(int corePoolSize, ThreadFactory threadFactory) 也可以指定工厂

		Executors.newScheduledThreadPool(3);//参数为保留在线程池中的线程,即使死亡也存在
        Executors.newScheduledThreadPool(3,(s)->thread);
第四种:ExecutorService newSingleThreadExecutor()

​ 单例线程,“任意时间”池中只能有一个线程
​ 用的是和cache池和fixed池相同的底层池,但线程数目是1,0秒IDLE(无IDLE)
​ 也可以传线程工厂

		Executors.newSingleThreadExecutor();
        Executors.newSingleThreadExecutor((s)->thread);

四、线程池执行Runnable接口和Callable接口的方式

这里执行的线程对象并不是指定工厂创建的线程,而是ExecutorService接口中的方法,或者Executor接口中的方法,因为ExecutorService接口继承Executor接口,所以一并用ExecutorService操作

执行Runnable线程

这个execute方法是executeServer的父类Execute中的方法而ExecutorService中也有执行Runnable的方法

 		executorService.execute(()-> System.out.println(""));
        executorService.submit(()-> System.out.println(""));
        Future<String> submit1 = executorService.submit(() -> System.out.println(""),"");
		//甚至可以给Runnable执行之后强行附加一个返回值,这个返回值只是自己定义的一个对象而已,只有调用get方法才能获取到
		try {
            String s = submit1.get();//这个返回值类型是自己定义的Future<T>的泛型
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }

甚至可以给Runnable执行之后强行附加一个返回值,这个返回值只是自己定义的一个对象而已,只有调用get方法才能获取到

执行Callable线程
		Future<String> submit = executorService.submit(() -> "");
		//这个submit没有指定返回值的参数,因为Callable自己带有返回值

四种创建线程的方法对比

实现Runnable和实现Callable接口的方式基本相同,不过是后者执行call()方法有返回值,后者线程执行体run()方法无返回值,因此可以把这两种方式归为一种这种方式与继承Thread类的方法之间的差别如下:
1、线程只是实现Runnable或实现Callable接口,还可以继承其他类。
2、这种方式下,多个线程可以共享一个target对象,非常适合多线程处理同一份资源的情形。
3、但是编程稍微复杂,如果需要访问当前线程,必须调用Thread.currentThread()方法。
4、继承Thread类的线程类不能再继承其他父类(Java单继承决定)。
5、前三种的线程如果创建关闭频繁会消耗系统资源影响性能,而使用线程池可以不用线程的时候放回线程池,用的时候再从线程池取,项目开发中主要使用线程池

部分借鉴:https://blog.csdn.net/m0_37840000/article/details/79756932

十分感谢

  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

善良的牙膏

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值