乐趣区

关于人工智能:52-线程同步

在本节中,咱们将具体探讨 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 包中的高级同步工具有了更深刻的理解。请务必多实际,以坚固这些知识点。
举荐浏览:

https://mp.weixin.qq.com/s/dV2JzXfgjDdCmWRmE0glDA

https://mp.weixin.qq.com/s/an83QZOWXHqll3SGPYTL5g

退出移动版