jedis解决高并发的一些学习

1、高并发带来的问题就是  {公共资源 } 的读写不准确

2、解决高并发的几种场景:

  场景一)  同一个JVM进程(jee中就是同一个tomcat)中,公共资源在同一块内存中,使用synchronized关键字给代码块或是方法加锁,使得同一个代码块不会被同时调用;成员变量的数据类型尽量使用JUC中的atomic*等原子类,其中的方法都是原子操作,但是性能会有所降低;对于非原子类成员变量修饰符可以使用volatile(强制使用主存变量)。

    说明:JUC在此场景中的使用非常广泛,主要是CAS操作,而且多线程的公共资源都是在主存中进行读取,不会在寄存器中做修改;但是,会有一个ABA问题(比如说一个线程one从内存位置V中取出A,这时候另一个线程two也从内存中取出A,并且two进行了一些操作变成了B,然         后two又将V位置的数据变成A,这时候线程one进行CAS操作发现内存中仍然是A,然后one操作成功)

  场景二)  非同一个JVM进程(集群或分布式),公共资源在第三方缓存中(如redis),JUC的CAS操作会失效,这个时候想实现公共资源的正确调用只能依赖第三方的同步锁机制(如redis的WATCH监控和事务MULTI-EXEC的配合使用)

3、实际应用:

  (1)jedis解决抢购的问题:

package com.thinkgem.jeesite;

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

import java.util.List;
import java.util.UUID;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * Created by Administrator on 2017/9/29.
 */
public class JedisMainTest {
    static final JedisPool pool = new JedisPool(new JedisPoolConfig() {{
        setMaxIdle(1000);
        setMaxTotal(1000);
        setTestOnBorrow(true);
    }}, "127.0.0.1", 6379);

    static final  String watchkeys = "watchKeys";// 监视keys
    static final String goodsStore = "goodsStore";//库存的key
    static final int stores = 10;//库存量

    public static void main(String[] args) throws InterruptedException {




        ExecutorService executor = Executors.newFixedThreadPool(1000);
        ExecutorService executor1 = Executors.newFixedThreadPool(1000);
        Jedis jedis = pool.getResource();
        jedis.set(watchkeys, "0");// 重置watchkeys为0
        jedis.set(goodsStore,""+stores);
        jedis.del("setsucc", "setfail");// 清空抢成功的,与没有成功的
        jedis.close();

        for (int i = 0; i < 10000; i++) {// 测试一万人同时访问
            executor.execute(new Runnable() {
                @Override
                public void run() {
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    for (int c = 0; c < 10; c++) {

                        executor1.execute(new MyRunnable());
                    }
                }
            });
//            Thread.sleep(50);
        }
        executor.shutdown();
    }


    static class MyRunnable implements Runnable {
/*TODO:未解决问题,一开始有两个以上的线程同时执行,会出现库存有剩余,但是同时执行的程序抢不到库存*/
        Jedis jedis = pool.getResource();

        public MyRunnable() {
        }

        @Override
        public void run() {
            try {

                jedis.watch(watchkeys);// 开始监视锁

                String val = jedis.get(goodsStore);
                int valint = Integer.valueOf(val);
                String userifo = UUID.randomUUID().toString();
                if (valint >0) {
                    Transaction tx = jedis.multi();// 开启事务

                    tx.decr(goodsStore);//库存量 -1
                    tx.incr(watchkeys);//修改监控变量

                    List<Object> list = tx.exec();// 提交事务,如果此时watchkeys被改动了,则返回null

                    //说明通过验证了,即库存还有剩余
                    if (list != null) {
                        System.out.println("用户:" + userifo + "抢购成功,当前抢购成功人数:"
                                + (stores-valint+1));
                    /* 抢购成功业务逻辑 */
                        jedis.sadd("setsucc", userifo);
                        Thread.sleep(900);//模拟业务执行时间
                    } else {
                        System.out.println("用户:" + userifo + "抢购失败,其他线程干扰");
                    /* 抢购失败业务逻辑 */
                        jedis.sadd("setfail", userifo);
                        Thread.sleep(400);//模拟业务执行时间
                    }

                } else {
                    System.out.println("用户:" + userifo + "抢购失败,库存以空");
                    jedis.sadd("setfail", userifo);
                    Thread.sleep(400);//模拟业务执行时间
                    // Thread.sleep(500);
                    return;
                }

            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                jedis.close();
            }

        }

    }
}

   (2)jedis解决抢购的问题2:(解决了(1)的问题,并优于其性能)

package com.thinkgem.jeesite;

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

import java.util.UUID;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * @DESC redis的list作为天然的库存队列。
 * 利用这一点,可以做一个list作为库存的公共资源,每次取值成功,就证明抢到商品了,否则,抢购失败
 * @Contact businessfgl@163.com
 */
public class JedisMainTest2 {
    static final JedisPool pool = new JedisPool(new JedisPoolConfig() {{
        setMaxIdle(1000);
        setMaxTotal(1000);
        setTestOnBorrow(true);
    }}, "127.0.0.1", 6379);

    static final int stores = 1000;//库存量
    static final String storesName = "stores";//抢购商品存放仓库名称
    static final String storesCountKey = "storesCountKey";//记录以往仓库中存放商品的数量的KEY
    static final String setSuccessName = "setSuccessName";//记录抢购成功用户的set集合KEY
    static final String setFailName = "setFailName";//记录抢购失败用户的set集合KEY


    public static void main(String[] args) throws InterruptedException {


        ExecutorService executor = Executors.newFixedThreadPool(1000);
        ExecutorService executor1 = Executors.newFixedThreadPool(1000);
        Jedis jedis = pool.getResource();

        initStores();//初始化
        incrStores(10);//动态增加10个商品
        jedis.close();

        for (int i = 0; i < 1000; i++) {
            executor.execute(new Runnable() {
                @Override
                public void run() {
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    for (int c = 0; c < 100; c++) {

                        executor1.execute(new MyRunnable());
                    }
                }
            });
//            Thread.sleep(50);
        }
        executor.shutdown();

//        System.exit(1);
    }

    /*初始化库存*/
    public static void initStores() {
        Jedis jedis = pool.getResource();
//        if(jedis.llen(storesName)>0){
//            throw new RuntimeException("库存值大于0,不可初始化");
//        }

        jedis.del(storesName, setSuccessName, setFailName);//删除仓库,删除抢购成功队列,删除抢购失败队列
        jedis.set(storesCountKey, stores + "");
        for (int i = 1; i <= stores; i++) {
            String storeProp = i + ":" + UUID.randomUUID().toString();//模拟   序号:ID
            jedis.rpush(storesName, storeProp);
        }
    }

    /*动态增加库存*/
    public static void incrStores(int stores) {
        Jedis jedis = pool.getResource();
        int storesCount = Integer.valueOf(jedis.get(storesCountKey));
        jedis.set(storesCountKey,(storesCount+stores)+"");//更新库存数量
        for (int i = 1 + storesCount; i <= stores + storesCount; i++) {
            String storeProp = i + ":" + UUID.randomUUID().toString();//模拟   序号:ID
            jedis.rpush(storesName, storeProp);
        }
    }


    static class MyRunnable implements Runnable {
        Jedis jedis = pool.getResource();

        public MyRunnable() {
        }

        @Override
        public void run() {
            try {

                String userifo = UUID.randomUUID().toString();

                //redis list取值
                String ls = jedis.lpop(storesName);

                //取值成功,说明拿到了商品,即抢购成功
                if (ls != null) {
                    String[] er = ls.split(":");
                    int num = Integer.valueOf(er[0]);//商品序号
                    String ID = er[1];//商品ID

                    /* 抢购成功业务逻辑 */
                    jedis.sadd(setSuccessName, userifo);
                    System.out.println("用户:" + userifo + "抢购成功,当前抢购成功人数:"
                            + num + "------抢购成功商品ID:" + ID);

                    Thread.sleep(900);//模拟业务执行时间

                } else {
                    System.out.println("用户:" + userifo + "抢购失败,库存以空");
                    jedis.sadd(setFailName, userifo);
                    Thread.sleep(400);//模拟业务执行时间
                    // Thread.sleep(500);
                    return;
                }

            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                jedis.close();
            }

        }

    }
}
原文地址:https://www.cnblogs.com/yuan951/p/7612473.html