当前位置: 首页 > article >正文

Java【线程与并发】

Java 的多线程可以让不同的程序块一起运行,达到多任务处理的目的。线程也是 Java 语言的一种对象,但并不是所有对象都可以成为线程。

Java 提供了三种创建线程的方法

  • 通过实现 Runnable 接口来创建线程
  • 通过继承Thread来创建线程
  • 通过 Callable 和 Future 创建线程

通过实现 Runnable 接口来创建线程

Java 程序只允许单一继承,即一个子类只能有一个父类,所以在 Java 中如果一个类继承某一个类,同时又想采用多线程,就不能用 Thread 类产生线程,要使用 Runnable 接口创建线程。多线程的定义语法如下:

class 类名 implements Runnable

{

    属性

    方法

    修饰符 run()   //覆写run()方法

    { 

        以线程处理的程序;

    }

}

//定义一个实现了Runnable接口的RunnableDemo类
class RunnableDemo implements Runnable 
{
    private Thread t; //用于存储线程对象
    private String threadName; //线程的名称
    
    //构造函数,接收线程名称作为参数
    RunnableDemo(String name) {
        threadName = name;
        System.out.println("Creating " + threadName);
    }
    
    //实现Runnable接口的run方法,定义线程的执行逻辑
    public void run() {
        System.out.println("Running " + threadName);
        try {
            for (int i = 4; i > 0; i--) {
                System.out.println("Thread: " + threadName + ", " + i);
                //让线程睡眠50毫秒
                Thread.sleep(50);
            }
        } catch (InterruptedException e) {
            System.out.println("Thread " + threadName + " interrupted.");
        }
        System.out.println("Thread " + threadName + " exiting.");
    }
    
    //start方法用于启动线程
    public void start() {
        System.out.println("Starting " + threadName);
        if (t == null) {
            //创建一个新的线程对象,将当前Runnable对象作为参数
            t = new Thread(this, threadName);
            //启动线程
            t.start();
        }
    }
}

//主类TestThread
public class TestThread {
    public static void main(String args[]) {
        //创建两个RunnableDemo对象,分别对应两个线程
        RunnableDemo R1 = new RunnableDemo("Thread-1");
        R1.start();
        
        RunnableDemo R2 = new RunnableDemo("Thread-2");
        R2.start();
    }
}

通过继承Thread来创建线程

继承类必须重写 run() 方法,该方法是新线程的入口点。它也必须调用 start() 方法才能执行。该方法尽管被列为一种多线程实现方式,但是本质上也是实现了 Runnable 接口的一个实例。

Thread 存放在 java.lang 类库里,但并不需要加载 java.lang 类库,因为它会自动加载。而 run() 方法是定义在 Thread 类里的一个方法,因此把线程的程序代码编写在 run() 方法内,事实上所做的工作就是覆写的操作。使一个类可激活线程,编写语法如下:

class 类名 extends Thread  //从Thread类扩展子类

{

    属性

    方法

    修饰符 run() {  //覆写run()方法

        以线程处理的程序;

    }

}

public class Test
{
    public static void main(String args[])
    {
        //创建TestThread类的一个实例,并调用start()方法启动该线程
        new TestThread().start();
        //循环输出
        for(int i=0; i<10; i++)
        {
            System.out.println("main 线程运行");
        }
    }
}

//定义一个继承自Thread类的TestThread类
class TestThread extends Thread
{
    public void run()
    {
        for(int i=0; i<10; i++)
        {
            System.out.println("TestThread 运行");
        }
    }
}

通过 Callable 和 Future 创建线程

  • Callable 接口与 Runnable 接口不同之处在于,Callable 接口可以返回一个结果,并且可以抛出异常。Callable 接口的 call() 方法用于定义线程的执行逻辑,并返回一个结果。
  • Future 接口用于表示异步计算的结果。Future 接口还提供了一些方法,如 get() 用于获取结果,cancel() 用于取消任务等。
  • FutureTask 类实现了 Future 接口和 Runnable 接口,可以作为任务提交给线程执行。它可以包装一个 Callable 对象或一个 Runnable 对象。当包装 Callable 对象时,可以获取任务的返回结果。

注意点

1. 创建 Callable 接口的实现类,并实现 call() 方法,该 call() 方法将作为线程执行体,并且有返回值。

2. 创建 Callable 实现类的实例,使用 FutureTask 类来包装 Callable 对象,该 FutureTask 对象封装了该 Callable 对象的 call() 方法的返回值。

3. 使用 FutureTask 对象作为 Thread 对象的 target 创建并启动新线程。

4. 调用 FutureTask 对象的 get() 方法来获得子线程执行结束后的返回值。

//定义一个实现了Callable接口的CallableThreadTest类,返回类型为Integer
public class CallableThreadTest implements Callable<Integer> {
    
    //主方法
    public static void main(String[] args)  
    {  
        //创建CallableThreadTest类的一个实例
        CallableThreadTest ctt = new CallableThreadTest();  
        
        //创建一个FutureTask对象,传入Callable对象
        FutureTask<Integer> ft = new FutureTask<>(ctt);  
        
        //主线程的循环
        for(int i = 0; i < 100; i++)  
        {  
            //打印当前线程的名称和循环变量i的值
            System.out.println(Thread.currentThread().getName() + " 的循环变量i的值" + i);  
            
            //当i等于20时,启动子线程
            if(i == 20)  
            {  
                //创建并启动一个新线程,传入FutureTask对象
                new Thread(ft, "有返回值的线程").start();  
            }  
        }  
        
        //尝试获取子线程的返回值
        try  
        {  
            //获取FutureTask的结果,这会阻塞主线程直到子线程执行完毕
            System.out.println("子线程的返回值:" + ft.get());  
        } catch (InterruptedException e)  
        {  
            e.printStackTrace();  
        } catch (ExecutionException e)  
        {  
            e.printStackTrace();  
        }  
    }
    
    //实现Callable接口的call方法,定义线程的执行逻辑
    @Override  
    public Integer call() throws Exception  
    {  
        int i = 0;  
        //子线程的循环
        for(; i < 100; i++)  
        {  
            //打印当前线程的名称和循环变量i的值
            System.out.println(Thread.currentThread().getName() + " " + i);  
        }  
        //返回循环变量i的最终值
        return i;  
    }  
}


http://www.kler.cn/a/467808.html

相关文章:

  • OSPF - 影响OSPF邻居建立的因素
  • 弥散张量分析开源软件 DSI Studio 简体中文汉化版可以下载了
  • 【insert 插入数据语法合集】.NET开源ORM框架 SqlSugar 系列
  • FQ-GAN代码解析
  • vite6+vue3+ts+prettier+eslint9配置前端项目(后台管理系统、移动端H5项目通用配置)
  • 基于开发/发布/缺陷分离模型的 Git 分支管理实践20250103
  • 项目配置设置二 (芒果头条项目进度3)
  • 大型 UniApp 应用的架构设计
  • HTTP、HTTP/2 和 gRPC 是网络通信协议或基于这些协议的技术,它们之间有显著的区别
  • 003__系统共享工具、服务器的使用
  • 高等数学学习笔记 ☞ 极限的运算法则与存在准则
  • MySQL(四)MySQL Select语句
  • RISC-V学习笔记
  • QML自定义数值编辑框SpinBox样式
  • 基于mysql数据库实现分布式锁
  • 73 mysql replication 集群的交互
  • Python 数据结构揭秘:栈与队列
  • HDFS块预留导致的存储空间异常的问题探究
  • python.exe无法找到程序入口 无法定位程序输入点(Anaconda Prompt报错)
  • 基于JAVA+SpringBoot+Vue的校园外卖服务系统
  • 无刷电机驱动板原理图解析
  • LinuxC高级day2
  • 模型训练二三事:参数个数、小批量、学习率衰减、输入形状
  • 044_Standalone App in Matlab中发布独立应用
  • [网络安全]sqli-labs Less-3 解题详析
  • vue elementUI Plus实现拖拽流程图,不引入插件,纯手写实现。