1. 程式人生 > >使用Redis實現鎖機制

使用Redis實現鎖機制

最近看了一些關於鎖的文章,下面記錄一下自己的理解

0x0 原理

鎖是為了解決多執行緒併發修改或訪問同一個資源而設計的。鎖的種類有許多種,以我當前所瞭解的鎖,除了樂觀鎖外,其他鎖的原理大同小異:
即在各個執行緒都能訪問的地方設定一個標記來記錄某個資源的訪問許可權。
例如,存在某個資源Target,有A,B,C三個執行緒去修改Target的值,我們可以設定一個變數

boolean isAccessible = true;

當A執行緒訪問Target時,先要去看isAccessible是否為true,如果為true,則獲取訪問權,同時將isAccessible設定為false;
此時如果B執行緒也去訪問Target,則發現isAccessible為false,因而獲取不到訪問許可權,那麼B執行緒可以while迴圈判斷isAccessible直到該變數為true時獲取訪問權;
當一個執行緒訪問完後,將標記isAccessible設為true。
值得注意的是,要保證以上過程正確執行必須滿足兩個條件:
第一、isAccessible必須保證各個執行緒都可見


第二、作為標記的變數的判斷和修改需要是原子操作!
首先,可以使用volatile關鍵字保證其多執行緒可見性,其次,可以使用java中原子物件例如AtomicBoolean物件及其compareAndSet方法來保證原子操作。
但是,以上例子中,鎖的實現是在單程序下的;如果環境是多程序甚至是分散式,如何模擬鎖的實現呢?
可以使用第三方快取來存放各個程序都能訪問的標記,而redis及其setnx方法恰恰滿足以上作為鎖的兩大條件,redis可以保證各個執行緒都能訪問,同時setnx (key, value)方法是指:如果這個key存在,返回0;如果key不存在,插入key-value鍵值對,並返回1。

0x1 例子

先寫一個小demo:

redis鎖工具類

package com.fly.lock;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

public class RedisLock {

    //初始化redis池
    private static JedisPoolConfig config;
    private static JedisPool pool;
    static
{ config = new JedisPoolConfig(); config.setMaxTotal(30); config.setMaxIdle(10); pool = new JedisPool(config, "192.168.233.200", 6379); } /** * 給target上鎖 * @param target */ public static void lock(Object target) { //獲取jedis Jedis jedis = pool.getResource(); //result接收setnx的返回值,初始值為0 Long result= 0L; while (result < 1) { //如果target在redis中已經存在,則返回0;否則,在redis中設定target鍵值對,並返回1 result = jedis.setnx(target.getClass().getName() + target.hashCode(), Thread.currentThread().getName()); } jedis.close(); } /** * 給target解鎖 * @param target */ public static void unLock(Object target) { Jedis jedis = pool.getResource(); //刪除redis中target物件的鍵值對 Long del = jedis.del(target.getClass().getName() + target.hashCode()); jedis.close(); } /** * 嘗試給target上鎖,如果鎖成功返回true,如果鎖失敗返回false * @param target * @return */ public static boolean tryLock(Object target) { Jedis jedis = pool.getResource(); Long row = jedis.setnx(target.getClass().getName() + target.hashCode(), "true"); jedis.close(); if (row > 0) { return true; } return false; } }

測試類

package com.fly.test;

import com.fly.lock.RedisLock;

class Task {

    public void doTask() {
        //上鎖
        RedisLock.lock(this);

        System.out.println("當前執行緒: " + Thread.currentThread().getName());
        System.out.println("開始執行: " + this.hashCode());

        try {
            System.out.println("doing...");
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("完成: " + this.hashCode());

        //解鎖
        RedisLock.unLock(this);
    }

}

public class Demo {

    public static void main(String[] args) {
        Task task = new Task();

        Thread[] threads = new Thread[5];
        for (Thread thread : threads) {
            thread = new Thread(()->{
                task.doTask();
            });
            thread.start();
        }

    }

}

輸出結果:

----------------------------------------------
當前執行緒: Thread-0
開始執行: 2081499965
doing...
完成: 2081499965
----------------------------------------------
當前執行緒: Thread-2
開始執行: 2081499965
doing...
完成: 2081499965
----------------------------------------------
當前執行緒: Thread-1
開始執行: 2081499965
doing...
完成: 2081499965
----------------------------------------------
當前執行緒: Thread-4
開始執行: 2081499965
doing...
完成: 2081499965
----------------------------------------------
當前執行緒: Thread-3
開始執行: 2081499965
doing...
完成: 2081499965

去掉redis鎖後,執行結果:

----------------------------------------------
----------------------------------------------
當前執行緒: Thread-2
開始執行: 1926683415
----------------------------------------------
當前執行緒: Thread-1
doing...
當前執行緒: Thread-0
----------------------------------------------
當前執行緒: Thread-3
開始執行: 1926683415
doing...
開始執行: 1926683415
doing...
----------------------------------------------
開始執行: 1926683415
doing...
當前執行緒: Thread-4
開始執行: 1926683415
doing...
完成: 1926683415
完成: 1926683415
完成: 1926683415
完成: 1926683415
完成: 1926683415

Process finished with exit code 0

利用redis這個性質,可以實現分散式鎖,當然設計一定複雜一些!
redis官方似乎提供了一個redlock,有時間去研究一下。

相關推薦

使用Redis實現機制

最近看了一些關於鎖的文章,下面記錄一下自己的理解 0x0 原理 鎖是為了解決多執行緒併發修改或訪問同一個資源而設計的。鎖的種類有許多種,以我當前所瞭解的鎖,除了樂觀鎖外,其他鎖的原理大同小異: 即在各個執行緒都能訪問的地方設定一個標記來記錄某個資源的訪問許

跟濤哥一起學嵌入式第11集:一個實現機制非常有意思的巨集

QQ群(宅學部落)有位學員問了一個很奇怪的巨集,覺得很有意思,特拿來分享,它的定義如下: 我們知道,巨集定義其實就是為了方便,給一串程式碼字串定義一個別名。有時候字串過於複雜,我們可以分多行書寫,然後使用邏輯連線符“\”連線起來,表示一個完整的字串。但是分析上面的巨集定義,你會發現它分別定義了2個巨集,

跟濤哥一起學嵌入式第11集:一個實現機制非常有意思的宏

gcc sha ado 鎖機制 http 機制 嵌入式 main.c 錯誤 QQ群(宅學部落)有位學員問了一個很奇怪的宏,覺得很有意思,特拿來分享,它的定義如下: 我們知道,宏定義其實就是為了方便,給一串代碼字符串定義一個別名。有時候字符串過於復雜,我們可以分多行書寫,然

springboot+redis實現token機制

專案結構 pom.xml <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-b

SpringBoot + Redis 實現快取機制

SpringBoot + Mybtis +Redis 快取使用 往期回顧: 上一節簡單的介紹了SpringBoot + Properties 實現分散式服務動態配置內外部檔案, application.properties配置檔案欲被 SpringBoot自動載

Redis學習筆記~Redis併發機制

回到目錄 redis客戶端驅動有很多,如ServiceStack.Redis,StackExchange.Redis等等,下面我使用ServiceStack.Redis為例,介紹一下在redis驅動中實現併發鎖的方式,併發就是多執行緒同時訪問和操作同一個資源,而對於redis來說,如果你多個執行緒共同修改一

35. Spring Boot整合Redis實現快取機制【從零開始學Spring Boot】

【視訊&交流平臺】 http://study.163.com/course/introduction.htm?courseId=1004329008&utm_campaign=commission&utm_source=40000000

Spring Boot整合Redis實現快取機制

本文章牽涉到的技術點比較多: 、Redis、Spring MVC,Spirng Cache,所以在看這篇文章的時候,需要對以上這些技術點有一定的瞭解或者也可以先看看這篇文章,針對文章中實際的技術點在進一步瞭解(注意,您需要自己下載Redis Server到您的本地,

通過Expire實現Redis機制

在我們的業務系統中,可能因為某種原因,使用者會不斷的點選表單提交按鈕,為了較少對系統的 無意義訪問、暴力提交 以及使用者體驗,我們利用redis的過期時間簡單的實現了一種Redis鎖 當用戶提交表單的時候,我們通過對錶單資料以及使用者唯一標識例如userNo進行加密,當做我們快取的唯

Redis實現分散式機制

Redis實現分散式鎖思路   常用的是redis函式是setnx(),這個應該是實現分散式鎖最主要的函式。首先是將某一業務標識名作為鍵存到redis裡,併為其設個過期時間,如果是還有加鎖請求過來,先是通過setnx()看看是否能將鎖的標識插入到redis裡,可

Redis實現分散式環境下的分散式機制

redis是一個key-value儲存系統。和Memcached類似,它支援儲存的value型別相對更多,包括string(字串)、list、set(集合)、zset(sorted set --有序集合)和hash(雜湊型別)。都支援push/pop、add/

關於redis中使用機制,( 實現分散式和任務佇列)

場景:       電商網站上有很多秒殺活動,會迎來一個使用者請求的高峰期,可能會有幾十萬幾百萬的併發量,來搶這個手機,在高併發的情形下會對資料庫伺服器或者是檔案伺服器應用伺服器造成巨大的壓力,嚴重時說不定就宕機了;       另一個問題是,秒殺的東西都是有量的,一款手

C#通過Redis實現分布式

rom img com 分布式鎖 ase 即使 lock sta ons Redis有三個最基本屬性來保證分布式鎖的有效實現: 安全性: 互斥,在任何時候,只有一個客戶端能持有鎖。 活躍性A:沒有死鎖,即使客戶端在持有鎖的時候崩潰,最後也會有其他客戶端能獲得鎖,超時機制。

分布式實現(一)——基於Redis實現

場景 網站 con 空閑 fun tac random uid set 原文:http://www.cnblogs.com/liuyang0/p/6744076.html 概述 目前幾乎很多大型網站及應用都是分布式部署的,分布式場景中的數據一致性問題一直是一個比

redis】基於redis實現分布式並發

val 內容 等待隊列 過多 具體實現 exec ret abs con 基於redis實現分布式並發鎖(註解實現) 說明   前提, 應用服務是分布式或多服務, 而這些"多"有共同的"redis";   GitHub: https:

Redis Sentinel實現機制與原理詳解

過程 正則 發送 進行 還需 生產環境 根據 stat 時間 原文:Redis Sentinel實現的機制與原理詳解序言 Redis-Sentinel是Redis官方推薦的高可用性(HA)解決方案。實際上這意味著你可以使用Sentinel模式創建一個可以不用人為幹預而應對

基於Redis實現分布式

cti ces 實現 並不是 可能 rand urn 請求 lease 1.setnx鎖在redis中最簡單的數據結構就是string。最早的時候,上鎖的操作一般使用setnx,這個命令是當:lock不存在的時候set一個val,或許你還會記得使用expire來增加鎖的過期

Redis實現分布式原理與實現分析

數據表 防止 中一 csdn 訂單 not 產生 www 整體 一、關於分布式鎖 關於分布式鎖,可能絕大部分人都會或多或少涉及到。 我舉二個例子: 場景一:從前端界面發起一筆支付請求,如果前端沒有做防重處理,那麽可能在某一個時刻會有二筆一樣的單子同時到達系統後臺。 場

redis實現悲觀(後端語言以php為例)

號碼 blank mys 時間 先來 ng-click print -m 兩種 1479 鎖機制 通常使用的鎖分為樂觀鎖,悲觀鎖這兩種,簡單介紹下這兩種鎖,作為本文的背景知識,對這類知識已經有足夠了解的同學可以跳過這部分。 樂觀鎖 先來看下百度百科上的解釋

redis實現分布式

mman blog protocol unix eply pic topic 算法 超時 SETNX key value 將key設置值為value,如果key不存在,這種情況下等同SET命令。 當key存在時,什麽也不做。SETNX是”SET if Not eXists”