授课语音

Java多线程同步和通信

内容包括基本概念、同步与通信方法、常见线程安全问题的模拟、线程同步的不同实现方式,以及具体的代码示例

1. 线程的常用方法

  • sleep(long millis):让线程进入休眠状态,暂停指定时间。
  • join():让当前线程等待另一个线程执行完毕。
  • yield():提示让出CPU资源,但不保证实际效果,可能立即再次执行。
  • interrupt():中断线程的执行。
  • isAlive():判断线程是否还在运行。

代码示例:使用 sleepjoin

public class ThreadMethodsDemo {
    public static void main(String[] args) {
        Thread thread1 = new Thread(() -> {
            for (int i = 1; i <= 3; i++) {
                System.out.println("线程1执行次数: " + i);
                try {
                    Thread.sleep(500); // 线程暂停500毫秒
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        Thread thread2 = new Thread(() -> {
            try {
                thread1.join(); // 等待线程1完成后再执行
                System.out.println("线程2开始执行");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

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

2. 线程安全问题的模拟

  • 当多个线程同时访问和修改共享资源(如变量或对象)时,如果没有适当的同步措施,就可能导致数据不一致的情况。

代码示例:线程安全问题模拟

public class UnsafeCounter {
    private int count = 0;

    public void increment() {
        count++; // 非线程安全的操作
    }

    public int getCount() {
        return count;
    }

    public static void main(String[] args) {
        UnsafeCounter counter = new UnsafeCounter();
        Runnable task = () -> {
            for (int i = 0; i < 1000; i++) {
                counter.increment();
            }
        };

        Thread t1 = new Thread(task);
        Thread t2 = new Thread(task);
        t1.start();
        t2.start();

        try {
            t1.join();
            t2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("最终计数值: " + counter.getCount());
    }
}

在该示例中,由于 increment 方法是非同步的,可能导致线程间计数值不一致。


3. 线程同步

  • 线程同步用于解决线程安全问题,保证多个线程对共享资源的操作是有序的。常见同步方式包括:
    • 同步代码块
    • 同步方法
    • Lock锁

3.1 同步代码块

  • 使用 synchronized 关键字,指定一个同步代码块,仅允许一个线程进入。
public class SynchronizedBlock {
    private int count = 0;

    public void increment() {
        synchronized (this) { // 使用同步代码块,保证线程安全
            count++;
        }
    }

    public int getCount() {
        return count;
    }
}

3.2 同步方法

  • 在方法前加 synchronized 关键字,使整个方法在同一时间内只能被一个线程执行。
public class SynchronizedMethod {
    private int count = 0;

    public synchronized void increment() { // 同步方法
        count++;
    }

    public int getCount() {
        return count;
    }
}

3.3 使用 Lock 实现同步

  • Lock 是一种显式的锁机制,提供更细粒度的锁控制。
  • 特点:支持可重入锁、锁超时和非阻塞锁。
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class LockExample {
    private int count = 0;
    private final Lock lock = new ReentrantLock();

    public void increment() {
        lock.lock(); // 获取锁
        try {
            count++;
        } finally {
            lock.unlock(); // 确保在异常情况下释放锁
        }
    }

    public int getCount() {
        return count;
    }
}

4. 线程通信

  • 线程通信用于解决线程间的协调问题,Java提供了 wait()notify()notifyAll() 方法,常用于生产者-消费者模式。

代码示例:生产者-消费者问题

import java.util.LinkedList;
import java.util.Queue;

public class ProducerConsumer {
    private final Queue<Integer> queue = new LinkedList<>();
    private final int CAPACITY = 5;

    public void produce() throws InterruptedException {
        int value = 0;
        while (true) {
            synchronized (this) {
                while (queue.size() == CAPACITY) {
                    wait(); // 队列满时,等待消费者消费
                }
                queue.offer(value);
                System.out.println("生产者生产了: " + value);
                value++;
                notifyAll(); // 通知消费者可以消费
            }
        }
    }

    public void consume() throws InterruptedException {
        while (true) {
            synchronized (this) {
                while (queue.isEmpty()) {
                    wait(); // 队列为空时,等待生产者生产
                }
                int value = queue.poll();
                System.out.println("消费者消费了: " + value);
                notifyAll(); // 通知生产者可以继续生产
            }
        }
    }

    public static void main(String[] args) {
        ProducerConsumer pc = new ProducerConsumer();

        Thread producerThread = new Thread(() -> {
            try {
                pc.produce();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        Thread consumerThread = new Thread(() -> {
            try {
                pc.consume();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        producerThread.start();
        consumerThread.start();
    }
}

5. 多线程同步和通信的最佳实践

  1. 尽量使用同步代码块代替同步方法,避免锁定不必要的代码,提高性能。
  2. 避免死锁:多个线程互相等待资源,造成程序阻塞,需合理安排锁的获取顺序。
  3. 使用合适的锁机制:在需要更加灵活控制锁的释放与获取时,推荐使用 Lock 接口。
  4. 减少同步代码块的长度:同步块越小,越能减少对其他线程的影响。
  5. 合理利用线程通信:生产者-消费者模式非常适合解决线程协调问题,保证资源的平衡使用。

以上是关于Java多线程同步和通信的内容,涵盖了从基础概念到代码实现的各个方面。希望通过这些内容,您能深入理解Java多线程的同步和通信技巧,在实际应用中更好地处理并发问题,提高程序的安全性和效率。

去1:1私密咨询

系列课程: