单体应用多线程锁
使用synchronized关键字或ReentrantLock类来保证线程的顺序执行
个别场景使用JDK1.5之后提供的大量原子类AtomicInteger(基于CAS的乐观锁)
测试锁模拟并发可使用闭锁:CountDownLatch类对象模拟
ReentrantLock的公平锁和非公平锁机制
? ? ? ?ReentrantLock有两个构造方法,默认的构造方法中,sync = new NonfairSync(); 我们可以从字面意思看出它是一个非公平锁。再看看第二个构造方法,它需要传入一个参数,参数是一个布尔型,true 是公平锁,false 是非公平锁。从上面的源码我们可以看出sync 有两个实现类,分别是FairSync 和NonfairSync;
? 唯一的不同之处在于!hasQueuedPredecessors() 这个方法,很明显这个方法是一个队列,由此可以推断,公平锁是将所有的线程放在一个队列中,一个线程执行完成后,从队列中取出下一个线程,而非公平锁则没有这个队列。这些都是公平锁与非公平锁底层的实现原理。
//基于原子类(乐观锁)
public class Test {
private AtomicInteger i = new AtomicInteger(0);
public static void main(String[] args) {
Test test = new Test();
ExecutorService es = Executors.newFixedThreadPool(50);
CountDownLatch cdl = new CountDownLatch(5000);
for (int i = 0;i < 5000; i++){
es.execute(()->{
test.i.incrementAndGet();
cdl.countDown();
});
}
es.shutdown();
try {
cdl.await();
System.out.println("执行完成后,i="+test.i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//基于synchronized (悲观锁)
//使用CountDownLatch模拟并发
public class Test {
private int i=0;
public static void main(String[] args) {
Test test = new Test();
ExecutorService es = Executors.newFixedThreadPool(50);
CountDownLatch cdl = new CountDownLatch(5000);
for (int i = 0;i < 5000; i++){
es.execute(()->{
//修改部分 开始
synchronized (test){
test.i++;
}
//修改部分 结束
cdl.countDown();
});
}
es.shutdown();
try {
cdl.await();
System.out.println("执行完成后,i="+test.i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//基于ReentrantLock(悲观锁)
//使用CountDownLatch模拟并发
public class test {
private int i=0;
Lock lock = new ReentrantLock();
public static void main(String[] args) {
test test = new test();
//线程池:50个线程
ExecutorService es = Executors.newFixedThreadPool(50);
//闭锁
CountDownLatch cdl = new CountDownLatch(5000);
for (int i = 0;i < 5000; i++){
es.execute(()->{
test.lock.lock();
test.i++;
test.lock.unlock();
cdl.countDown();
});
}
es.shutdown();
try {
//等待5000个任务执行完成后,打印出执行结果
cdl.await();
System.out.println("执行完成后,i="+test.i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
分布式多线程锁
实现方式,一半基于第三方,如:数据库加锁,redis的单线程特性,zk,redisson分布式锁
1.基于mysql数据库sql特性? ?select.....for update? 行锁(排它锁),高并发不适用,数据库压力大
2.redis分布式锁,代码示例如下:
//公共类,锁工具
//AutoCloseable 自动关闭资源
@Slf4j
public class RedisLock implements AutoCloseable {
private RedisTemplate redisTemplate;
private String key;
private String value;
//单位:秒
private int expireTime;
public RedisLock(RedisTemplate redisTemplate,String key,int expireTime){
this.redisTemplate = redisTemplate;
this.key = key;
this.expireTime=expireTime;
this.value = UUID.randomUUID().toString();
}
/**
* 获取分布式锁
* @return
*/
public boolean getLock(){
RedisCallback<Boolean> redisCallback = connection -> {
//设置NX
RedisStringCommands.SetOption setOption = RedisStringCommands.SetOption.ifAbsent();
//设置过期时间
Expiration expiration = Expiration.seconds(expireTime);
//序列化key
byte[] redisKey = redisTemplate.getKeySerializer().serialize(key);
//序列化value
byte[] redisValue = redisTemplate.getValueSerializer().serialize(value);
//执行setnx操作
Boolean result = connection.set(redisKey, redisValue, expiration, setOption);
return result;
};
//获取分布式锁
Boolean lock = (Boolean)redisTemplate.execute(redisCallback);
return lock;
}
public boolean unLock() {
String script = "if redis.call(\"get\",KEYS[1]) == ARGV[1] then\n" +
" return redis.call(\"del\",KEYS[1])\n" +
"else\n" +
" return 0\n" +
"end";
RedisScript<Boolean> redisScript = RedisScript.of(script,Boolean.class);
List<String> keys = Arrays.asList(key);
Boolean result = (Boolean)redisTemplate.execute(redisScript, keys, value);
log.info("释放锁的结果:"+result);
return result;
}
@Override
public void close() throws Exception {
unLock();
}
}
//可起多应用多线程测试
//try(){}catch(){} 新特性
//AutoCloseable 自动关闭资源
@Service
@Slf4j
public class SchedulerService {
@Autowired
private RedisTemplate redisTemplate;
@Scheduled(cron = "0/5 * * * * ?")
public void sendSms(){
try(RedisLock redisLock = new RedisLock(redisTemplate,"autoSms",30)) {
if (redisLock.getLock()){
log.info("向138xxxxxxxx发送短信!");
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
Redisson分布式锁
//引入依赖jar包
//<dependency>
// <groupId>org.redisson</groupId>
// <artifactId>redisson</artifactId>
// <version>3.11.2</version>
//</dependency>
//<dependency>
// <groupId>org.redisson</groupId>
// <artifactId>redisson-spring-boot-starter</artifactId>
// <version>3.17.0</version>
//</dependency>
//简单一个请求方法示例
@RestController
@Slf4j
public class RedissonLockController {
@Autowired
private RedissonClient redisson;
@RequestMapping("redissonLock")
public String redissonLock() {
//获取锁
RLock rLock = redisson.getLock("order");
log.info("我进入了方法!!");
try {
//加锁,设置过期时间
rLock.lock(30, TimeUnit.SECONDS);
log.info("我获得了锁!!!");
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
log.info("我释放了锁!!");
rLock.unlock();
}
log.info("方法执行完成!!");
return "方法执行完成!!";
}
}
|