Redis實現分布式鎖

來源:互聯網
上載者:User

標籤:delete   bytes   keyword   exec   sleep   view   thold   ati   實現   

Redis實現分布式鎖

在叢集等多伺服器中經常要使用到同步處理一下業務,這時普通的事務是滿足不要業務需求,需要分布式鎖。分布式鎖的實現方式有多種,如redis實現分布式鎖,zookeeper實現分布式鎖等,這篇先實現redis分布式鎖。

實現原理

1、通過setnx(lock_timeout)實現,如果設定了鎖返回1,已經有值沒有設定成功返回0。

2、死結問題:通過時間來判斷是否到期,如果已經到期,擷取到到期時間get(lockKey),然後getset(lock_timeout)判斷是否和get相同,相同則證明已經加鎖成功,因為可能會導致多個線程同時執行getset(lock_timeout)方法。這是可能導致多個線程都只需getset後,對於判斷加鎖成功的線程,再加expire(lockKey, LOCK_TIMEOUT, TimeUnit.MILLISECONDS)到期時間,防止多個線程同時疊加時間,導致鎖時效時間翻倍。

3、針對叢集伺服器時間不一致問題,可以從調用redis的time()擷取目前時間。

代碼實現分布式鎖介面

DistributionLock.java

/** * 建立時間:2016年12月8日 下午6:51:51 *  * 分布式鎖 *  * @author andy * @version 2.2 */public interface DistributionLock {    //加鎖成功 返回加鎖時間    public Long lock(String lockKey, String threadname);    //解鎖 需要更加加鎖時間判斷是否有許可權    public void unlock(String lockKey, long lockvalue, String threadname);}
分布式鎖Redis實現

RedisDistributionLock.java

import java.io.Serializable;import java.util.concurrent.TimeUnit;import org.apache.log4j.Logger;import org.springframework.dao.DataAccessException;import org.springframework.data.redis.connection.RedisConnection;import org.springframework.data.redis.core.RedisCallback;import org.springframework.data.redis.core.RedisTemplate;import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer;import org.andy.utils.SpringContextHolder;/** * 建立時間:2016年12月8日 下午5:44:16 *  * redis分布式鎖 *  * @author andy * @version 2.2 */public class RedisDistributionLock implements DistributionLock{    private static final long LOCK_TIMEOUT = 60 * 1000; //加鎖逾時時間 單位毫秒  意味著加鎖期間內執行完操作 如果未完成會有並發現象    private static final Logger LOG = Logger.getLogger(RedisDistributionLock.class); //redis鎖日誌    @SuppressWarnings("unchecked")    private static RedisTemplate<Serializable, Serializable> redisTemplate = (RedisTemplate<Serializable, Serializable>) SpringContextHolder            .getBean("redisTemplate");    /**     * 取到鎖加鎖 取不到鎖一直等待直到獲得鎖     */    @Override    public synchronized Long lock(String lockKey, String threadname) {        LOG.info(threadname + "開始執行加鎖");        while (true) { //迴圈擷取鎖            Long lock_timeout = System.currentTimeMillis() + LOCK_TIMEOUT + 1; //鎖時間            if (redisTemplate.execute(new RedisCallback<Boolean>() {                @Override                public Boolean doInRedis(RedisConnection connection) throws DataAccessException {                    JdkSerializationRedisSerializer jdkSerializer = new JdkSerializationRedisSerializer();                    byte[] value = jdkSerializer.serialize(lock_timeout);                    return connection.setNX(lockKey.getBytes(), value);                }            })) { //如果加鎖成功                LOG.info(threadname + "加鎖成功++++++++111111111");                redisTemplate.expire(lockKey, LOCK_TIMEOUT, TimeUnit.MILLISECONDS); //設定逾時時間,釋放記憶體                return lock_timeout;            }else {                Long currt_lock_timeout_Str = (Long) redisTemplate.opsForValue().get(lockKey); // redis裡的時間                if (currt_lock_timeout_Str != null && currt_lock_timeout_Str < System.currentTimeMillis()) { //鎖已經失效                    // 判斷是否為空白,不為空白的情況下,說明已經失效,如果被其他線程設定了值,則第二個條件判斷是無法執行                    Long old_lock_timeout_Str = (Long) redisTemplate.opsForValue().getAndSet(lockKey, lock_timeout);                    // 擷取上一個鎖到期時間,並設定現在的鎖到期時間                    if (old_lock_timeout_Str != null && old_lock_timeout_Str.equals(currt_lock_timeout_Str)) {                        // 如過這個時候,多個線程恰好都到了這裡,但是只有一個線程的設定值和當前值相同,他才有權利擷取鎖                        LOG.info(threadname + "加鎖成功+++++++2222222222");                        redisTemplate.expire(lockKey, LOCK_TIMEOUT, TimeUnit.MILLISECONDS); //設定逾時時間,釋放記憶體                        return lock_timeout;//返回加鎖時間                    }                }            }            try {                LOG.info(threadname +  "等待加鎖,睡眠100毫秒");                 TimeUnit.MILLISECONDS.sleep(100);//睡眠100毫秒            } catch (InterruptedException e) {                e.printStackTrace();            }         }    }    @Override    public synchronized void unlock(String lockKey, long lockvalue, String threadname) {        LOG.info(threadname + "執行解鎖==========");//正常直接刪除 如果異常關閉判斷加鎖會判斷到期時間        Long currt_lock_timeout_Str = (Long) redisTemplate.opsForValue().get(lockKey); // redis裡的時間        if (currt_lock_timeout_Str != null && currt_lock_timeout_Str == lockvalue) {//如果是加鎖者 則刪除鎖 如果不是則等待自動到期 重新競爭加鎖            redisTemplate.delete(lockKey); //刪除鍵            LOG.info(threadname + "解鎖成功-----------------");        }    }}

註:上面介面中參數threadname只是為了測試多線程資料列印,產生環境可以去掉介面DistributionLock和實作類別RedisDistributionLock中的String threadname參數。

多伺服器採用擷取redis時間,代替上面使用到的所有目前時間System.currentTimeMillis()。

public long currtTimeFromRedis(){ //擷取redis目前時間        return redisTemplate.execute(new RedisCallback<Long>() {            @Override            public Long doInRedis(RedisConnection connection) throws DataAccessException {                return connection.time();            }        });    }
類比測試測試代碼

類比20個線程同時執行業務,擷取資源。

@Test    public void testRedisDistributionLock(){        for (int i = 0; i < 20; i++) {            new Thread(new Runnable() {                @Override                public void run() {                    task(Thread.currentThread().getName());                }            }).start();        }        try {            TimeUnit.MINUTES.sleep(1);        } catch (InterruptedException e) {            e.printStackTrace();        }    }    private void task(String name){        DistributionLock lock = new RedisDistributionLock();         Long locktime; //加鎖時間        if ((locktime = lock.lock(RedisKeyUtil.DISTRIBUTED_LOCK_NO + 1, name)) != null) {            //開始執行任務            System.out.println(name + "任務執行中");            //任務執行完畢 關閉鎖            lock.unlock(RedisKeyUtil.DISTRIBUTED_LOCK_NO + 1, locktime, name);        }    }
測試結果

加鎖後,保證只有一個線程擷取當前資源,釋放鎖後釋放資源。

有時間寫一下zookeeper實現分布式鎖

Redis實現分布式鎖

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.