Java中开启多线程的三种方法

Java开启多线程有三种方式:

  • 继承Thread类,重写run方法
  • 实现Runnable接口,重写run方法
  • 实现Callable接口,重写call方法(juc并发包中,用的不多)

 

方式一:直接继承Thread类,重写run()方法

  • 执行线程必须调用start()方法,从而加入到调度器中
  • 不一定立即执行,系统安排调度分配执行(等待分配时间片)
  • 直接调用run()方法不是开启线程,是普通方法调用
  • 使用继承Thread类的方法来创建线程类时,多个线程之间无法共享线程类的实例变量(成员变量)
/**
 * 创建多线程方法一:
 * 1、创建:继承Thread + 重写run()方法
 * 2、启动:创建子类对象 + start()
 */
public class StartThread extends Thread{
    //多线程入口
    @Override
    public void run() {
        for(int i = 0; i < 50; i++){
            System.out.println("一边听音乐");
        }
    }

    public static void main(String[] args) {
        //创建子对象
        StartThread st = new StartThread();
        //启动
        st.start();  //开启多线程,不保证立即调用,调度交由CPU
        //st.run();  //调用普通方法,正常的逻辑顺序流
        for(int i = 0; i < 50; i++){
            System.out.println("一边coding");
        }
    }
}

打开start()原码,会发现start()会调用本地方法start0(),再本地方法start0()中会调用run()方法。

 

方式二:实现Runnable接口,重写run()方法

  • java中存在单继承的局限性,因此建议多采用实现,少用继承,也就是多用Runable接口
  • 方便共享资源,一份资源多个代理
  • Runnable对象仅仅作为Thread对象的target,Runnable实现类里包含的run()方法仅作为线程执行体。而实际的线程对象依然是Thread实例,只是该Thread线程负责执行其target的run()方法。
/**
 * 创建多线程方法二:
 * 1、创建:实现Runnable中的run()方法
 * 2、启动:创建实现类对象 + 代理类Thread对象 + start()
 */
public class StartRun implements Runnable{
    //多线程入口
    @Override
    public void run() {
        for(int i = 0; i < 50; i++){
            System.out.println("一边听音乐");
        }
    }

    public static void main(String[] args) {
        //创建实现类对象
        StartRun sr = new StartRun();
        //创建代理类对象
        Thread t = new Thread(sr);
        //启动
        t.start();  //开启多线程,不保证立即调用,调度交由CPU
        for(int i = 0; i < 50; i++){
            System.out.println("一边coding");
        }
    }
}

 由于sr和t只用了一次,所以建议采用匿名类

public class StartRun implements Runnable{
    //多线程入口
    @Override
    public void run() {
        for(int i = 0; i < 50; i++){
            System.out.println("一边听音乐");
        }
    }

    public static void main(String[] args) {
        new Thread(new StartRun()).start();
        for(int i = 0; i < 50; i++){
            System.out.println("一边coding");
        }
    }
}

针对方法二中,一份资源,多份代理,实现资源共享,这里以一个简单地实例说明

  • ctrl + alt + t 快速加上try-catch代码
/**
 * 资源共享,并发编程(需要保证线程安全,后续讲解)
 * ctrl + alt + p 快速加上try-catch代码
 */
public class Web12306 implements Runnable {

    private int ticketNumbs = 10;
    @Override
    public void run() {

        while(true) {
            if (ticketNumbs <= 0) {
                break;
            }
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "--->" + ticketNumbs--);
        }
    }

    public static void main(String[] args) {
        System.out.println(Thread.currentThread().getName());
        Web12306 web = new Web12306();
        new Thread(web,"码畜").start();
        new Thread(web,"码农").start();
        new Thread(web,"码神").start();
    }

}

 

方式三:使用Callable和Future创建线程

  • juc并发包下实现Callable接口,重写call()方法
  • 据说工作几年之后才会接触,可以了解下用于面试
import java.util.concurrent.*;

/**
 * 创建线程的方式三
 * 创建:实现Callable中的call()方法
 * 启动:创建执行服务  ExecutorService service = Executors.newFixedThreadPool(1);
 *      提交结果     Future<T> result = service.submit(tc);
 *      获取结果     T r = result.get();
 *      关闭服务     service.shutdownNow();
 */
public class ThreadCall implements Callable<Boolean> {
    @Override
    public Boolean call() throws Exception {
        for(int i = 0; i < 20; i++){
            System.out.println(Thread.currentThread().getName() + "-->" + "听音乐");
        }
        return true;
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ThreadCall tc = new ThreadCall();
        ThreadCall tc2 = new ThreadCall();
        //创建执行服务
        ExecutorService service = Executors.newFixedThreadPool(2);
        //提交结果
        Future<Boolean> result = service.submit(tc);
        Future<Boolean> result2 = service.submit(tc2);
        //获取结果
        boolean r = result.get();  //boolean根据泛型指定的类型变
        boolean r2 = result2.get();
        //关闭服务
        service.shutdownNow();
    }

}

 

补充:阅读疯狂java讲义,总结一下方式三的一些知识点

前面方式一和方式二都是将run()方法作为线程执行体,那么可否将任一方法包装成线程执行体,java当前不行(C#可以),但是为了更为方便的进行多线程编程,从Java 5开始提供Callable接口,可以说是Runnable接口的增强版。

主要特点:

  • call()方法可以有返回值
  • call()方法可以声明抛出异常

疯狂java讲义中使用Callable接口的方式与上述方式不太一样,此书中的方式基本与方式二一致,也是使用Thread代理,只不过在Runnable中直接代理Runnable对象,这里需要使用FutureTask类包装一下,但实质一样。与上述写法明显不同,我想可能是存在不用的使用方式,有时间再去探究juc编程。

 继承Thread类方式实现Runnable or Callable方式
优点编写简单,如需访问当前线程,只需使用this即可

1、还可以继承其他类

2、在这种方式下,多个线程可以共享同一个target对象,也就是一份资源,多个代理,从而可以将CPU、代码和数据分开

缺点已经继承了Thread类了,无法继承其他类编程相对复杂些,访问当前线程必须使用Thread.currentThread()
总结推荐采用实现Runnable or Callable方式来创建多线程

最后附上书中方式三写法:

package com.ly.thread;

import java.util.concurrent.FutureTask;

/**
 * liyang 2020-08-11
 *
 */
public class ThirdThread {

    public static void main(String[] args) {
        //创建Callable对象
        //ThirdThread rt = new ThirdThread();
        //先使用Lambda表达式创建Callable<>对象
        //再使用FutureTask来包装Callable对象
        FutureTask<Integer> task = new FutureTask<>(() -> {
            int i = 0;
            for(; i < 100; i++) {
                System.out.println(Thread.currentThread().getName() + "的循环变量i的值: " + i);
            }
            return i; //call()方法可以有返回值
        });

        for(int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName() + "的循环变量i的值: " + i);
            if(i == 20) {
                //实例还是以Callable的封装对象来创建并启动线程的
                new Thread(task, "有返回值的线程").start();
            }
        }

        try {
            System.out.println("子线程的返回值:" + task.get()); //获取线程返回值
        }
        catch (Exception e) {
            e.printStackTrace();
        }
    }

}

运行结果:交替执行主线程和子线程,以下结果仅仅作为显示,因为各打印100行,太占篇幅

main的循环变量i的值: 0
main的循环变量i的值: 1
main的循环变量i的值: 2
...

有返回值的线程的循环变量i的值: 0
有返回值的线程的循环变量i的值: 1
...
有返回值的线程的循环变量i的值: 99
main的循环变量i的值: 99
子线程的返回值:100

Process finished with exit code 0

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值