锁是控制多个线程访问共享资源的一种同步机制。

  1. synchronized:可以将代码块或方法设置为同步。
  2. ReentrantLock:提供了比synchronized更广泛的锁操作函数。
  3. ReadWriteLock:允许多个线程同时读共享资源,但只允许一个线程写共享资源。如ReentrantReadWriteLock。
  4. StampedLock:提供了乐观读锁,可以取代ReadWriteLock。
  5. Semaphore:信号量,可以控制同时访问的线程个数。

不同锁的应用场景:

  1. synchronized:适用于加锁代码块比较小的情况。
  2. ReentrantLock:需要更灵活的锁获取与释放、可中断锁、公平锁等高级功能时使用。
  3. ReadWriteLock:读多写少的场景,允许同时多个线程读。
  4. StampedLock:读多写少,需要乐观读锁时使用。
  5. Semaphore:限流,需要控制最大运行线程数时使用。

悲观锁和乐观锁是两种不同的锁机制:

悲观锁:

  • 总是假设会发生冲突,因此在访问数据的时候都采取加锁的方式。这种锁机制下访问的数据都是互斥的。
  • 典型的悲观锁是通过 synchronized 或者 ReentrantLock 等来实现。
  • 访问时加锁,性能较差,但确保了数据访问的正确性。

乐观锁:

  • 假设不会发生冲突,只在提交操作时检查是否违反数据完整性。
  • 典型的乐观锁是通过版本号机制来实现。
  • 更新的时候不加锁,只是检查版本号,性能较好。但数据可能会被覆盖。
  • 适用于写比较少的场景下。

总结:

  • 悲观锁性能差但安全,乐观锁性能好但有冲突的风险。
  • 根据实际场景需要选择合适的锁机制。读多写少时建议使用乐观锁,写操作频繁时建议使用悲观锁。

使用如下:

synchronized

package com.demo1;

import lombok.NonNull;
import lombok.Synchronized;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.concurrent.locks.StampedLock;

public class Lock implements Runnable {
    private final ReentrantLock reentrantLock = new ReentrantLock();
    private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
    private final StampedLock stampedLock = new StampedLock();

    @Override
    public void run() {
        synchronized (Lock.class){
            for (int i = 1; i < 100; i++) {
                System.out.println(i);
                try {
                    Thread.sleep(10);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            try {
                Thread.sleep(3000);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        //构建线程池
        ThreadPoolExecutor pool = new ThreadPoolExecutor(2, 4, 2, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<>(2), new ThreadFactory() {
            @Override
            public Thread newThread(@NonNull Runnable r) {
                Thread t = new Thread(r);
                t.setName("myThread");
                return t;
            }
        }, new ThreadPoolExecutor.AbortPolicy());
        Lock l = new Lock();
        pool.submit(l);
        pool.submit(l);
        pool.shutdown();
    }
}
ReentrantLock:
@Override
public void run() {
    reentrantLock.lock();
        for (int i = 1; i < 100; i++) {
            System.out.println(i);
            try {
                Thread.sleep(10);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        try {
            Thread.sleep(3000);
        } catch (Exception e) {
            e.printStackTrace();
        }
        reentrantLock.unlock();
    }
ReadWriteLock:
@Override
public void run() {
    readWriteLock.readLock().lock();
        for (int i = 1; i < 100; i++) {
            System.out.println(i);
            try {
                Thread.sleep(10);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        try {
            Thread.sleep(3000);
        } catch (Exception e) {
            e.printStackTrace();
        }
    readWriteLock.readLock().unlock();
}
当使用读锁时,会出现如: 1 1 2 2 3 3这样交替的情况,但是写锁不会出现这种情况
StampedLock:
private final StampedLock lock = new StampedLock();

// 乐观读
public void optimisticRead() {
    long stamp = lock.tryOptimisticRead();
    // 执行读操作
    if(!lock.validate(stamp)){//当出现数据不一致问题时进行升级
        // 升级为悲观读锁
        stamp = lock.readLock();
        try {
            // 重做读取操作
        } finally {
            lock.unlockRead(stamp);
        }
    }
}

// 悲观写
public void pessimisticWrite() {
    long stamp = lock.writeLock();
    try {
        // 写操作
    } finally {
        lock.unlockWrite(stamp);
    }
}                   
Semaphore://只允许两个线程同时使用,类似c++的pv操作
private final Semaphore semaphore = new Semaphore(2);
@SneakyThrows
@Override
public void run() {
    semaphore.acquire();
    System.out.println("执行");
    Thread.sleep(3000);
    semaphore.release();
}
如果有什么错误,欢迎指正