首页 > 其他分享 >实现线程的三种方式

实现线程的三种方式

时间:2023-12-28 20:13:12浏览次数:24  
标签:run 方式 Thread method1 三种 线程 执行 public

一、继承Thread类

Thread 类中创建线程最重要的两个方法为:

public void start();

public void run();

采用 Thread 类创建线程,用户只需要继承 Thread,覆盖 Thread 中的 run 方法,父类 Thread 中的 run 方法没有抛出异常,那么子类也不能抛出异常,最后采用 start 启动线程即可。

【示例代码1】不使用线程

public class ThreadTest01 {
	public static void main(String[] args) {
		Processor p = new Processor();
		p.run();
		method1();
	}
	private static void method1() {
		System.out.println("--------method1()----------");
	}
}
class Processor {
	public void run() {
		for (int i=0; i<10; i++) {
			System.out.println(i);
		}
	}
}

【执行结果】

0
1
2
3
4
5
6
7
8
9
--------method1()----------

以上顺序输出相应的结果(属于串行) , 也就是 run 方法完全执行完成后,才执行 method1 方法, 也就是 method1 必须等待前面的方法返回才可以得到执行,这是一种“同步编程模型”。

这样执行存在什么样的弊端呢?

按照顺序执行,这就极大的降低了程序的执行效率。无法同时执行多个代码片段,这也是多线程并发所要达到的目的。

【示例代码2】使用线程

public class ThreadTest02 {
	public static void main(String[] args) {
		Processor p = new Processor();
		/*
			如果是手动调用该方法,
			则并不能采用 run 来启动一个场景(线程),
			run 就是一个普通方法调用。
		*/
		//p.run();
		
		/*
			采用 start 启动线程,不是直接调用 run,
			start 不是马上执行线程,而是使线程进入就绪状态
			线程的真正执行是由 Java 的线程调度机制完成的。
		*/
		p.start();
		
		//线程只能启动一次,无法启动多次
		//p.start();
		
		method1();
	}
	
	private static void method1() {
		System.out.println("--------method1()----------");
	}
}
class Processor extends Thread {
	/*
		覆盖 Thread 中的 run 方法,该方法没有异常
		该方法是由 java 线程调度机制调用的,因此
		我们不应该手动调用该方法
	*/
	public void run() {
		for (int i=0; i<10; i++) {
			System.out.println(i);
		}
	}
}

 

【执行结果】

--------method1()----------
0
1
2
3
4
5
6
7
8
9

通过输出结果大家会看到,没有按照顺序执行,而在输出数字的同时执行了 method1()方法,如果从效率上看,采用多线程的示例要快些,因为我们可以看作他是同时执行的, mthod1()方法没有等待前面的操作完成才执行, 这叫“异步编程模型”。

那么,为什么会是这样的执行结果呢?

这就涉及到Java线程的调度机制了,该程序包含两个线程一个是主线程也就是main线程,另外一个是用户创建的p线程,当类加载完成后,主线程启动,开始执行main方法栈帧,按照代码自上而下的执行顺序,先创建Processor的实例化对象p,接着是执行p.start();启动p线程,这时method1();方法还没有执行,此时两个线程均已经启动,按照Java线程调度的规则,两个线程开始抢夺执行程序的时间片(即CPU的执行权),注意,这种抢夺是随机的,也就是说,不一定输出结果就是method1方法先执行,for循环语句后执行。可以多执行几次即可看到不一样的执行结果。

二、实现 Runnable 接口

其实 Thread 对象本身就实现了 Runnable 接口,但一般建议直接使用 Runnable 接口来写多线程程序,因为接口会比类带来更多的好处(面向接口编程的原则)。

【示例代码3】

public class ThreadTest03 {
	public static void main(String[] args) {
		//Processor r1 = new Processor();
		/*
			使用多态机制父类型引用指向子类型对象,
			因为这样可以调用Runnable接口的方法
		*/
		Runnable r1 = new Processor();
		
		//不能直接调用 run方法,原因见上文
		//p.run();
		
		//创建线程对象,并将r1对象作为参数传入(Thread的构造方法)
		Thread t1 = new Thread(r1);
		
		//启动线程
		t1.start();
		
		method1();
	}
	
	private static void method1() {
		System.out.println("--------method1()----------");
	}
}
//实现 Runnable 接口
class Processor implements Runnable {
	//实现 Runnable 中的 run 方法
	public void run() {
		for (int i=0; i<10; i++) {
			System.out.println(i);
		}
	}
}

【执行结果】

-------method1()----------
0
1
2
3
4
5
6
7
8
9

结果分析见上,原因是一样的,只不过是换了一种方式实现线程而已。

三、实现Callable接口

观察上文两种线程的执行方式,存在什么缺点。显然,以上两种线程的执行un方法时是没有返回值的,而实际上也会存在需要得到线程执行的返回结果的情况,那么怎么办呢?这时就可以考虑使用第三种线程的实现方式。

优点:可以获取到线程的执行结果。

缺点:效率比较低,在获取t线程执行结果的时候,当前线程受阻塞,效率较低。

【示例代码4-1】使用匿名内部类

import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
public class ThreadTest04 {
    public static void main(String[] args) throws Exception {
        // 第一步:创建一个“未来任务类”对象。
        // 参数非常重要,需要给一个Callable接口实现类对象。
        FutureTask task = new FutureTask(new Callable() {
            @Override
            public Object call() throws Exception { // call()方法就相当于run方法。只不过这个有返回值
                // 线程执行一个任务,执行之后可能会有一个执行结果
                // 模拟执行
                System.out.println("call method begin");
                Thread.sleep(1000 * 10);//当前线程睡眠10秒
                System.out.println("call method end!");
                int a = 100;
                int b = 200;
                return a + b; //自动装箱(300结果变成Integer)
            }
        });

        // 创建线程对象
        Thread t = new Thread(task);

        // 启动线程
        t.start();

        // 这里是main方法,这是在主线程中。
        // 在主线程中,怎么获取t线程的返回结果?
        // get()方法的执行会导致“当前线程阻塞”
        Object obj = task.get();
        System.out.println("线程执行结果:" + obj);

        // main方法这里的程序要想执行必须等待get()方法的结束
        // 而get()方法可能需要很久。因为get()方法是为了拿另一个线程的执行结果
        // 另一个线程执行是需要时间的。
        System.out.println("hello world!");
    }
}

【执行结果】

call method begin
call method end!
线程执行结果:300
hello world!

【示例代码4-2】不使用匿名内部类

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

public class ThreadTest01 {
    public static void main(String[] args) throws Exception {
        //创建Callable接口的实现类的实例化对象
        CallableImpl callable = new CallableImpl();

        // 第一步:创建一个“未来任务类”对象。
        // 参数非常重要,需要给一个Callable接口实现类对象。
        FutureTask task = new FutureTask(callable);

        // 创建线程对象
        Thread t = new Thread(task);

        // 启动线程
        t.start();

        // 这里是main方法,这是在主线程中。
        // 在主线程中,怎么获取t线程的返回结果?
        // get()方法的执行会导致“当前线程阻塞”
        Object obj = task.get();
        System.out.println("线程执行结果:" + obj);

        // main方法这里的程序要想执行必须等待get()方法的结束
        // 而get()方法可能需要很久。因为get()方法是为了拿另一个线程的执行结果
        // 另一个线程执行是需要时间的。
        System.out.println("hello world!");
    }
}
//实现Callable接口
class CallableImpl implements Callable{
    @Override
    public Object call() throws Exception { // call()方法就相当于run方法。只不过这个有返回值
        // 线程执行一个任务,执行之后可能会有一个执行结果
        // 模拟执行
        System.out.println("call method begin");
        Thread.sleep(1000 * 10);//当前线程睡眠10秒
        System.out.println("call method end!");
        int a = 100;
        int b = 200;
        return a + b; //自动装箱(300结果变成Integer)
    }
}

【执行结果】

call method begin
call method end!
线程执行结果:300
hello world!

标签:run,方式,Thread,method1,三种,线程,执行,public
From: https://www.cnblogs.com/shengluBlog/p/17933454.html

相关文章

  • 添加一个SQL Server身份验证方式
    关键几步如下,特别是最后一步,记得勾选SQLServer和Windows.    参考网址:如何在SQLSERVER的windows身份验证添加一个SQLServer身份验证方式_sqlserverwindows身份验证添加-CSDN博客 ......
  • Java多线程:数据一致性问题及解决方案
    引言在面向对象的编程语言Java中,多线程编程是一个强大的工具,可以使我们能够构建高效率和高并发的应用程序。然而,多线程环境下的数据共享也带来了数据一致性的挑战。在本文中,我们将探讨Java多线程中的数据一致性问题,并提出几种解决方案。数据一致性问题当多个线程同时对共享资源进行......
  • 云服务器接入高防IP无法访问的原因以及处理方式
    云服务器,也称为ElasticComputeService(ECS),是一种简单高效、安全可靠、处理能力可弹性伸缩的计算服务。它是一种虚拟化的服务器,运行公共的操作系统和软件,并允许用户通过网络进行访问。用户无需提前购买硬件,即可迅速创建或释放任意多台云服务器。云服务器帮助用户快速构建更稳定、安......
  • 摩尔线程S2000GPU环境配置
    引子书接上文,这边再来一个国产AI芯片的环境配置,OK,让我们开始吧。一、安装系统Ubuntu20.04.11、USB光盘刻录系统2、安装3、系统主板BIOS开启Above4G及ResizeBAR功能4、命令lspci|grep123二、安装GPU环境1、安装驱动程序(服务器Ip:10.231.6.92,账号:nick密码:123456)......
  • WSO2是一个开源的API管理平台,它提供了一套完整的解决方案,用于设计和发布API,创建和管理
    WSO2是一个开源的API管理平台,它提供了一套完整的解决方案,用于设计和发布API,创建和管理开发人员社区,以及以可扩展的方式保护和路由API流量¹。它利用来自WSO2平台的成熟组件来保护,集成和管理API¹。此外,它还与WSO2分析平台集成,提供现成的报告和警报,让您即时了解API行为¹。WSO2API......
  • ASP.NET Core 内置异常中间件的三种方法
    方法一app.UseExceptionHandler(configure=>{configure.Run(asynccontext=>{varexcHandler=context.Features.Get<IExceptionHandlerPathFeature>();varex=excHandler.Error;if(ex!=null){conte......
  • 【SpringBoot快速入门】(2)SpringBoot的配置文件与配置方式详细讲解
    之前我们已经学习的Spring、SpringMVC、Mabatis、Maven,详细讲解了Spring、SpringMVC、Mabatis整合SSM的方案和案例,上一节我们学习了SpringBoot的开发步骤、工程构建方法以及工程的快速启动,从这一节开始,我们开始学习SpringBoot配置文件。接下来,我们逐步开始学习,本教程所有示例均基于......
  • 深入探究多线程中的虚假唤醒现象--从生产者消费者问题到高级解决方案的全方位解读
    文章目录生产者和消费者问题虚假呼唤问题解决方案线程之间的虚假唤醒问题常出现在多线程编程中。我看国内很多教程都解释的稀里糊涂的,所以打算写一篇博客好好絮叨絮叨。首先看一下线程虚假唤醒的定义:多线程环境下,有多个线程执行了wait()方法,需要其他线程执行notify()或者notifyAl......
  • postgresql数据库迁移(拷贝数据库文件方式迁移)
    旧机器A:1、找到postgresql的data目录2、停止postgresql数据库3、执行tar-zcvfdatabak.tar.gzdata/新机器B:tar-zxvf databak.tar.gz-C/usr/local/postgres/使用scp或是ftp,或是移动硬盘将压缩文件放到新机器B上,前提条件是B机器要装有新的postgresql数据库,先停止数据库,然后找......
  • Unreal入门,开关04,蓝图通讯的几种方式(总结)
    1.直接调用,一开始做的开关就是这种方式,在地图编辑的时候给开关对象设置好关联的灯的引用,触发的时候直接调用灯提供的开关函数即可1.1给ActorSwitch添加一个ObjectReference成员1.2在地图编辑器中初始化这个成员,关联到ActorLight1.3在Overlap事件中通过ActorLight的引......