栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

创建线程的四种方式详解

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

创建线程的四种方式详解

使用Thread创建线程

先看看Thread是怎么创建一个线程并运行的,代码如下。

public class MyThread  {
    public static int number ;
    public static int count = 100;
    public static void main(String[] args){
        Thread1 thread1 = new Thread1();
        thread1.setName("线程1");
        thread1.start();    
    }
}
class Thread1 extends Thread{
    @Override
    public void run(){
        while (true){
            if(MyThread.count < 0 ){
                break;
            }else {
                MyThread.count--;
            }
            MyThread.number = 100-MyThread.count;
            System.out.println(Thread.currentThread().getName()+"得到了"+"第"+MyThread.number+"张车票");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

以上可知通过继承Thread创建一个线程实际上是调用的Thread类中的start()方法。而start()方法实际上又是调用的由native修饰的本地方法start0()去创建一个线程(java方法与本地方法以及java如何调用本地方法略)。此时该线程便处于运行状态,等待cpu的调度执行重写的run方法中的代码。如果主线程想要等到创建的线程执行完成之后在运行,可以使用Thread.join()方法。

使用Runnable创建线程

使用Runnable创建代码如下

public class MyRunnable {
    public static int number ;
    public static int count = 100;
    public  static void main(String[] args){
        Runnable1 runnable1 = new Runnable1();
        Thread thread1 = new Thread(runnable1);
        thread1.start();
    }
}
class Runnable1 implements Runnable{
    @Override
    public void run() {
        while(true){
            if(MyThread.count < 0){
                break;
            }else {
                MyThread.count--;
            }
            MyThread.number = 100-MyThread.count;
            System.out.println("线程"+Thread.currentThread().getName()+"得到了"+"第"+MyThread.number+"张车票");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

通过以上代码可知通过实现Runnable接口创建线程,实际上还是将Runnable接口实现类的实例对象传入Thread类对象中,由Thread类中的start方法创建线程,再等待cpu调度执行该对象的run方法。其实Thread实现的run方法也是Runnable接口中定义的,因为Thread也实现了Runnable接口。那么就是说Runnable接口中run方法是线程执行内容的载体,而Thread类中start方法则是用来创建线程的。那么既然原理都是一样的为什么还要提供两种实现方式呢?主要还是因为Thread是一个类,如果某个类想要创建线程继承了Thread类了之后就不能再继承其他父类了,所以Thread
类初始化的时候提供了传入Runnable接口实现类的实例对象的方法。

使用Callable创建线程

前面提到的通过Thread和Runnable创建线程都是异步执行的,并不能拿到线程的执行结果。而使用Callable创建线程则可以拿到线程的执行结果。通过Callable创建线程代码如下。

public class MyCallable {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        Callable1 callable1 = new Callable1();
        FutureTask futureTask = new FutureTask<>(callable1);
        Thread aa = new Thread(futureTask);
        aa.start();
        System.out.println(futureTask.get().toString());
    }
}
class Callable1 implements Callable{
    @Override
    public Object call() throws Exception {
        Date date = new Date();
        return date;
    }
}

其中FutureTask类实现了RunnableFuture接口,而RunnableFuture接口又继承了Runnable接口以及Future接口。所以FutureTask实例也可以用来初始化Thread类对象,并通过Thread类中start方法启动线程。线程启动之后会调用FutureTask中实现Runnable接口中的run()方法,run()方法再去调用Callable类对象中的call()方法并得到返回结果result,再将result封装进成员变量中,之后便可以通过get()方法得到返回值。FutureTask实现了Future接口中的方法如下。
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。
其实FutureTask类中除了直接传Callable类对象来初始化之外,还可以通过传Runnable接口实现类类对象 + 返回类型 来初始化。在之后的内容中会讲到。

使用线程池创建线程

前面已经提到线程执行的两个主要接口:Runnable和Callable,以及创建线程的类Thread和得到线程执行结果的类FutureTask。其中Thread类以及Runnable接口是JDK中一开始就提供了的。而FutureTask,Future,Callable则是在JDK1.5开始提供的Executor框架的包concurrent下的。Executor框架是一个用于统一创建线程与运行线程的接口。Executor框架实现的就是线程池的功能。先看下如何用线程池创建线程。

public class MyThreadPool {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ExecutorService executorService1= new ThreadPoolExecutor(1,1,60L,TimeUnit.SECONDS, new ArrayBlockingQueue(10));
        executorService1.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName());
            }
        });
        executorService1.shutdown();
        ExecutorService executorService2 = new ThreadPoolExecutor(1,1,60L,TimeUnit.SECONDS, new ArrayBlockingQueue(10));
        Future future = executorService2.submit(new Callable() {
            @Override
            public Object call()  {
                return new Date();
            }
        });
        System.out.println(future.get());
        executorService2.shutdown();
    }
}
 

关于使用ThreadPoolExecutor创建线程池源码没太看懂,之后再补上。大概处理逻辑是如果核心线程没满则创建一个核心线程。如果已经满了则存入队列中等待核心线程空闲再去运行。如果队列也满了则创建一个非核心线程运行,如果非核心线程也满了则执行饱和策略(共四种,也可以选择自己实现饱和策略)。而submit与execute的区别则在于submit可以返回一个future对象用于获取线程执行的结果。而对于Executors工具类提供的创建常用线程池的方法一般不推荐使用,可以了解。

线程的状态转换以及销毁策略

略,等到锁写完了再进行补充。

转载请注明:文章转载自 www.mshxw.com
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号