在本节中,咱们将具体探讨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
包提供了许多高级的并发工具,如Semaphore
、CountDownLatch
、CyclicBarrier
、ReentrantLock
等。这些工具能够帮忙咱们更容易地编写线程平安的代码。
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
发表回复