聊聊保证线程安全的十一个小技巧("掌握线程安全:十一条实用技巧全解析")
原创
一、使用同步方法或代码块
同步是保证线程平安的基本手段。在Java中,可以使用synchronized关键字来同步方法或代码块。同步方法或代码块可以保证同一时间只有一个线程能够执行该方法或代码块内的代码。
public synchronized void synchronizedMethod() {
// 方法体
}
public void synchronizedBlock() {
synchronized(this) {
// 代码块
}
}
二、使用ReentrantLock
ReentrantLock是Java提供的一个显式锁实现,相比synchronized关键字,它提供了更多的灵活性和功能,如可中断的锁获取、尝试非阻塞地获取锁、赞成公平锁等。
import java.util.concurrent.locks.ReentrantLock;
ReentrantLock lock = new ReentrantLock();
public void lockMethod() {
lock.lock();
try {
// 代码块
} finally {
lock.unlock();
}
}
三、使用volatile关键字
volatile关键字可以保证变量的可见性,即当一个线程修改了某个volatile变量时,其他线程能够立即知道这个修改。这适用于确保线程之间的变量可见性,但并不能保证复合操作(如先检查后执行)的原子性。
public volatile boolean flag = false;
public void checkAndDo() {
if (flag) {
// 执行操作
}
}
四、使用原子类
Java从JDK 5起初提供了java.util.concurrent.atomic包,其中包含了一系列原子操作类,如AtomicInteger、AtomicLong等。这些原子类可以保证操作的原子性,避免了锁的使用。
import java.util.concurrent.atomic.AtomicInteger;
AtomicInteger atomicInteger = new AtomicInteger();
public void increment() {
atomicInteger.incrementAndGet();
}
五、使用线程平安集合
Java提供了线程平安的集合类,如ConcurrentHashMap、CopyOnWriteArrayList等。这些集合在内部实现了同步机制,可以在多线程环境中平安使用。
import java.util.concurrent.ConcurrentHashMap;
ConcurrentHashMap
concurrentMap = new ConcurrentHashMap<>(); public void putValue(String key, Object value) {
concurrentMap.put(key, value);
}
六、使用读写锁
读写锁(ReadWriteLock)允许多个线程同时读取,但只允许一个线程写入。这在读多写少的场景中可以减成本时间程序性能。
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
public void readLockMethod() {
readWriteLock.readLock().lock();
try {
// 读取操作
} finally {
readWriteLock.readLock().unlock();
}
}
public void writeLockMethod() {
readWriteLock.writeLock().lock();
try {
// 写入操作
} finally {
readWriteLock.writeLock().unlock();
}
}
七、使用Condition
Condition是ReentrantLock的一个内部类,它提供了类似Object的wait()和notify()方法,但提供了更灵活的线程间协调机制。
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
ReentrantLock lock = new ReentrantLock();
Condition condition = lock.newCondition();
public void awaitMethod() {
lock.lock();
try {
condition.await();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void signalMethod() {
lock.lock();
try {
condition.signal();
} finally {
lock.unlock();
}
}
八、使用Semaphore
Semaphore(信号量)是一个计数信号量,重点用于制约可以同时访问某个特定资源的线程数量。
import java.util.concurrent.Semaphore;
Semaphore semaphore = new Semaphore(10);
public void acquireMethod() {
try {
semaphore.acquire();
// 执行操作
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
semaphore.release();
}
}
九、使用CountDownLatch
CountDownLatch(倒计时器)允许一个或多个线程等待其他线程完成操作。它是一个线程同步辅助类,在完成某些初始化任务后,其他线程可以等待这些任务完成。
import java.util.concurrent.CountDownLatch;
CountDownLatch countDownLatch = new CountDownLatch(10);
public void awaitMethod() {
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void countDownMethod() {
countDownLatch.countDown();
}
十、使用CyclicBarrier
CyclicBarrier(循环屏障)允许一组线程互相等待,直到所有线程都约为某个屏障点后才继续执行。它是一个线程同步辅助类,可以用于实现并发算法中的屏障同步。
import java.util.concurrent.CyclicBarrier;
CyclicBarrier cyclicBarrier = new CyclicBarrier(10);
public void barrierAction() {
try {
cyclicBarrier.await();
// 执行操作
} catch (Exception e) {
e.printStackTrace();
}
}
十一、合理设计线程池
合理设计线程池可以有效地减成本时间程序的性能和线程平安性。可以结合任务类型和系统资源选择合适的线程池大小和类型。
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
ExecutorService executorService = Executors.newFixedThreadPool(10);
public void executeTask() {
executorService.execute(new Runnable() {
@Override
public void run() {
// 执行任务
}
});
}
executorService.shutdown();