三、线程的构造方法与三种方式

目录

线程构造方法

线程的三种方式 

一、直接 继承Thread法

run与start方法的区别

二、实现Runnable法

 采用匿名内部类创建

采用lambda表达(常用)

三、FutureTask方式,实现Callable接口

Callable和Future

Executor


线程构造方法

构造方法名名称
Thread()
Thread(String name)name为线程名字
Thread(Runnable target)
Thread(Runnable target, String name)name为线程名字

线程的三种方式 

一、直接 继承Thread

编写一个类,直接 继承 java.lang.Thread 线程类重写 run方法

  • 随便写一个类
  • 让这个类继承线程的方法
  • 重写继承线程类的run方法
  • 创建该类的线程对象(new一个继承线程的类)
  • 调继承父类(线程类)的start (现在这个线程已经在就绪状态)
  • 启动线程(当我调用线程对象的start()方法代表已经启动线程)
package 多线程2;

public class ThreadTest01 {
    public static void main(String[] args) {
        // 定义MyThread继承线程类
        class MyThread extends Thread{
            public void run(){
            }
        }
        // 创建线程对象
        MyThread t = new MyThread();
        // 启动线程。
        t.start();
    }
}

run与start方法的区别

 线程的run方法,不会启动线程,不会分配新的分支栈。(这种方式就是单线程)

还是走之前的主线程(main线程),执行完直接在上方弹栈

线程的start方法,是启动线程的方法,当调用该方法就是启用线程,这个线程就从新建状态到达就绪状态。(这种就是多线程)

作用是:

  1. 启动一个分支线程,在JVM中开辟一个新的栈空间,这段代码任务完成之后,瞬间就结束了。
  2. 这段代码的任务只是为了开启一个新的栈空间,只要新的栈空间开出来,start()方法就结束了。线程就启动成功了。
  3. 启动成功的线程会自动调用run方法,并且run方法在分支栈的栈底部(压栈)。
  4. run方法在分支栈的栈底部,main方法在主栈的栈底部。run和main是平级的。

开辟一个分支栈,栈与栈之间相互独立、互不干扰

package 多线程2;

public class ThreadTest02 {
    public static void main(String[] args) {
        MyThread t = new MyThread();

        //t.run(); 不会启动线程,不会分配新的分支栈。(这种方式就是单线程。)

        // 启动线程
        t.start();
        // 这里的代码还是运行在主线程中(主线程)
        for(int i = 0; i < 1000; i++){
            System.out.println("主线程--->" + i);
        }
    }
}

class MyThread extends Thread {
    @Override
    public void run() {
        // 编写程序,这段程序运行在分支线程中(分支线程)。
        for(int i = 0; i < 1000; i++){
            System.out.println("分支线程--->" + i);
        }
    }
}

执行上方代码打印对应线程记录,可以看到有主线程与分支线程并发执行。

二、实现Runnable

编写一个类,实现 java.lang.Runnable 接口,实现run方法

  • 随便写一个类MyRunnable 
  • 让这个类实现Runnable 接口
  • 重写run方法(注意,该类还不是一个线程类,只是一个可以运行的类还不是一个线程)
  • new一个线程对象把这个类传进去(new线程类传入可运行的类/接口,这个才是线程)
  • 调用线程对象的 start() 方法。(这个就是启动线程)
package 多线程;

public class ThreadTest03 {
    public static void main(String[] args) {
        //线程
        Thread t = new Thread(new MyRunnable());
        // 启动线程
        t.start();


    }
}

// 这并不是一个线程类,是一个可运行的类。它还不是一个线程。
class MyRunnable implements Runnable {
    @Override
    public void run() {

    }
}
package 多线程;

public class ThreadTest03 {
    public static void main(String[] args) {
        //线程
        Thread t = new Thread(new MyRunnable());
        // 启动线程
        t.start();

        for(int i = 0; i < 100; i++){
            System.out.println("主线程--->" + i);
        }
    }
}

// 这并不是一个线程类,是一个可运行的类。它还不是一个线程。
class MyRunnable implements Runnable {
    @Override
    public void run() {
        for(int i = 0; i < 100; i++){
            System.out.println("分支线程--->" + i);
        }
    }
}

 执行上方代码打印对应线程记录,可以看到有主线程与分支线程并发执行。

 采用匿名内部类创建

package 多线程;

//匿名内部类
public class ThreadTest04 {
    public static void main(String[] args) {
        // 创建线程对象,采用匿名内部类方式。
        Thread t = new Thread(new Runnable(){
            @Override
            public void run() {
                for(int i = 0; i < 100; i++){
                    System.out.println("t线程---> " + i);
                }
            }
        });
        // 启动线程
        t.start();

        for(int i = 0; i < 100; i++){
            System.out.println("main线程---> " + i);
        }
    }
}

采用lambda表达(常用)

package 多线程;


public class ThreadTest04 {
    public static void main(String[] args) {

        //
        Thread t = new Thread(() -> {
            for(int i = 0; i < 100; i++){
                System.out.println("t线程---> " + i);
            }
        });
        // 启动线程
        t.start();

        for(int i = 0; i < 100; i++){
            System.out.println("main线程---> " + i);
        }
    }
}

注意:

第二种方式实现接口比较常用,因为一个类实现了接口,它还可以去继承其它的类,更灵活。

三、FutureTask方式,实现Callable接口

这种方式实现的线程可以获取线程的返回值。
上述讲解的那两种方式是无法获取线程返回值的,因为run方法返回void。

​ 思考:​ 系统委派一个线程去执行一个任务,该线程执行完任务之后,可能会有一个执行结果,我们怎么能拿到这个执行结果呢?

使用第三种方式:实现Callable接口方式。

Callable和Future

它俩很有意思的,一个产生结果,一个拿到结果

  • Callable接口类似于Runnable,从名字就可以看出来了
  • 但是Runnable不会返回结果,并且无法抛出返回结果的异常
  • 而Callable功能更强大一些,被线程执行后,可以返回值
  • 这个返回值可以被Future拿到也就是说,Future可以拿到异步执行任务的返回值

下面来看一个简单的例子:

package 多线程;

import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

public class CallableAndFuture {
    public static void main(String[] args) {
        
        Callable<Integer> callable = new Callable<Integer>() {
            public Integer call() throws Exception {
                return new Random().nextInt(100);
            }
        };
        FutureTask<Integer> future = new FutureTask<Integer>(callable);
        new Thread(future).start();
        try {
            Thread.sleep(5000);
            System.out.println(future.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}

FutureTask实现了两个接口,Runnable和Future

所以它既可以作为Runnable被线程执行,又可以作为Future得到Callable的返回值

那么这个组合的使用有什么好处呢?

假设有一个很耗时的返回值需要计算,并且这个返回值不是立刻需要的话,那么就可以使用这个组合,用另一个线程去计算返回值,而当前线程在使用这个返回值之前可以做其它的操作,等到需要这个返回值时,再通过Future得到。

上述的例子可以看到

  • Callable<Integer> callable = new Callable<Integer>()相当于实现Runnable的可运行的类
  • FutureTask<Integer> future = new FutureTask<Integer>(callable)把这个类传进去,这个future就是线程对象
  • new Thread(future).start() new一个线程对象并开启线程
  •  System.out.println(future.get()) 获取线程的返回值

Executor

ExecutorService继承自Executor,它的目的是为我们管理Thread对象,从而简化并发线程

Executor使我们无需显示的去管理线程的生命周期,是JDK 5之后启动任务的首选方式。执行多个带返回值的任务,并取得多个返回值。

代码如下:

package 多线程;

import java.util.concurrent.*;

public class CallableAndFuture03 {
    public static void main(String[] args) {
        ExecutorService threadPool = Executors.newCachedThreadPool();
        CompletionService<Integer> cs = new ExecutorCompletionService<Integer>(threadPool);
        for(int i = 1; i < 5; i++) {
            final int taskID = i;
            cs.submit(new Callable<Integer>() {
                public Integer call() {
                    return taskID;
                }
            });
        }
        for(int i = 1; i < 5; i++) {
            try {
                System.out.println(cs.take().get());
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
        }
    }
} 

打印获取线程的返回值。 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值