锁
感觉说到锁,就必须跟多线程,分布式扯上关系,锁的产生应该就是为了解决上面的业务场景的(个人理解)
单JVM,多线程使用synchronized,lock,数据库行及锁就能解决;
分布式,多JVM通过上述方案就满足不了,需要用到Zookeeper、redis 、Mongdb
所有技术的产生都是有业务或者时代去驱动的,不可能凭空臆造、假象
脱离业务场景谈技术就是耍流氓!!!
下面我们一个个细说,结合多线程来说:
上代码
package com.cloudwalk.shark.interview.thread; import java.util.concurrent.Executor; import java.util.concurrent.Executors; public class Demo1 { public Integer count = 0; public static void main(String[] args) { final Demo demo1 = new Demo(); Executor executor = Executors.newFixedThreadPool(10); for(int i=0;i<1000;i++){ executor.execute(new Runnable() { @Override public void run() { demo1.count++; } }); } try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("final count value:"+demo1.count); } }
返回结果并不是1000?,每次结果可能并不一样;
这就是比较常见的多线程导致的异常操作,下面我们通过锁来解决synchronized
package com.cloudwalk.shark.interview.thread; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executor; import java.util.concurrent.Executors; public class SynchronizedDemo { public volatile Integer count = 0; public static CountDownLatch countDownLatch = new CountDownLatch(1000); public static void main(String[] args) { final SynchronizedDemo demo = new SynchronizedDemo(); Executor executor = Executors.newFixedThreadPool(10); for(int i=0;i<1000;i++){ executor.execute(new Runnable() { public void run() { synchronized (demo){ demo.count++; countDownLatch.countDown(); } } }); } try { countDownLatch.await(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("final count value:"+demo.count); } }
synchronized我们在用的时候只要记得锁就行,不需要主动去释放,JVM会帮我们去实现,这个也是跟lock不一样的地方
下面这个是lock的锁去解决多线程带来的问题
package com.cloudwalk.shark.interview.thread; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executor; import java.util.concurrent.Executors; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class LockDemo { public volatile Integer count = 0; public static CountDownLatch countDownLatch = new CountDownLatch(1000); public static Lock lock = new ReentrantLock(); public static void main(String[] args) { final LockDemo demo = new LockDemo(); Executor executor = Executors.newFixedThreadPool(100); for(int i=0;i<1000;i++){ executor.execute(new Runnable() { public void run() { while(true) { if (lock.tryLock()) { try{ demo.count++; countDownLatch.countDown(); break; }catch(Exception e){ }finally { lock.unlock(); } } } } }); } try { countDownLatch.await(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("final count value:"+demo.count); } }
lock就需要我们自己去释放,为了防止异常的发生,都放在finally去做;
synchronized其实没什么好看的,就是java的一个关键字,简单暴力高效,这里就不比较两者之间的效率了,存在即合理,都有各自的主场;
下面带大家看看lock中的东东(这个后面我们再继续看):
继续带大家往下看 ,顺着大神的思路往下捋,看看数据库行及锁是怎么一回事:
package com.cloudwalk.shark.controller; import com.cloudwalk.shark.model.User; import com.cloudwalk.shark.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.MediaType; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; @Controller @RequestMapping(value = "/lock", produces = {MediaType.APPLICATION_JSON_UTF8_VALUE}) public class SharkDBLockController { @Autowired private UserService userService; @PostMapping("/db/{userName}") @ResponseBody public void updateUserScore(@PathVariable("userName") String userName) throws InterruptedException { User user = userService.findUserByName(userName); Thread.sleep(2000); user.setScore(user.getScore()+1); userService.updateUser(user); } }
初窥门径的程序员会认为事务最基本的ACID中便包含了原子性,但是事务的原子性和今天所讲的并发中的原子操作仅仅是名词上有点类似。
而有点经验的程序员都能知道这中间发生了什么,这只是暴露了项目中并发问题的冰山一角,千万不要认为上面的代码没有必要列举出来,
我在实际项目开发中,曾经见到有多年工作经验的程序员仍然写出了类似于上述会出现并发问题的代码。
Zookeeper
redis --Redisson
Mongdb
数据库行及锁
没有什么比每天有成长进步更高兴的事情