模拟多线程触发
package com.ws.controller; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; import javax.annotation.Resource; import java.util.concurrent.CountDownLatch; import java.util.concurrent.locks.Lock; @Api(value = "锁机制", description = "锁机制说明") @RestController public class LockController { private static long count = 20;//黄牛 private CountDownLatch countDownLatch = new CountDownLatch(5); @Resource(name="redisLock") private Lock lock; @ApiOperation(value="售票") @RequestMapping(value = "/sale", method = RequestMethod.GET) public Long sale() throws InterruptedException { count = 20; countDownLatch = new CountDownLatch(5); System.out.println("-------共20张票,分五个窗口开售-------"); new PlusThread().start(); new PlusThread().start(); new PlusThread().start(); new PlusThread().start(); new PlusThread().start(); return count; } // 线程类模拟一个窗口买火车票 public class PlusThread extends Thread { private int amount = 0;//抢多少张票 @Override public void run() { System.out.println(Thread.currentThread().getName() + "开始售票"); countDownLatch.countDown(); if (countDownLatch.getCount()==0){ System.out.println("----------售票结果------------------------------"); } try { countDownLatch.await(); } catch (InterruptedException e) { e.printStackTrace(); } while (count > 0) { lock.lock(); try { if (count > 0) { //模拟卖票业务处理 amount++; count--; } }finally{ lock.unlock(); } try { Thread.sleep(10); } catch (Exception e) { e.printStackTrace(); } } System.out.println(Thread.currentThread().getName() + "售出"+ (amount) + "张票"); } } }
redis锁实现
package com.ws.lock; import com.enjoy.utils.FileUtils; import org.springframework.data.redis.connection.jedis.JedisConnectionFactory; import org.springframework.stereotype.Service; import redis.clients.jedis.Jedis; import javax.annotation.Resource; import java.util.Arrays; import java.util.UUID; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; @Service public class RedisLock implements Lock { private static final String KEY = "LOCK_KEY"; @Resource private JedisConnectionFactory factory; private ThreadLocal<String> local = new ThreadLocal<>(); @Override //阻塞式的加锁 public void lock() { //1.尝试加锁 if(tryLock()){ return; } //2.加锁失败,当前任务休眠一段时间 try { Thread.sleep(10);//性能浪费 } catch (InterruptedException e) { e.printStackTrace(); } //3.递归调用,再次去抢锁 lock(); } @Override //阻塞式加锁,使用setNx命令返回OK的加锁成功,并生产随机值 public boolean tryLock() { //产生随机值,标识本次锁编号 String uuid = UUID.randomUUID().toString(); Jedis jedis = (Jedis) factory.getConnection().getNativeConnection(); /** * key:我们使用key来当锁 * uuid:唯一标识,这个锁是我加的,属于我 * NX:设入模式【SET_IF_NOT_EXIST】--仅当key不存在时,本语句的值才设入 * PX:给key加有效期 * 1000:有效时间为 1 秒 */ String ret = jedis.set(KEY, uuid,"NX","PX",1000); //设值成功--抢到了锁 if("OK".equals(ret)){ local.set(uuid);//抢锁成功,把锁标识号记录入本线程--- Threadlocal return true; } //key值里面有了,我的uuid未能设入进去,抢锁失败 return false; } //正确解锁方式 public void unlock() { //读取lua脚本 String script = FileUtils.getScript("unlock.lua"); //获取redis的原始连接 Jedis jedis = (Jedis) factory.getConnection().getNativeConnection(); //通过原始连接连接redis执行lua脚本 jedis.eval(script, Arrays.asList(KEY), Arrays.asList(local.get())); } //----------------------------------------------- @Override public Condition newCondition() { return null; } @Override public boolean tryLock(long time, TimeUnit unit) throws InterruptedException { return false; } @Override public void lockInterruptibly() throws InterruptedException { } }
文件读取工具类
package com.ws.utils; import java.io.BufferedReader; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStreamReader; public class FileUtils { //无成员变量 --- 无状态 public static String getScript(String fileName){ String path = FileUtils.class.getClassLoader().getResource(fileName).getPath(); return readFileByLines(path); } public static String readFileByLines(String fileName) { FileInputStream file = null; BufferedReader reader = null; InputStreamReader inputFileReader = null; String content = ""; String tempString = null; try { file = new FileInputStream(fileName); inputFileReader = new InputStreamReader(file, "utf-8"); reader = new BufferedReader(inputFileReader); // 一次读入一行,直到读入null为文件结束 while ((tempString = reader.readLine()) != null) { content += tempString; } reader.close(); } catch (IOException e) { e.printStackTrace(); return null; } finally { if (reader != null) { try { reader.close(); } catch (IOException e1) { } } } return content; } public static void main(String[] args) { String path = FileUtils.class.getClassLoader().getResource("unlock.lua").getPath(); String script = FileUtils.readFileByLines(path); System.out.println(script); } }
redis配置类
package com.ws.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.jedis.JedisConnectionFactory; import redis.clients.jedis.JedisPoolConfig; @Configuration public class RedisConfig { @Bean public JedisPoolConfig jedisPoolConfig() { JedisPoolConfig jedisPoolConfig = new JedisPoolConfig(); jedisPoolConfig.setMaxIdle(10); jedisPoolConfig.setMaxTotal(10000); return jedisPoolConfig; } @Bean public JedisConnectionFactory jedisConnectionFactory(JedisPoolConfig jedisPoolConfig) { JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory(); jedisConnectionFactory.setHostName("192.168.42.111"); jedisConnectionFactory.setPort(6379); jedisConnectionFactory.setPassword("12345678"); jedisConnectionFactory.setUsePool(true); jedisConnectionFactory.setPoolConfig(jedisPoolConfig); return jedisConnectionFactory; } }
lua脚本
if redis.call("get",KEYS[1]) == ARGV[1] then return redis.call("del",KEYS[1]) else return 0 end