JAVA之线程间的通信机制

Java中线程间的通信机制主要用于协调多个线程之间的协作,使得它们能够在特定的时机交换信息、同步执行或触发特定行为。

以下是Java中常用的线程间通信机制:

  1. 共享变量与同步机制
    • volatile关键字:用来修饰共享变量,确保线程对变量的修改对其他线程立即可见,消除指令重排序带来的影响,适用于简单的状态标记等场景。
    • synchronized关键字:用于实现临界区同步,通过锁定特定对象,确保同一时刻只有一个线程访问共享资源。在synchronized代码块或方法中,可以安全地修改共享变量,同时配合wait()、notify()、notifyAll()方法实现线程间的通信。
      • wait():当前线程释放锁并进入等待状态,直到被其他线程调用notify()或notifyAll()唤醒。
      • notify():唤醒一个因调用wait()而处于等待状态的线程。
      • notifyAll():唤醒所有因调用wait()而处于等待状态的线程。
/**
 * 共享变量与同步机制(synchronized + wait/notify)
 */
public class SharedVarExample {
    private int value;
    private final Object lock = new Object();

    public void increment() {
        synchronized (lock) {
            value++;
            System.out.printf("增加value值为:%d\n", value);
            lock.notify();//唤醒等待的线程
        }
    }

    public void decrement() {
        synchronized (lock) {
            while (value == 0) { //条件不满足时等待
                try {
                    System.out.println("等待value值被修改...");
                    lock.wait();
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }
            value--;
            System.out.printf("减少value值为:%d\n", value);
        }
    }

    public static void main(String[] args) throws InterruptedException {
        SharedVarExample example = new SharedVarExample();

        //创建修改共享资源的线程
        Thread updateThread = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    TimeUnit.SECONDS.sleep(1); //模拟延迟,确保等待线程先启动
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
                example.increment();
            }
        });

        //创建等待共享资源变化的线程
        Thread waitThread = new Thread(new Runnable() {
            @Override
            public void run() {
                example.decrement();
            }
        });

        //启动线程
        waitThread.start();
        updateThread.start();

        //等待两个线程结束
        waitThread.join();
        updateThread.join();

        System.out.println("两个线程都执行完了。");
    }
    //执行结果
    /*
    等待value值被修改...
    增加value值为:1
    减少value值为:0
    两个线程都执行完了。
    */
}
  1. Lock与Condition接口
    • ReentrantLock:可重入锁,比synchronized提供了更灵活的锁定机制,如公平锁、非公平锁的选择,以及尝试加锁、定时加锁等操作。
    • Condition:与ReentrantLock配套使用,替代了synchronized的wait/notify机制。每个Condition对象都关联一个Lock,线程可以调用condition.await()进入等待状态,并在其他线程调用condition.signal()或condition.signalAll()时被唤醒。Condition允许创建多个等待-通知条件,适用于更为复杂的同步场景。
/**
 * Lock与Condition接口
 */
public class LockConditionExample {
    private Lock lock = new ReentrantLock();
    private Condition condition = lock.newCondition();
    private boolean ready;

    public void before() {
        lock.lock();
        try {
            // 执行一些操作...
            ready = true;
            condition.signalAll(); // 唤醒等待的线程
        } finally {
            lock.unlock();
        }
    }

    public void after() {
        lock.lock();
        try {
            while (!ready) { // 条件不满足时等待
                condition.await();
            }
            // 执行后续操作...
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        } finally {
            lock.unlock();
        }
    }
}
  1. 阻塞队列(BlockingQueue)
    • Java提供了多种实现如ArrayBlockingQueue、LinkedBlockingQueue等,它们在插入元素(生产者)和移除元素(消费者)时可以自动阻塞和唤醒线程,实现了生产者与消费者模式的线程间通信。线程无需显式调用wait/notify,只需进行正常的队列操作即可实现线程间的协调。
public class BlockingQueueExample {
    private static final int QUEUE_CAPACITY = 10;
    private final BlockingQueue<String> queue = new ArrayBlockingQueue<>(QUEUE_CAPACITY);

    class ProducerThread extends Thread {
        @Override
        public void run() {
            String item = "Item";
            try {
                while (true) {
                    TimeUnit.MILLISECONDS.sleep(100); //模拟延迟
                    queue.put(item);
                    System.out.println("Produced: " + item);
                    item += " (new)";
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
    }

    class ConsumerThread extends Thread {
        @Override
        public void run() {
            try {
                while (true) {
                    TimeUnit.MILLISECONDS.sleep(100); //模拟延迟
                    String item = queue.take();
                    System.out.println("Consumed: " + item);
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
    }

    public static void main(String[] args) {
        BlockingQueueExample example = new BlockingQueueExample();
        ProducerThread producer = example.new ProducerThread();
        ConsumerThread consumer = example.new ConsumerThread();

        // 启动生产者和消费者线程
        producer.start();
        consumer.start();

        // 为了让示例程序在控制台输出一些信息后退出,这里添加了主线程睡眠一段时间
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }

        // 为了演示目的,这里强制中断生产者和消费者线程(实际应用中可能需要更优雅的方式停止线程)
        producer.interrupt();
        consumer.interrupt();
    }
    //执行结果:
    /*
    Produced: Item
    Consumed: Item
    Produced: Item (new)
    Consumed: Item (new)
    Produced: Item (new) (new)
    Consumed: Item (new) (new)
    Consumed: Item (new) (new) (new)
    Produced: Item (new) (new) (new)
    ......
     */
}
  1. 管道流(PipedInputStream/PipedOutputStream、PipedReader/PipedWriter)
    • 管道流允许在两个线程之间直接传递数据,一个线程通过输出流写入数据,另一个线程通过输入流读取数据。这种方式适用于线程间需要交换大量数据或流式数据的情况。
public class PipedStreamExample {
    private final PipedInputStream input = new PipedInputStream();
    private final PipedOutputStream output = new PipedOutputStream(input);

    public PipedStreamExample() throws IOException {
    }

    class ReaderThread extends Thread {
        @Override
        public void run() {
            try {
                byte[] buffer = new byte[1024];
                int read;
                while ((read = input.read(buffer)) != -1) {
                    System.out.println("Received: " + new String(buffer, 0, read));
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    class WriterThread extends Thread {
        @Override
        public void run() {
            try {
                String message = "Hello, Pipe!";
                output.write(message.getBytes());
                output.flush();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) throws IOException {
        PipedStreamExample example = new PipedStreamExample();

        // 创建并启动读取线程
        ReaderThread reader = example.new ReaderThread();
        reader.start();

        // 确保读取线程已启动后再启动写入线程
        try {
            while (!reader.isAlive()) {
                TimeUnit.MILLISECONDS.sleep(100);
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }

        WriterThread writer = example.new WriterThread();
        writer.start();

        // 等待写入线程完成(实际应用中可能需要更优雅的方式停止线程)
        try {
            writer.join();
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
    //执行结果:
    /*
    Received: Hello, Pipe!
    */
}
  1. Future与Callable
    • 通过ExecutorService提交Callable任务并获取Future对象,主线程可以调用Future的get()方法阻塞等待异步任务的结果,从而实现线程间的同步通信。
public class FutureCallableExample {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ExecutorService executor = Executors.newSingleThreadExecutor();
        Future<Integer> future = executor.submit(new Callable<Integer>() {
            @Override
            public Integer call() {
                return longRunningTask();
            }
        });

        // 主线程可以继续执行其他任务...

        Integer result = future.get(); // 阻塞等待结果
        System.out.println("Result: " + result);

        executor.shutdown();
    }

    private static int longRunningTask() {
        // 执行耗时操作...
        return 666;
    }
    //执行结果:
    /*
      Result: 666
    */
}
  1. 并发工具类
    • CountDownLatch:允许一个或多个线程等待其他线程完成一组操作后再继续执行,常用于实现线程间的阶段性同步。
public class CountDownLatchExample {
    public static void main(String[] args) throws InterruptedException {
        CountDownLatch startSignal = new CountDownLatch(1);
        CountDownLatch doneSignal = new CountDownLatch(3);

        for (int i = 0; i < 3; ++i) { // 3个任务
            new Thread(new Worker(startSignal, doneSignal)).start();
        }

        doSomethingElse(); // 在所有任务开始前做其他事

        startSignal.countDown(); // 放行所有等待的任务

        doneSignal.await(); // 等待所有任务完成

        System.out.println("所有任务执行完成");
    }

    private static void doSomethingElse() {
        System.out.println("在所有任务开始前做其他事");
        // ...
    }

    static class Worker implements Runnable {
        private final CountDownLatch startSignal;
        private final CountDownLatch doneSignal;

        Worker(CountDownLatch startSignal, CountDownLatch doneSignal) {
            this.startSignal = startSignal;
            this.doneSignal = doneSignal;
        }

        @Override
        public void run() {
            try {
                startSignal.await(); // 等待开始信号

                doWork(); // 执行任务

                doneSignal.countDown(); // 任务完成,计数减一
            } catch (InterruptedException ex) {
                Thread.currentThread().interrupt();
            }
        }

        private void doWork() {
            System.out.println("执行任务...");
            // ...
        }
    }
    //执行结果:
    /*
    在所有任务开始前做其他事
    执行任务...
    执行任务...
    执行任务...
    所有任务执行完成
    */
}
  • CyclicBarrier:让一组线程到达某个屏障(同步点)时暂停,直到所有线程都到达后才解除阻塞,继续执行。
public class CyclicBarrierExample {
    public static void main(String[] args) {
        int parties = 4;
        CyclicBarrier barrier = new CyclicBarrier(parties, () -> {
            System.out.println("所有线程都已到达屏障");
        });

        for (int i = 0; i < parties; i++) {
            new Thread(() -> {
                try {
                    System.out.println(Thread.currentThread().getName() + " 线程执行中...");
                    barrier.await(); // 等待其他线程到达
                    System.out.println(Thread.currentThread().getName() + " 已达到障碍");
                } catch (BrokenBarrierException | InterruptedException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
    //执行结果:
    /*
    Thread-0 线程执行中...
    Thread-1 线程执行中...
    Thread-2 线程执行中...
    Thread-3 线程执行中...
    所有线程都已到达屏障
    Thread-3 已达到障碍
    Thread-0 已达到障碍
    Thread-2 已达到障碍
    Thread-1 已达到障碍
    */
}
  • Semaphore:基于计数的信号量,用于控制同时访问特定资源的线程数量,线程可以通过acquire()获取许可,release()释放许可,实现资源的协调与通信。
public class SemaphoreExample {
    private final Semaphore semaphore = new Semaphore(3); // 最大并发数为3

    public void accessResource() {
        try {
            semaphore.acquire(); // 获取许可,若无可用许可则阻塞
            // 访问资源
            System.out.println(Thread.currentThread().getName() + "线程正在访问资源");
            Thread.sleep(1000); // 模拟耗时操作
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        } finally {
            semaphore.release(); // 释放许可
        }
    }

    public static void main(String[] args) {
        SemaphoreExample example = new SemaphoreExample();
        for (int i = 0; i < 10; i++) {
            new Thread(() -> example.accessResource()).start();
        }
    }
    //执行结果:
    /*
    Thread-0线程正在访问资源
    Thread-2线程正在访问资源
    Thread-1线程正在访问资源
    Thread-3线程正在访问资源
    Thread-5线程正在访问资源
    Thread-4线程正在访问资源
    Thread-6线程正在访问资源
    Thread-8线程正在访问资源
    Thread-7线程正在访问资源
    Thread-9线程正在访问资源
    */
}

在使用以上线程间通信机制时,需注意线程安全问题,避免数据竞争和死锁的发生。合理选择和组合这些机制,可以帮助编写出高效、可靠的多线程应用程序。

  • 25
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

执子之意

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值