如何在Java中实现多线程编程

简介: Java多线程编程有三种主要方式:继承Thread类、实现Runnable接口、实现Callable接口(结合Future获取结果),推荐使用Runnable避免单继承限制。通过线程池(如ExecutorService)可高效管理线程,提升性能。多线程共享资源时需注意线程安全,使用synchronized或Lock机制保证数据一致性。适用于并发执行、异步计算等场景。

在Java中实现多线程编程主要有三种核心方式:继承Thread实现Runnable接口实现Callable接口(结合Future。此外,还可以通过线程池(ExecutorService)更高效地管理线程。以下是详细实现方法及代码示例:

一、继承Thread

Thread类是Java线程的基础类,通过继承它并重写run()方法定义线程执行逻辑,调用start()方法启动线程(而非直接调用run())。

// 1. 继承Thread类
class MyThread extends Thread {
   
    // 2. 重写run()方法,定义线程执行逻辑
    @Override
    public void run() {
   
        for (int i = 0; i < 5; i++) {
   
            // Thread.currentThread().getName()获取当前线程名称
            System.out.println(Thread.currentThread().getName() + ":执行第" + i + "次");
            try {
   
                Thread.sleep(500); // 线程休眠500ms(模拟耗时操作)
            } catch (InterruptedException e) {
   
                e.printStackTrace();
            }
        }
    }
}

public class ThreadDemo {
   
    public static void main(String[] args) {
   
        // 3. 创建线程对象
        MyThread thread1 = new MyThread();
        MyThread thread2 = new MyThread();

        // 设置线程名称(可选)
        thread1.setName("线程A");
        thread2.setName("线程B");

        // 4. 启动线程(调用start(),而非run())
        thread1.start();
        thread2.start();
    }
}

输出(顺序可能不同,体现线程并发)

线程A:执行第0次
线程B:执行第0次
线程A:执行第1次
线程B:执行第1次
...

二、实现Runnable接口

Runnable是函数式接口(仅含run()方法),通过实现它定义线程逻辑,再将实例传入Thread类启动。推荐此方式,因为Java单继承,但可多实现。

// 1. 实现Runnable接口
class MyRunnable implements Runnable {
   
    // 2. 实现run()方法
    @Override
    public void run() {
   
        for (int i = 0; i < 5; i++) {
   
            System.out.println(Thread.currentThread().getName() + ":执行第" + i + "次");
            try {
   
                Thread.sleep(500);
            } catch (InterruptedException e) {
   
                e.printStackTrace();
            }
        }
    }
}

public class RunnableDemo {
   
    public static void main(String[] args) {
   
        // 3. 创建Runnable实例
        MyRunnable runnable = new MyRunnable();

        // 4. 将Runnable传入Thread,创建线程对象
        Thread thread1 = new Thread(runnable, "线程C");
        Thread thread2 = new Thread(runnable, "线程D");

        // 5. 启动线程
        thread1.start();
        thread2.start();
    }
}

特点:多个线程可共享同一个Runnable实例的资源(如成员变量),适合多线程协作。

三、实现Callable接口(带返回值)

Callable接口与Runnable类似,但允许线程执行后返回结果,且可抛出受检异常。需结合FutureFutureTask获取结果。

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

// 1. 实现Callable接口,指定返回值类型(此处为Integer)
class MyCallable implements Callable<Integer> {
   
    private int num;

    public MyCallable(int num) {
   
        this.num = num;
    }

    // 2. 实现call()方法(有返回值)
    @Override
    public Integer call() throws Exception {
   
        int sum = 0;
        for (int i = 1; i <= num; i++) {
   
            sum += i;
        }
        System.out.println(Thread.currentThread().getName() + ":计算完成");
        return sum; // 返回结果
    }
}

public class CallableDemo {
   
    public static void main(String[] args) throws ExecutionException, InterruptedException {
   
        // 3. 创建Callable实例
        MyCallable callable = new MyCallable(100);

        // 4. 用FutureTask包装Callable(FutureTask实现了Future和Runnable)
        FutureTask<Integer> futureTask = new FutureTask<>(callable);

        // 5. 传入Thread启动
        Thread thread = new Thread(futureTask, "计算线程");
        thread.start();

        // 6. 调用get()获取结果(会阻塞当前线程,直到子线程执行完毕)
        int result = futureTask.get();
        System.out.println("1~100的和:" + result); // 输出:5050
    }
}

特点:适合需要线程返回结果的场景(如异步计算)。

四、线程池(ExecutorService

频繁创建/销毁线程会消耗资源,线程池可复用线程,提高效率。通过Executors工具类创建线程池(实际开发中推荐ThreadPoolExecutor自定义)。

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ThreadPoolDemo {
   
    public static void main(String[] args) {
   
        // 1. 创建固定大小的线程池(3个线程)
        ExecutorService executor = Executors.newFixedThreadPool(3);

        // 2. 提交任务(Runnable或Callable)
        for (int i = 0; i < 5; i++) {
   
            final int taskNum = i;
            executor.submit(new Runnable() {
   
                @Override
                public void run() {
   
                    System.out.println(Thread.currentThread().getName() + ":执行任务" + taskNum);
                    try {
   
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
   
                        e.printStackTrace();
                    }
                }
            });
        }

        // 3. 关闭线程池(不再接受新任务,等待现有任务完成)
        executor.shutdown();
    }
}

常用线程池类型

  • newFixedThreadPool(n):固定大小的线程池
  • newCachedThreadPool():可缓存的线程池(线程数动态调整)
  • newSingleThreadExecutor():单线程池(任务串行执行)
  • newScheduledThreadPool(n):支持定时/周期性任务的线程池

五、线程常用方法

方法 说明
start() 启动线程(底层调用run()
run() 线程执行逻辑(需重写)
sleep(long ms) 让当前线程休眠指定毫秒(不会释放锁)
join() 等待该线程执行完毕后,再继续执行其他线程
yield() 让出CPU资源,让其他线程优先执行
setPriority(int) 设置线程优先级(1~10,默认5)
isAlive() 判断线程是否存活

六、线程安全问题

多线程共享资源时可能出现数据不一致,需通过同步机制解决:

  • synchronized关键字(同步方法/代码块)
  • Lock接口(如ReentrantLock

示例(synchronized解决线程安全)

class Counter {
   
    private int count = 0;

    // 同步方法:保证同一时间只有一个线程执行
    public synchronized void increment() {
   
        count++;
    }

    public int getCount() {
   
        return count;
    }
}

public class ThreadSafety {
   
    public static void main(String[] args) throws InterruptedException {
   
        Counter counter = new Counter();
        Thread t1 = new Thread(() -> {
   
            for (int i = 0; i < 10000; i++) {
   
                counter.increment();
            }
        });
        Thread t2 = new Thread(() -> {
   
            for (int i = 0; i < 10000; i++) {
   
                counter.increment();
            }
        });

        t1.start();
        t2.start();
        t1.join(); // 等待t1完成
        t2.join(); // 等待t2完成

        System.out.println("最终计数:" + counter.getCount()); // 正确输出20000(无同步可能小于20000)
    }
}

总结

  • 简单场景:用ThreadRunnable(推荐Runnable)。
  • 需要返回值:用Callable + Future
  • 高效管理线程:用线程池。
  • 多线程共享资源:需考虑线程安全(synchronizedLock)。

多线程编程的核心是并发协作资源同步,实际开发中需根据场景选择合适的实现方式。

相关文章
|
3月前
|
人工智能 自然语言处理 安全
SOFA AI 网关基于 Higress 的落地实践
SOFA 商业化团队为满足客户 AI 业务的发展需求,基于开源 Higress 内核构建,推出了 SOFA AI 网关,专为 SOFA 场景深度优化、能力增强,是面向 AI 需求的智能网关解决方案。
308 23
|
监控 网络协议 物联网
你知道什么是物联网MQTT么?
你知道什么是物联网MQTT么?
1012 0
|
3月前
|
IDE Java 编译器
java编程最基础学习
Java入门需掌握:环境搭建、基础语法、面向对象、数组集合与异常处理。通过实践编写简单程序,逐步深入学习,打牢编程基础。
249 1
|
3月前
|
人工智能 智能设计 数据可视化
挑战杯阿里云赛题收官,看大学生如何用AI技术助力乡村振兴
第十九届“挑战杯”人工智能领域擂台赛聚焦乡村振兴,阿里云联合公益力量,组织全国800所高校学子,运用AI技术为浙江开化、江西遂川定制文旅与农产品创新方案,推动赛事成果落地转化,助力乡村发展。
|
3月前
|
监控 算法 固态存储
Ashampoo UnInstaller使用教程!高效彻底的软件卸载助手(附下载)
Ashampoo UnInstaller 可彻底卸载软件并清理残留文件、注册表等,提升系统速度与稳定性。支持批量卸载、程序迁移、安装保护及实时监控,操作简单,是优化电脑性能的高效工具。
304 1
|
算法 Java
JAVA 雪花算法 唯一ID生成工具类
JAVA 雪花算法 唯一ID生成工具类
2977 0
|
NoSQL Redis
Redis 执行 Lua保证原子性原理
Redis 执行 Lua 保证原子性原理
1085 1
|
10月前
|
缓存 自然语言处理 JavaScript
JavaScript中闭包详解+举例,闭包的各种实践场景:高级技巧与实用指南
闭包是JavaScript中不可或缺的部分,它不仅可以增强代码的可维护性,还能在模块化、回调处理等场景中发挥巨大作用。然而,闭包的强大也意味着需要谨慎使用,避免潜在的性能问题和内存泄漏。通过对闭包原理的深入理解以及在实际项目中的灵活应用,你将能够更加高效地编写出简洁且功能强大的代码。 只有锻炼思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~
|
存储 安全 Java
synchronized原理详解(通俗易懂超级好)
当系统检查到锁是重量级锁之后,会把等待想要获得锁的线程进行阻塞,被阻塞的线程不会消耗cpu。但是阻塞或者唤醒一个线程时,都需要操作系统来帮忙,这就需要从用户态转换到内核态,而转换状态是需要消耗很多时间的,有可能比用户执行代码的时间还要长。
synchronized原理详解(通俗易懂超级好)
|
Java
java将输入的数字金额转换为中文大写金额
java将输入的数字金额转换为中文大写金额
1396 1