为了账号安全,请及时绑定邮箱和手机立即绑定

5.2. 线程同步

标签:
Java

在本节中,我们将详细讨论Java中的线程同步,包括synchronized关键字的使用、wait()notify()方法的使用以及java.util.concurrent包中的高级同步工具。我们将通过实例来讲解每个知识点,确保你能够全面理解。

5.2.1 synchronized关键字

我们已经在上一节简要介绍了synchronized关键字的使用。在本节中,我们将详细讨论synchronized关键字。

synchronized关键字可以修饰方法或者代码块,它能确保同一时刻只有一个线程可以执行被修饰的代码。当一个线程进入被synchronized关键字修饰的方法或代码块时,其他线程将无法访问该方法或代码块,直到第一个线程执行完毕。

5.2.1.1 synchronized修饰方法

示例:银行账户类

public class BankAccount {
    private double balance;

    public BankAccount(double balance) {
        this.balance = balance;
    }

    public synchronized void deposit(double amount) {
        balance += amount;
    }

    public synchronized void withdraw(double amount) {
        balance -= amount;
    }

    public synchronized double getBalance() {
        return balance;
    }
}

在上述示例中,我们使用synchronized关键字修饰了deposit()withdraw()getBalance()方法。这样,在同一时刻,只有一个线程可以访问这些方法。

5.2.1.2 synchronized修饰代码块

示例:银行账户类

public class BankAccount {
    private double balance;

    public BankAccount(double balance) {
        this.balance = balance;
    }

    public void deposit(double amount) {
        synchronized (this) {
            balance += amount;
        }
    }

    public void withdraw(double amount) {
        synchronized (this) {
            balance -= amount;
        }
    }

    public double getBalance() {
        synchronized (this) {
            return balance;
        }
    }
}

在上述示例中,我们使用synchronized关键字修饰了代码块,实现了和修饰方法相同的效果。

5.2.2 wait()、notify()和notifyAll()方法

wait()notify()notifyAll()方法用于线程间的通信。这些方法是java.lang.Object类的成员,因此每个Java对象都拥有这些方法。

5.2.2.1 wait()方法

wait()方法用于让当前线程等待,直到其他线程调用该对象的notify()notifyAll()方法。在调用wait()方法时,当前线程会释放对象锁,进入等待状态。

5.2.2.2 notify()方法

notify()方法用于唤醒在此对象监视器上等待的单个线程。如果有多个线程在等待,只会唤醒其中一个线程。

5.2.2.3 notifyAll()方法

notifyAll()方法用于唤醒在此对象监视器上等待的所有线程。

示例:生产者和消费者问题

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

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

    public void produce() {
        synchronized (this) {
            while (queue.size() == MAX_SIZE) {
                try {
                    wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            int value = (int) (Math.random() * 100);
            System.out.println("Produced: " + value);
            queue.add(value);
            notify();
        }
    }

    public void consume() {
        synchronized (this) {
            while (queue.isEmpty()) {
                try {
                    wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            int value = queue.poll();
            System.out.println("Consumed: " + value);
            notify();
        }
    }
}

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

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

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

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

在上述示例中,我们实现了一个生产者和消费者问题的解决方案。生产者和消费者共享一个队列,生产者向队列中添加数据,消费者从队列中取出数据。produce()consume()方法使用synchronized关键字保证线程安全,wait()notify()方法用于线程间的通信。

5.2.3 java.util.concurrent包

java.util.concurrent包提供了许多高级的并发工具,如SemaphoreCountDownLatchCyclicBarrierReentrantLock等。这些工具可以帮助我们更容易地编写线程安全的代码。

5.2.3.1 Semaphore(信号量)

Semaphore用于限制可以访问某些资源(或者执行某些操作)的线程数量。它有一个计数器,当一个线程获得许可时,计数器会减1;当线程释放许可时,计数器会加1。

示例:限制同时访问文件的线程数量

import java.util.concurrent.Semaphore;

public class FileAccessController {
    private Semaphore semaphore;

    public FileAccessController(int maxConcurrentAccess) {
        semaphore = new Semaphore(maxConcurrentAccess);
    }

    public void readFile() {
        try {
            semaphore.acquire();
            System.out.println("Thread " + Thread.currentThread().getName() + " is reading file...");
            Thread.sleep(2000);
            System.out.println("Thread " + Thread.currentThread().getName() + " finished reading file.");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            semaphore.release();
        }
    }

    public static void main(String[] args) {
        FileAccessController controller = new FileAccessController(3);

        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                controller.readFile();
            }, "Thread-" + i).start();
        }
    }
}

在上述示例中,我们使用Semaphore限制了同时访问文件的线程数量。当线程数量超过限制时,其他线程必须等待。

5.2.3.2 ReentrantLock(可重入锁)

ReentrantLock是一种可重入的互斥锁,与synchronized关键字类似,但提供了更多的灵活性。ReentrantLock允许我们显示地加锁和解锁,还支持公平锁和非公平锁。

示例:银行账户类

import java.util.concurrent.locks.ReentrantLock;

public class BankAccount {
    private double balance;
    private ReentrantLock lock = new ReentrantLock();

    public BankAccount(double balance) {
        this.balance = balance;
    }

    public void deposit(double amount) {
        lock.lock();
        try {
            balance += amount;
        } finally {
            lock.unlock();
        }
    }

    public void withdraw(double amount) {
        lock.lock();
        try {
            balance -= amount;
        } finally {
            lock.unlock();
        }
    }

    public double getBalance() {
        lock.lock();
        try {
            return balance;
        } finally {
            lock.unlock();
        }
    }
}

在上述示例中,我们使用ReentrantLock替换了synchronized关键字,实现了相同的线程安全效果。

以上就是Java多线程与并发的“5.2 线程同步”的所有内容。通过这些示例,你应该对synchronized关键字、wait()notify()方法以及java.util.concurrent包中的高级同步工具有了更深入的了解。请务必多实践,以巩固这些知识点。

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消