文章目录
- 前言
- 一、场景描述
- 二、加锁
- 1.synchronized
- 2.ReentrantLock
- 三、扩展
- 1.ThreadLocal
- 总结
前言
在多线程场景下,多个线程同时对共享变量进行操作是存在风险的,这时候就需要加锁来保证数据的正确性。
一、场景描述
我这里有5个无人机,准备卖到乌克兰,奈何买家太多了,应酬不来,就挂到了网上,先到先得。
卖方
@Controller
public class StudentLockController {private static int number=5;public boolean get(){String name = Thread.currentThread().getName();if(number>0){try {//模拟业务处理时间Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println(name+"业务处理成功");number--;return true;}System.out.println(name+"业务处理失败");return false;}public int size(){return number;}
}
买方
@RunWith(SpringRunner.class)
@SpringBootTest(classes = SpringbootStart.class)
public class SpringbootStartTest {@Autowiredprivate StudentLockController studentLockController;@Testpublic void test(){normal();}public void normal(){int count=10;ExecutorService threadPool = Executors.newFixedThreadPool(count);Future<Boolean> submit = null;for(int i=0;i<count;i++){submit = threadPool.submit(() -> studentLockController.get());}try {submit.get();Thread.sleep(1500);} catch (InterruptedException e) {throw new RuntimeException(e);} catch (ExecutionException e) {throw new RuntimeException(e);}System.out.println("数量:"+studentLockController.size());threadPool.shutdown();}
}
来了10个买家,都抢上了,这库存就5个了肯定不够,这样不行,得重新抢,我加个锁让他们排队去
二、加锁
1.synchronized
public synchronized boolean get(){String name = Thread.currentThread().getName();if(number>0){try {//模拟业务处理时间Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println(name+"业务处理成功");number--;return true;}System.out.println(name+"业务处理失败");return false;}
给这个方法加个锁,但这个锁范围太大了,导致我库存没卖完,这不耽误挣钱了嘛,那哪行,继续改。
2.ReentrantLock
@Controller
public class StudentLockController {private static int number=5;private ReentrantLock rl=new ReentrantLock();public boolean get(){String name = Thread.currentThread().getName();while (!rl.tryLock()){try {//获取锁等待时间Thread.sleep(500);} catch (InterruptedException e) {throw new RuntimeException(e);}};try {if(number>0){//模拟业务处理时间Thread.sleep(1000);System.out.println(name+"业务处理成功");number--;return true;}} catch (Exception e) {throw new RuntimeException(e);}finally {if(rl.isLocked()){rl.unlock();}}System.out.println(name+"业务处理失败");return false;}public int size(){return number;}
}
嗯,东西都卖出去了,没货了就失败了,这个能挣大钱了,ReentrantLock这个好👍
三、扩展
每个买家购买物品之前需要登陆,一个买家账户会开启一个专门的线程来维护,买家有3次用户名、密码失败的次数,这就需要控制每个线程拥有单独的变量,ThreadLocal可以为每个线程开辟单独的工作空间。
1.ThreadLocal
private static ThreadLocal<Integer> threadLocal = new ThreadLocal<>();threadLocal.set(0);Integer count = threadLocal.get();if(count >=3){System.out.println("已经达到最大重试次数,请联系管理员进行重置!");}threadLocal.remove();
总结
回到顶部
加锁是为了保证多线程下数据的安全,但是锁过多和范围过大会影响程序性能,增加服务的处理时间,所以,使用锁要考虑当前的业务场景是否合适。