感觉说到锁,就必须跟多线程,分布式扯上关系,锁的产生应该就是为了解决上面的业务场景的(个人理解)

单 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 {

</span><span style="color: rgba(255, 102, 0, 1)">public Integer count = 0;

</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> main(String[] args) {
    </span><span style="color: rgba(0, 0, 255, 1)">final</span> Demo demo1 = <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> Demo();
    Executor executor </span>= Executors.newFixedThreadPool(10<span style="color: rgba(0, 0, 0, 1)">);
    </span><span style="color: rgba(0, 0, 255, 1)">for</span>(<span style="color: rgba(0, 0, 255, 1)">int</span> i=0;i&lt;1000;i++<span style="color: rgba(0, 0, 0, 1)">){
        executor.execute(</span><span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> Runnable() {
            @Override
            </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> run() {
                demo1.count</span>++<span style="color: rgba(0, 0, 0, 1)">;
            }
        });
    }
    </span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)"> {
        Thread.sleep(</span>5000<span style="color: rgba(0, 0, 0, 1)">);
    } </span><span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)"> (InterruptedException e) {
        e.printStackTrace();
    }

    System.out.println(</span>"final count value:"+<span style="color: rgba(0, 0, 0, 1)">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 {

</span><span style="color: rgba(0, 0, 255, 1)">public</span>  <span style="color: rgba(0, 0, 255, 1)">volatile</span>  Integer count = 0<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">static</span> CountDownLatch countDownLatch = <span style="color: rgba(0, 0, 255, 1)">new</span> CountDownLatch(1000<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> main(String[] args) {
    </span><span style="color: rgba(0, 0, 255, 1)">final</span> SynchronizedDemo demo = <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> SynchronizedDemo();
    Executor executor </span>= Executors.newFixedThreadPool(10<span style="color: rgba(0, 0, 0, 1)">);
    </span><span style="color: rgba(0, 0, 255, 1)">for</span>(<span style="color: rgba(0, 0, 255, 1)">int</span> i=0;i&lt;1000;i++<span style="color: rgba(0, 0, 0, 1)">){
        executor.execute(</span><span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> Runnable() {
            </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> run() {
                </span><span style="color: rgba(255, 102, 0, 1)">synchronized</span><span style="color: rgba(0, 0, 0, 1)"><span style="color: rgba(255, 102, 0, 1)"> (demo){</span>
                    demo.count</span>++<span style="color: rgba(0, 0, 0, 1)">;
                    countDownLatch.countDown();
                }
            }
        });
    }
    </span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)"> {
      <span style="color: rgba(255, 102, 0, 1)"> countDownLatch.await();</span>
    } </span><span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)"> (InterruptedException e) {
        e.printStackTrace();
    }

    <span style="color: rgba(255, 102, 0, 1)">System.out.println(</span></span><span style="color: rgba(255, 102, 0, 1)">"final count value:"+</span><span style="color: rgba(0, 0, 0, 1)"><span style="color: rgba(255, 102, 0, 1)">demo.count);</span>
}

}

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 {

</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">volatile</span> Integer count = 0<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">static</span>  CountDownLatch countDownLatch = <span style="color: rgba(0, 0, 255, 1)">new</span> CountDownLatch(1000<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">static</span> Lock lock =  <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> ReentrantLock();
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> main(String[] args) {
    </span><span style="color: rgba(0, 0, 255, 1)">final</span> LockDemo demo = <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> LockDemo();
    Executor executor </span>= Executors.newFixedThreadPool(100<span style="color: rgba(0, 0, 0, 1)">);
    </span><span style="color: rgba(0, 0, 255, 1)">for</span>(<span style="color: rgba(0, 0, 255, 1)">int</span> i=0;i&lt;1000;i++<span style="color: rgba(0, 0, 0, 1)">){
        executor.execute(</span><span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> Runnable() {
            </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> run() {
                </span><span style="color: rgba(0, 0, 255, 1)">while</span>(<span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">) {
                    </span><span style="color: rgba(255, 102, 0, 1)">if (lock.tryLock()) {
                        </span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)">{
                            demo.count</span>++<span style="color: rgba(0, 0, 0, 1)">;

countDownLatch.countDown();
break;
}
catch(Exception e){
}
finally {
lock.unlock();
}
}
}

            }
        });
    }
    </span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)"> {
        countDownLatch.await();
    } </span><span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)"> (InterruptedException e) {
        e.printStackTrace();
    }
    System.out.println(</span>"final count value:"+<span style="color: rgba(0, 0, 0, 1)">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

数据库行及锁