首先还原一个线程不安全的事故现场:
运行结果:
明显是有问题的,相同的票售出了多次。
基于Oracle实现简单的分布式锁
创建一个仅有一个主键字段的表:
数据库实体类:
@Table(name = "oracle_lock")
@Setter
@Getter
@AllArgsConstructor
public class OracleLockEntity {
@Id
@Column(name = "id")
private String id;
}
Mapper接口:
/**
* @author Dongguabai
* @date 2018-07-20 11:01
*/
public interface OracleLockMapper extends Mapper<OracleLockEntity> {
}
Lock接口的方法太多,现在建一个中间实现类:
/**
* @author Dongguabai
* @date 2018-07-20 11:13
*/
public class LockDecorator implements Lock{
@Override
public void lock() {
}
@Override
public void lockInterruptibly() throws InterruptedException {
}
@Override
public boolean tryLock() {
return false;
}
@Override
public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
return false;
}
@Override
public void unlock() {
}
@Override
public Condition newCondition() {
return null;
}
}
编写数据库锁:
/**
* Oracle分布式锁
* 注意:1.性能较差
* 2.可能会出现死锁(比如解锁的时候数据库挂了)
* 3.代码复杂
* 4.不可重入
*
* @author Dongguabai
* @date 2018-07-20 10:56
*/
@Component("simpleOracleLock")
@Slf4j
public class SimpleOracleLock extends LockDecorator {
public static final String LOCK_ID = "1";
public static final OracleLockEntity LOCK_ORACLE_ENTITY = new OracleLockEntity(LOCK_ID);
@Autowired
private OracleLockMapper oracleLockMapper;
/**
* 非阻塞式加锁
*
* @return
*/
@Override
public boolean tryLock() {
try {
oracleLockMapper.insert(LOCK_ORACLE_ENTITY);
} catch (Exception e) {
log.info("尝试加锁失败!");
return false;
}
return true;
}
/**
* 解锁
*/
@Override
public void unlock() {
oracleLockMapper.deleteByPrimaryKey(LOCK_ID);
}
/**
* 阻塞式加锁
*/
@Override
public void lock() {
if (!tryLock()) {
int randomSleepMillis = new Random().nextInt(100);
try {
Thread.sleep(randomSleepMillis);
} catch (InterruptedException e) {
log.info("尝试加锁失败,线程sleep{}毫秒!", randomSleepMillis);
}
lock();
}
}
}
运行测试:
@Resource(name = "simpleOracleLock")
private Lock simpleOracleLock;
@Test
public void test22() throws InterruptedException {
RunnableImpl runnable = new RunnableImpl();
Thread t1 = new Thread(runnable, "售票窗口一");
Thread t2 = new Thread(runnable, "售票窗口二");
Thread t3 = new Thread(runnable, "售票窗口三");
Thread t4 = new Thread(runnable, "售票窗口四");
t1.start();
t2.start();
t3.start();
t4.start();
//主线程等待子线程执行完毕
Thread.currentThread().join();
}
class RunnableImpl implements Runnable {
@Override
public void run() {
while (sum > 0) {
simpleOracleLock.lock();
try {
System.out.println(Thread.currentThread().getName() + "现在卖了第" + (sum--) + "张票");
}catch (Exception e){
}finally {
simpleOracleLock.unlock();
}
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
运行结果:
符合要求,按照顺序票一个个卖出去了。