標籤:排列 and thread str 測試 dex nts resource jedis
本篇文章是通過watch(監控)+mutil(事務)實現應用於在分布式高並發處理等相關情境。下邊先通過redis-cli.exe來測試多個線程修改時,遇到問題及解決問題。
高並發下修改同一個key遇到的問題:
1)定義一個hash類型的key,key為:lock_test,元素locker的值初始化為0。
2)實現高並發下對locker元素的值遞增:定義64個多線程,並發的對lock_test元素locker的值進行修改。
package com.dx.es;import java.util.concurrent.CountDownLatch;import redis.clients.jedis.Jedis;import redis.clients.jedis.JedisPool;public class Test_UnLock { public static void main(String[] args) { final JedisPool pool = RedisUtil.getPool(); // 獲得jedis對象 Jedis jedis = pool.getResource(); jedis.hset("lock_test", "locker", "0"); String val = jedis.hget("lock_test", "locker"); System.out.println("lock_test.locker的初始值為:" + val); jedis.close(); int threahSize = 64; final CountDownLatch threadsCountDownLatch = new CountDownLatch(threahSize); Runnable handler = new Runnable() { public void run() { Jedis jedis = pool.getResource(); Integer integer = Integer.valueOf(jedis.hget("lock_test", "locker")); jedis.hset("lock_test", "locker", String.valueOf(integer + 1)); jedis.close(); threadsCountDownLatch.countDown(); } }; for (int i = 0; i < threahSize; i++) { new Thread(handler).start(); } // 等待所有並行子線程任務完成。 try { threadsCountDownLatch.await(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("complete"); val = jedis.hget("lock_test", "locker"); System.out.println(val); }}
此時,會出現以下問題:
- A線程擷取key的值為0,而B線程也擷取jkey的值0,則A把key值遞增為1,B線程也實現把key值遞增為1。兩個線程都執行了key值修改:0到1。
- 在1)中最終key修改為了1,但是c線程擷取key的值為0(因為c線程讀取key值時,a、b線程還未觸發修改,因此c線程讀取到的值為0),此時d線程讀取到的值為1(因為d線程讀取key值時,a、b線程已觸發修改,一次d線程取到的值為1)。
- 此時假設d線程優先觸發遞增,則在c線程未觸發提交之前d線程已經把值修改了2,但是c此時並不知道在它擷取到值到修改之前這段時間發生了什麼,直接把值修改1。
此時執行列印結果為:
lock_test.locker的初始值為:0complete24 #備忘:也可能是其他值,可能是正確值64的可能性比較小。
通過watch(監控)+mutil(事務)解決上邊的問題:redis-cli.exe下的事務操作:
# 事務被成功執行redis 127.0.0.1:6379> MULTIOKredis 127.0.0.1:6379> INCR user_idQUEUEDredis 127.0.0.1:6379> INCR user_idQUEUEDredis 127.0.0.1:6379> INCR user_idQUEUEDredis 127.0.0.1:6379> PINGQUEUEDredis 127.0.0.1:6379> EXEC1) (integer) 12) (integer) 23) (integer) 34) PONG
並發情況下使用watch+mutil操作:
事務塊內所有命令的傳回值,按命令執行的先後順序排列。 當操作被打斷時,返回空值 nil 。
A線程:
# 監視 key ,且事務成功執行redis 127.0.0.1:6379> WATCH lock lock_timesOKredis 127.0.0.1:6379> MULTIOKredis 127.0.0.1:6379> SET lock "huangz"QUEUEDredis 127.0.0.1:6379> INCR lock_timesQUEUEDredis 127.0.0.1:6379> EXEC1) OK2) (integer) 1
B線程:
# 監視 key ,且事務被打斷redis 127.0.0.1:6379> WATCH lock lock_timesOKredis 127.0.0.1:6379> MULTIOKredis 127.0.0.1:6379> SET lock "joe" # 就在這時,另一個用戶端修改了 lock_times 的值QUEUEDredis 127.0.0.1:6379> INCR lock_timesQUEUEDredis 127.0.0.1:6379> EXEC # 因為 lock_times 被修改, joe 的事務執行失敗(nil)
上邊示範了A、B線程並發下的watch+mutil操作情況。
需要掌握Redis 事務命令:
Watch 命令用於監視一個(或多個) key ,如果在事務執行之前這個(或這些) key 被其他命令所改動,那麼事務將被打斷。可用版本:>= 2.2.0
| 序號 |
命令及描述 |
| 1 |
DISCARD 取消事務,放棄執行事務塊內的所有命令。 |
| 2 |
EXEC 執行所有事務塊內的命令。 |
| 3 |
MULTI 標記一個事務塊的開始。 |
| 4 |
UNWATCH 取消 WATCH 命令對所有 key 的監視。 |
| 5 |
WATCH key [key ...] 監視一個(或多個) key ,如果在事務執行之前這個(或這些) key 被其他命令所改動,那麼事務將被打斷。 |
Redis:多線程修改同一個Key使用watch+事務(mutil)實現樂觀鎖