模擬多線程觸發java
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鎖實現web
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 { } }
文件讀取工具類redis
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配置類spring
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