Java中的线程同步详解
在多线程编程中,线程同步是一个非常重要的话题。线程同步的目的是为了保证多个线程访问共享资源时,能够避免数据的不一致性和竞争条件。Java 提供了多种机制来实现线程同步,包括 synchronized
关键字、显式锁 (ReentrantLock
)、信号量 (Semaphore
)、读写锁 (ReadWriteLock
) 等。本文将详细介绍 Java 中的线程同步机制,涵盖基本概念、常用方法和具体代码示例。
一、基本概念
线程同步是指协调多个线程对共享资源的访问,以保证数据的一致性和正确性。在没有同步的情况下,多个线程同时访问和修改同一个共享资源,可能会导致数据的不一致性和难以预测的错误。
二、synchronized
关键字
synchronized
是 Java 提供的最基本的线程同步机制。它可以用于方法和代码块,确保在同一时刻只有一个线程可以执行被 synchronized
修饰的方法或代码块。
1. 同步方法
同步方法是指在方法声明中使用 synchronized
关键字。
package cn.juwatech.threads;
public class SynchronizedMethodExample {
private int count = 0;
public synchronized void increment() {
count++;
}
public synchronized int getCount() {
return count;
}
public static void main(String[] args) {
SynchronizedMethodExample example = new SynchronizedMethodExample();
Runnable task = () -> {
for (int i = 0; i < 1000; i++) {
example.increment();
}
};
Thread thread1 = new Thread(task);
Thread thread2 = new Thread(task);
thread1.start();
thread2.start();
try {
thread1.join();
thread2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Final count: " + example.getCount());
}
}
2. 同步代码块
同步代码块是指在代码块中使用 synchronized
关键字。
package cn.juwatech.threads;
public class SynchronizedBlockExample {
private final Object lock = new Object();
private int count = 0;
public void increment() {
synchronized (lock) {
count++;
}
}
public int getCount() {
synchronized (lock) {
return count;
}
}
public static void main(String[] args) {
SynchronizedBlockExample example = new SynchronizedBlockExample();
Runnable task = () -> {
for (int i = 0; i < 1000; i++) {
example.increment();
}
};
Thread thread1 = new Thread(task);
Thread thread2 = new Thread(task);
thread1.start();
thread2.start();
try {
thread1.join();
thread2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Final count: " + example.getCount());
}
}
三、显式锁 (ReentrantLock
)
ReentrantLock
是 Java 提供的显式锁,功能比 synchronized
更加丰富。它提供了更高的灵活性,可以显式地获取和释放锁,并且支持公平锁和非公平锁。
package cn.juwatech.threads;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class ReentrantLockExample {
private final Lock lock = new ReentrantLock();
private int count = 0;
public void increment() {
lock.lock();
try {
count++;
} finally {
lock.unlock();
}
}
public int getCount() {
lock.lock();
try {
return count;
} finally {
lock.unlock();
}
}
public static void main(String[] args) {
ReentrantLockExample example = new ReentrantLockExample();
Runnable task = () -> {
for (int i = 0; i < 1000; i++) {
example.increment();
}
};
Thread thread1 = new Thread(task);
Thread thread2 = new Thread(task);
thread1.start();
thread2.start();
try {
thread1.join();
thread2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Final count: " + example.getCount());
}
}
四、信号量 (Semaphore
)
Semaphore
是一种计数信号量,用于控制同时访问特定资源的线程数量。它可以用于实现资源池、限流等功能。
package cn.juwatech.threads;
import java.util.concurrent.Semaphore;
public class SemaphoreExample {
private final Semaphore semaphore = new Semaphore(3);
public void accessResource(int threadId) {
try {
semaphore.acquire();
System.out.println("Thread " + threadId + " is accessing resource...");
Thread.sleep(2000);
System.out.println("Thread " + threadId + " is releasing resource.");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
semaphore.release();
}
}
public static void main(String[] args) {
SemaphoreExample example = new SemaphoreExample();
for (int i = 1; i <= 10; i++) {
int threadId = i;
new Thread(() -> example.accessResource(threadId)).start();
}
}
}
五、读写锁 (ReadWriteLock
)
ReadWriteLock
是一种读写锁,用于区分读操作和写操作。它允许多个读操作并发进行,但写操作是互斥的。
package cn.juwatech.threads;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class ReadWriteLockExample {
private final ReadWriteLock rwLock = new ReentrantReadWriteLock();
private int count = 0;
public void increment() {
rwLock.writeLock().lock();
try {
count++;
} finally {
rwLock.writeLock().unlock();
}
}
public int getCount() {
rwLock.readLock().lock();
try {
return count;
} finally {
rwLock.readLock().unlock();
}
}
public static void main(String[] args) {
ReadWriteLockExample example = new ReadWriteLockExample();
Runnable writerTask = () -> {
for (int i = 0; i < 1000; i++) {
example.increment();
}
};
Runnable readerTask = () -> {
for (int i = 0; i < 1000; i++) {
System.out.println("Count: " + example.getCount());
}
};
Thread writerThread = new Thread(writerTask);
Thread readerThread1 = new Thread(readerTask);
Thread readerThread2 = new Thread(readerTask);
writerThread.start();
readerThread1.start();
readerThread2.start();
try {
writerThread.join();
readerThread1.join();
readerThread2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Final count: " + example.getCount());
}
}
六、线程同步的最佳实践
- 选择合适的同步机制:根据具体需求选择合适的同步机制,如
synchronized
、显式锁、信号量等。 - 减少锁的粒度:尽量减少锁的粒度,以提高并发性能。
- 避免死锁:注意避免死锁,尤其是在使用多个锁的情况下。
- 使用并发集合类:Java 提供了一些并发集合类,如
ConcurrentHashMap
、CopyOnWriteArrayList
等,可以简化线程同步。
通过以上内容,我们详细介绍了 Java 中的线程同步机制,包括基本概念、常用方法及其具体代码示例,并探讨了使用不同同步机制的最佳实践。掌握这些知识,能够帮助我们编写出更加高效和可靠的多线程程序。