【并发编程】创建线程的四种方式

本文详细介绍了在Java中创建线程的四种常见方法:继承Thread类、实现Runnable接口、使用Callable与Future以及利用线程池。每种方法都有其适用场景和优缺点,如资源共享、返回值支持等。

上一篇我们初步认识了线程,现在我们来讲一下,创建线程的三种方式

1.继承Thread

类通过继承thread类,然后重写run方法(run方法中是线程真正执行的代码,runable也是如此)即可。当子类被实例化后,调用start方法即可启动线程。(不能调用run方法,直接调用run方法,和方法调用没有区别,不是线程。)

这种继承thread创建线程的方式,线程之间无法共享实例变量,因为每次都创建了新的实例,肯定不能共享了。

/**
 * 通过集成Thread类,重写run方法启动线程
 */
public class FirstThread extends Thread {
    private int i;

    public void run() {
        for (; i < 100; i++) {
            //当线程类继承thread类时,直接使用this即可获得当前线程
            //Thread对象的getName(),返回当前线程的名字
            //因此可以直接调用getName()方法返回当前线程的名字
            System.out.println(Thread.currentThread().getName() + " " + i);
        }
    }

    public static void main(String[] args) {
        for (int i = 0; i < 100; i++) {
            //调用thread的currentThread()方法获取当前线程
            System.out.println(Thread.currentThread().getName() + " " + i);
            if(i==20){
                //创建并启动第一个线程
                new FirstThread().start();
                //创建并启动第二个线程
                new FirstThread().start();
                
                //类似Runable的启动方法,通过继承thread也可以。
                FirstThread firstThread1 = new FirstThread();
                new Thread(firstThread1,"thread线程").start();
            }
        }
    }
}

执行效果大致如下:

有四个线程启动,一个主线程,线程名字为main 0。还有三个其他线程,线程名分别为:Thread-0、Thread-1、thread线程。这四个线程都打印了1到100。运行结果的一部分展示如下:

2.实现Runable接口

顾名思义,我们需要创建一个类,由此类实现runable接口,然后重写run方法。在启动线程时,将类实例化,然后创建thread,然后将实例化得对象作为创建thread的的参数即可。这种方式创建的线程类,是互相共享资源的。

/**
 * 通过实现runable接口来创建线程类
 */
public class SecondThread implements Runnable {
    private int i;

    //run方法同样是线程执行体
    public void run() {
        for (; i < 100; i++) {
            //当线程类实现runable接口时
            //如果想获取当前线程,只能用Thread.currentThread()方法
            System.out.println(Thread.currentThread().getName() + " " + i);
        }
    }

    public static void main(String[] args) {
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName() + " " + i);
            if (i==20){
                SecondThread st = new SecondThread(); //
                //通过new Thread(Target,name)方法创建新线程
                new Thread(st,"新线程1").start();
                new Thread(st,"新线程2").start();
            }
        }
    }
}

通过runable来创建线程是共享实例变量的,故执行结果是i是被共享的,不是新线程1和新线程2 各自执行打印1到100,而是拿到上一线程修改后的变量,继续打印。执行部分效果如下:

其实类似的还有一种创建方式,是通过lambda表达式进行创建的了,原理是和实现runable接口是一样的。 

public class T02_CreateThreadByLambda {

    public static void main(String[] args) {
        for (int i = 0; i < 100; i++) {
            if (i == 20) {
                for (int j = 0; j < 100; j++) {
                    new Thread(() -> {
                        System.out.println("Hello Lambda!");
                    }).start();
                }
            }
        }
    }
}

执行的部分结果如下:由图可知,我们这种创建方式也是可以,其实lambda表达式,就相当于创建thread的参数,然后启动线程。

 

3.使用Callable和Future创建线程

从继承Thread类和实现Runnable接口可以看出,上述两种方法都不能有返回值,且不能声明抛出异常。而Callable接口则实现了此两点,Callable接口如同Runable接口的升级版,其提供的call()方法将作为线程的执行体,同时允许有返回值。

但是Callable对象不能直接作为Thread对象的参数,因为Callable接口是 Java 5 新增的接口,不是Runnable接口的子接口。对于这个问题的解决方案,就引入 Future接口,此接口可以接受call() 的返回值,RunnableFuture接口是Future接口和Runnable接口的子接口,可以作为Thread对象的target 。并且, Future 接口提供了一个实现类:FutureTask 。

FutureTask实现了RunnableFuture接口,可以作为 Thread对象的target。


/**
 * 使用callable和future创建线程
 */
public class ThirdThread {
    public static void main(String[] args) {
        //创建Callable对象
        ThirdThread rt = new ThirdThread();

        //先使用Lambda表达式创建Callable<Interger>对象
        //使用furureTask来 包装Callable对象
        FutureTask<Integer> task = new FutureTask<>((Callable<Integer>) () -> {
            int i = 0;
            for (; i < 100; i++) {
                System.out.println(Thread.currentThread().getName() + " " + i);
            }
            //call方法可以有返回值
            return i;
        });
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName() + " " + i);
            if (i == 20) {
                //实质还是以callable对象来创建并启动线程的
                new Thread(task, "有返回值的线程").start();
            }
        }
        try {
            //获取线程返回值
            System.out.println("子进程的返回值:" + task.get());
        }
        catch (Exception ex){
            ex.printStackTrace();
        }
    }
}

执行结果我们我们可以猜测一下,应该是有名称叫“main”线程打印0到99,名称叫“有返回值的线程”打印0到99,最后是子进程,由于最后还进行了一次++,所以返回值为100,所以就打印了一次:“子进程的返回值:100”

执行的部分结果如下: 

中间都是打印“有返回值的线程:i”  ,我省略掉了。

4.使用线程池创建

1)使用Executors类中的newFixedThreadPool(int num)方法创建一个线程数量为num的线程池

2)调用线程池中的execute()方法执行由实现Runnable接口创建的线程;调用submit()方法执行由实现Callable接口创建的线程

3)调用线程池中的shutdown()方法关闭线程池

public class Thread4 {
     
    public static void main(String[] args) throws Exception {
         
        Thread.currentThread().setName("主线程");
        System.out.println(Thread.currentThread().getName()+":"+"输出的结果");
        //通过线程池工厂创建线程数量为2的线程池
        ExecutorService service = Executors.newFixedThreadPool(2);
        //执行线程,execute()适用于实现Runnable接口创建的线程
        service.execute(new ThreadDemo4());
        service.execute(new ThreadDemo6());
        service.execute(new ThreadDemo7());
        //submit()适用于实现Callable接口创建的线程
        Future<String> task = service.submit(new ThreadDemo5());
        //获取call()方法的返回值
        String result = task.get();
        System.out.println(result);
        //关闭线程池
        service.shutdown();
    }
}
//实现Runnable接口
class ThreadDemo4 implements Runnable{
     
    @Override
    public void run() {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+":"+"输出的结果");
    }
     
}
//实现Callable接口
class ThreadDemo5 implements Callable<String>{
 
    @Override
    public String call() throws Exception {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+":"+"输出的结果");
        return Thread.currentThread().getName()+":"+"返回的结果";
    }
 
}
//实现Runnable接口
class ThreadDemo6 implements Runnable{
     
    @Override
    public void run() {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+":"+"输出的结果");
    }
     
}
//实现Runnable接口
class ThreadDemo7 implements Runnable{
     
    @Override
    public void run() {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+":"+"输出的结果");
    }
     
}

输出结果如下: 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值