一、悲观锁的实现

悲观锁是一种保守的锁策略,它假设在任何时候都会发生并发冲突,因此在访问共享资源之前先锁住它,确保其他线程无法修改它,直到当前线程完成操作。在Java中,可以使用synchronized关键字实现悲观锁。

1. 使用synchronized关键字

在Java中,synchronized关键字可以用来实现悲观锁。它可以修饰方法或代码块,确保同一时间只有一个线程可以执行被修饰的方法或代码块。

```java
public synchronized void synchronizedMethod() {
// 业务逻辑
}
```

上面的代码示例中,synchronized关键字修饰了一个方法,表示该方法在执行时会获取对象的锁,其他线程需要等待该线程执行完毕才能获得锁并执行。

2. 使用ReentrantLock

除了synchronized关键字,还可以使用Java中的Lock接口及其实现类ReentrantLock来实现悲观锁。

```java
Lock lock = new ReentrantLock();

public void lockMethod() {
lock.lock();
try {
// 业务逻辑
} finally {
lock.unlock();
}
}
```

上面的代码示例中,使用ReentrantLock的lock方法获取锁,使用unlock方法释放锁。

3. 悲观锁的应用场景

悲观锁适用于冲突频繁的场景,当并发冲突较多时,使用悲观锁可以有效减少冲突,保证数据的一致性。悲观锁的性能一般较差,适用于读多写少的场景。

二、乐观锁的实现

乐观锁是一种乐观的锁策略,它认为在大部分情况下并发冲突是很少发生的,因此在访问共享资源之前不会加锁,而是在更新时检查数据的版本号或时间戳,如果发现数据已被其他线程修改,则放弃本次操作。在Java中,可以使用版本号或时间戳实现乐观锁。

1. 使用版本号实现乐观锁

使用版本号实现乐观锁的方式是,在数据模型中增加一个版本号字段,并在更新时将版本号加1。在读取数据时,将版本号一同读取,并在更新时比对版本号是否相等,如果不相等说明数据已被其他线程修改,则放弃本次操作。

```java
public class OptimisticLock {
private int version;
private int data;

public synchronized void updateData(int newData) {
if (this.version == getVersion()) {
this.data = newData;
this.version++;
} else {
// 数据已被其他线程修改,放弃操作
}
}

public int getVersion() {
return version;
}

public int getData() {
return data;
}
}
```

上面的代码示例中,使用一个version字段存储版本号,当更新数据时,先比对version是否一致,如果一致则更新数据并增加version,否则放弃操作。

2. 使用时间戳实现乐观锁

使用时间戳实现乐观锁的方式是,在数据模型中增加一个时间戳字段,并在更新时更新时间戳。在读取数据时,将时间戳一同读取,并在更新时比对时间戳是否一致,如果不一致说明数据已被其他线程修改,则放弃本次操作。

```java
public class OptimisticLock {
private long timestamp;
private int data;

public synchronized void updateData(int newData) {
if (this.timestamp == getTimestamp()) {
this.data = newData;
this.timestamp = System.currentTimeMillis();
} else {
// 数据已被其他线程修改,放弃操作
}
}

public long getTimestamp() {
return timestamp;
}

public int getData() {
return data;
}
}
```

上面的代码示例中,使用一个timestamp字段存储时间戳,当更新数据时,先比对timestamp是否一致,如果一致则更新数据并更新timestamp,否则放弃操作。

3. 乐观锁的应用场景

乐观锁适用于冲突较少的场景,当并发冲突少且冲突概率低时,使用乐观锁可以减少锁的竞争,提高系统的并发性能。乐观锁的性能一般较好,适用于读多写多的场景。乐观锁一般适用于数据更新频率较低的情况下。