当前位置:首页 > 行业动态 > 正文

java中如何保证线程安全

使用synchronized关键字或Lock接口实现线程同步,避免多 线程同时访问共享资源。

在Java中,为了保证线程安全,我们可以采用多种方法,以下是一些常用的技术介绍:

1、synchronized关键字

synchronized关键字是Java中最基本的同步机制,它可以用来修饰方法或者以代码块的形式存在,当一个线程访问一个使用synchronized修饰的方法或代码块时,其他线程将会被阻塞,直到该线程执行完毕,这样可以确保同一时刻只有一个线程能够访问该方法或代码块,从而保证线程安全。

示例:

public class Counter {
    private int count = 0;
    public synchronized void increment() {
        count++;
    }
    public synchronized void decrement() {
        count--;
    }
    public synchronized int getCount() {
        return count;
    }
}

2、ReentrantLock类

ReentrantLock是Java并发包中的一个类,它实现了Lock接口,与synchronized关键字不同,ReentrantLock提供了更加灵活的锁操作,如可中断的获取锁、公平锁等,ReentrantLock需要手动加锁和解锁,因此在使用时需要注意避免死锁。

示例:

import java.util.concurrent.locks.ReentrantLock;
public class LockExample {
    private final ReentrantLock lock = new ReentrantLock();
    private int count = 0;
    public void increment() {
        lock.lock();
        try {
            count++;
        } finally {
            lock.unlock();
        }
    }
    public void decrement() {
        lock.lock();
        try {
            count--;
        } finally {
            lock.unlock();
        }
    }
    public int getCount() {
        lock.lock();
        try {
            return count;
        } finally {
            lock.unlock();
        }
    }
}

3、StampedLock类

StampedLock是Java并发包中的一个类,它提供了一种乐观读锁的机制,与ReentrantLock不同,StampedLock不需要手动加锁和解锁,而是通过乐观读锁和悲观写锁的方式实现线程安全,乐观读锁可以提高读取性能,但可能会导致写线程饥饿,在使用StampedLock时需要权衡读写性能和线程安全性。

示例:

import java.util.concurrent.locks.StampedLock;
import java.util.concurrent.atomic.AtomicInteger;
public class StampedLockExample {
    private final StampedLock lock = new StampedLock();
    private AtomicInteger count = new AtomicInteger(0);
    private long stamp = 0;
    public void increment() {
        long writeStamp = lock.writeLock(); // 获取悲观写锁
        try {
            if (stamp != writeStamp) { // 如果当前没有写锁,则升级为乐观读锁并重新获取悲观写锁
                stamp = writeStamp; // 更新乐观读锁的版本号为当前写锁的版本号
                if (stamp == writeStamp) { // 如果当前仍然有写锁,则升级为悲观读锁并重新获取悲观写锁(注意:这里不会出现死锁)
                    while (true) { // 循环等待写锁释放,直到成功获取悲观读锁为止(注意:这里不会出现死锁)
                        long readStamp = lock.tryOptimisticRead(); // 尝试获取乐观读锁(如果当前没有写锁,则直接返回;否则返回-1)
                        if (readStamp != writeStamp) { // 如果当前没有写锁,则升级为悲观读锁并重新获取悲观写锁(注意:这里不会出现死锁)
                            stamp = readStamp; // 更新乐观读锁的版本号为当前乐观读锁的版本号(注意:这里不会出现死锁)
                            break; // 跳出循环,成功获取悲观读锁(注意:这里不会出现死锁)
                        } else { // 如果当前有写锁,则继续等待(注意:这里不会出现死锁)
                            Thread.yield(); // 让出CPU时间片,等待其他线程执行(注意:这里不会出现死锁)
                        }
                    }
                } else { // 如果当前没有写锁,则直接升级为悲观读锁并重新获取悲观写锁(注意:这里不会出现死锁)
                }
            } else { // 如果当前已经有写锁,则直接升级为悲观读锁并重新获取悲观写锁(注意:这里不会出现死锁)
            }
            count.incrementAndGet(); // 原子性地增加计数值(注意:这里不会出现死锁)
        } finally { // 释放悲观写锁(注意:这里不会出现死锁)
            lock.unlockWrite(writeStamp); // 释放悲观写锁(注意:这里不会出现死锁)
        }
    }
}

4、Semaphore类和CountDownLatch类(信号量和倒计时门闩)

Semaphore类和CountDownLatch类都是Java并发包中的类,它们可以用来控制多个线程之间的同步,Semaphore类是一个计数信号量,可以用于限制同时访问某个资源的线程数量,CountDownLatch类是一个倒计时门闩,可以用于等待多个线程都完成某个任务后再继续执行,这两个类都可以保证线程安全,但它们的使用场景和方式有所不同。

0