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

java中多线程的一些常见操作

Java 中的多线程是通过并发编程来提高应用程序的效率和响应速度。Java 提供了多个机制和类来支持多线程编程,包括继承 Thread 类、实现 Runnable 接口、使用线程池等。以下是 Java 中一些常见的多线程操作和应用场景。

1. 创建线程

1.1 通过继承 Thread 类创建线程

继承 Thread 类并重写 run 方法是创建线程的一种方式。run 方法包含线程的执行体。

public class MyThread extends Thread {
    @Override
    public void run() {
        System.out.println("Thread is running: " + Thread.currentThread().getName());
    }

    public static void main(String[] args) {
        MyThread thread = new MyThread();
        thread.start();  // 启动线程
    }
}
  • start() 方法会启动线程并调用 run() 方法。
  • run() 方法不能直接调用,必须通过 start() 启动线程。
1.2 通过实现 Runnable 接口创建线程

Runnable 接口适合当你不想继承 Thread 类时使用。你只需要实现 run 方法,然后将其传递给 Thread 对象。

public class MyRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println("Thread is running: " + Thread.currentThread().getName());
    }

    public static void main(String[] args) {
        MyRunnable myRunnable = new MyRunnable();
        Thread thread = new Thread(myRunnable);
        thread.start();  // 启动线程
    }
}

2. 线程的生命周期

线程有几个常见的生命周期状态:

  • 新建(New):线程对象被创建,但未调用 start() 方法。
  • 可运行(Runnable):线程被启动,处于可运行状态,但可能被操作系统调度暂停。
  • 阻塞(Blocked):线程正在等待某个资源。
  • 等待(Waiting):线程正在等待其他线程执行某些操作。
  • 终止(Terminated):线程执行完毕,生命周期结束。

3. 线程的同步

当多个线程访问共享资源时,为了避免数据不一致的问题,通常需要使用同步机制。

3.1 使用 synchronized 关键字

synchronized 关键字用于在方法或代码块上加锁,确保在同一时间内只有一个线程能够执行被同步的代码块。

示例:同步方法
public class Counter {
    private int count = 0;

    // 使用 synchronized 修饰方法,保证线程安全
    public synchronized void increment() {
        count++;
    }

    public int getCount() {
        return count;
    }

    public static void main(String[] args) {
        Counter counter = new Counter();

        // 创建两个线程同时访问共享资源
        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                counter.increment();
            }
        });
        Thread t2 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                counter.increment();
            }
        });

        t1.start();
        t2.start();

        try {
            t1.join();  // 等待线程 t1 执行完毕
            t2.join();  // 等待线程 t2 执行完毕
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("Final count: " + counter.getCount());  // 输出: Final count: 2000
    }
}

在上面的例子中,increment 方法被 synchronized 修饰,这意味着同一时刻只有一个线程能够访问这个方法。

3.2 使用同步代码块

你也可以使用同步代码块来锁定指定的代码区域,从而减少锁的范围,提高效率。

public class Counter {
    private int count = 0;

    public void increment() {
        synchronized (this) {  // 锁定当前对象
            count++;
        }
    }

    public int getCount() {
        return count;
    }

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

        t1.start();
        t2.start();

        try {
            t1.join();  // 等待线程 t1 执行完毕
            t2.join();  // 等待线程 t2 执行完毕
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("Final count: " + counter.getCount());  // 输出: Final count: 2000
    }
}

在上面的代码中,increment 方法内的同步代码块确保了每次只有一个线程能够进入该代码块,防止了竞态条件的出现。

4. 线程间通信

线程间通信是在多个线程之间交换信息的机制。Java 提供了 wait()notify()notifyAll() 方法来实现线程间的通信。

4.1 使用 wait()notify() 进行线程通信
  • wait():使当前线程进入等待状态,并释放锁,直到被其他线程通知。
  • notify():通知一个正在等待的线程,使其从等待状态中醒来,继续执行。
  • notifyAll():通知所有正在等待的线程,所有线程都会尝试重新获取锁。
示例:生产者-消费者问题
class Storage {
    private int product = 0;
    private final int capacity = 10;

    // 生产者生产产品
    public synchronized void produce() throws InterruptedException {
        while (product >= capacity) {
            wait();  // 如果库存已满,生产者等待
        }
        product++;
        System.out.println("Produced, product count: " + product);
        notifyAll();  // 通知消费者线程
    }

    // 消费者消费产品
    public synchronized void consume() throws InterruptedException {
        while (product <= 0) {
            wait();  // 如果库存为空,消费者等待
        }
        product--;
        System.out.println("Consumed, product count: " + product);
        notifyAll();  // 通知生产者线程
    }
}

public class ProducerConsumer {
    public static void main(String[] args) {
        Storage storage = new Storage();

        // 生产者线程
        Thread producer = new Thread(() -> {
            try {
                for (int i = 0; i < 20; i++) {
                    storage.produce();
                    Thread.sleep(100);  // 模拟生产时间
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        // 消费者线程
        Thread consumer = new Thread(() -> {
            try {
                for (int i = 0; i < 20; i++) {
                    storage.consume();
                    Thread.sleep(150);  // 模拟消费时间
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        producer.start();
        consumer.start();
    }
}

在这个例子中,我们模拟了生产者和消费者的线程通信机制。生产者线程不断生产产品,而消费者线程不断消费产品。当产品库存满时,生产者等待;当产品库存为空时,消费者等待。

5. 线程池(Executor Service)

线程池是 Java 提供的一个高效的多线程管理工具,它可以避免创建过多的线程,减少系统资源的消耗。Java 中的线程池是通过 ExecutorService 接口来管理的。

5.1 创建线程池

线程池的创建通常使用 Executors 工厂类,它提供了几种常用的线程池:

  • newFixedThreadPool(int n):创建一个固定大小的线程池。
示例:使用 ExecutorService 执行任务
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ThreadPoolExample {
    public static void main(String[] args) {
        // 创建一个固定大小的线程池,池中有 3 个线程
        ExecutorService executorService = Executors.newFixedThreadPool(3);

        // 提交多个任务给线程池
        for (int i = 0; i < 5; i++) {
            executorService.submit(() -> {
                System.out.println("Task executed by: " + Thread.currentThread().getName());
                try {
                    Thread.sleep(1000);  // 模拟任务执行
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
        }

        // 关闭线程池
        executorService.shutdown();  // 提交完任务后调用 shutdown 来关闭线程池
    }
}

6. 线程的优先级

Java 允许设置线程的优先级,从而影响线程的调度顺序。线程的优先级是一个整数值,范围从 1 到 10,其中 1 为最低优先级,10 为最高优先级。

示例:设置线程优先级
public class ThreadPriority {
    public static void main(String[] args) {
        Thread highPriorityThread = new Thread(() -> {
            System.out.println("High priority thread is running.");
        });

        Thread lowPriorityThread = new Thread(() -> {
            System.out.println("Low priority thread is running.");
        });

        highPriorityThread.setPriority(Thread.MAX_PRIORITY);  // 设置高优先级
        lowPriorityThread.setPriority(Thread.MIN_PRIORITY);  // 设置低优先级

        highPriorityThread.start();
        lowPriorityThread.start();
    }
}

注意,虽然 Java 提供了线程优先级的设置,但线程调度是由操作系统管理的,不同的操作系统可能会根据自己的调度算法来决定线程的实际执行顺序。

7. 中断线程

线程的中断通常用于停止线程的执行或通知线程需要停止。通过调用线程的 interrupt() 方法可以设置线程的中断标志,而线程可以通过 isInterrupted() 方法来检查自己是否被中断。需要注意的是,interrupt() 并不会立即终止线程,它只是设置线程的中断状态,具体的中断行为需要在线程代码中自行判断并处理。

示例:中断线程
public class InterruptExample {
    public static void main(String[] args) throws InterruptedException {
        Thread longRunningThread = new Thread(() -> {
            try {
                for (int i = 0; i < 10; i++) {
                    if (Thread.interrupted()) {
                        System.out.println("Thread is interrupted, stopping...");
                        return;  // 响应中断,终止线程
                    }
                    System.out.println("Running... " + i);
                    Thread.sleep(1000);  // 模拟长时间任务
                }
            } catch (InterruptedException e) {
                System.out.println("Thread was interrupted during sleep.");
            }
        });

        longRunningThread.start();
        
        // 等待 3 秒后中断线程
        Thread.sleep(3000);
        longRunningThread.interrupt();  // 发出中断信号
    }
}

8. 线程的 join() 方法

join() 方法用于等待一个线程完成。当调用 join() 方法时,当前线程会阻塞,直到目标线程执行完毕为止。它常用于确保某些任务执行完之后再执行其他任务。

示例:使用 join() 等待线程完成
public class JoinExample {
    public static void main(String[] args) throws InterruptedException {
        Thread thread1 = new Thread(() -> {
            try {
                Thread.sleep(2000);  // 模拟任务执行
                System.out.println("Thread 1 finished");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        Thread thread2 = new Thread(() -> {
            try {
                Thread.sleep(1000);  // 模拟任务执行
                System.out.println("Thread 2 finished");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        thread1.start();
        thread2.start();

        // 等待 thread1 和 thread2 完成后再继续执行
        thread1.join();
        thread2.join();

        System.out.println("All threads finished");
    }
}

在这个例子中,main 线程会等待 thread1thread2 执行完毕后再继续执行。


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

相关文章:

  • Dubbo 核心知识全解析:原理、流程与关键机制
  • 前端路由 Hash 和 History 模式原理对比区别
  • 十二、Vue 路由
  • 【Vim Masterclass 笔记05】第 4 章:Vim 的帮助系统与同步练习
  • 【Nginx】Nginx代理模式相关概念解释及Nginx安装
  • 共阳极LED的控制与短路问题解析
  • Git快速入门(二)·本地仓库·GitHubDesktop的使用
  • 如何使用python清空特定路径下所有文件夹下中的文件,把空文件夹要保留下来
  • 【开源免费】基于Vue和SpringBoot的共享汽车管理系统(附论文)
  • 【ARM】Keil恢复默认设置
  • 低代码开发助力数字化转型的战略价值
  • vue的整理
  • C++中一些常用头文件及其解析
  • 大数据数仓Hive和数据集市、数据治理
  • [羊城杯 2024]hiden
  • IC验证面试常问问题
  • Lua : Coroutine(协程)
  • Linux(16)——安装和更新 RPM 软件包
  • 详细说明嵌入式linux中bootcmd与bootargs差异
  • leetcode hot 100 前k个高平元素
  • 线程同步——使用场景区分
  • 【每日学点鸿蒙知识】grid里面的item支持拖动问题、WebView回调问题、获取页面名称、弹幕效果实现、修改App输出路径 |
  • 基础14 C++申请内存的各种方法
  • 自动化测试的心得
  • Singleton: WebRTC中ThreadManager中的单例模式
  • [创业之路-231]:《华为闭环战略管理》-5-企业组织架构、业务架构、技术架构、产品架构等它们有哪些不同的地方,又有哪些是相同的?