学生选课场景,跟抢购火车票类似。百度了一下,总结出4种方式
1.线程锁。加了 synchronized ,性能急剧下降。 java 本身就是多线程的,你把它单线程使用,不是明智的选择。 同时,如果分布式部署的时候,加了 synchronized 也无法控制并发。
2.数据库锁。乐观锁,悲观锁。基于数据库乐观锁面对的场景是并发量低,用户活跃度较低的场景, 主要考察的是数据库磁盘的读写能力,并发能力在 300-700 ,随着硬盘速度的提升,有可能会有所提高。
3.缓存(基于内存数据库的异步写入)redis或者memcached
基于Redis
import java.util.List;
import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.Transaction;
public class MyRunnable implements Runnable {
String watchkeys = "total"; //监视keys
int total; //商品总数
Jedis redis = new Jedis("127.0.0.1");
String userinfo;
public MyRunnable() {
}
public MyRunnable(String userinfo, int count) {
this.userinfo = userinfo;
this.total = count;
}
public static void main(String[] args) {
int total = 50;
final Jedis redis = new Jedis("127.0.0.1");
redis.set("total", String.valueOf(total)); //设置商品总数
//redis.del("setsucc", "setfail");
redis.close();
//创建用户,设置商品总数,然后开始抢购
ExecutorService executor = Executors.newFixedThreadPool(20); //最大并发数(⊙o⊙)…
for (int i = 0; i < 1000; i++) { //抢购人数
executor.execute(new MyRunnable("user" + getRandomString(6),
total));
}
executor.shutdown();
}
@Override
public void run() {
redis.watch(watchkeys);
int num = Integer.valueOf(redis.get(watchkeys));
if (num >= 1 && num <= total) {
Transaction tx = redis.multi(); //开启事务
tx.incrBy(watchkeys, -1); //增量为-1
List list = tx.exec(); //提交事务,如果此时watchkeys被改动,则返回null
if (list == null || list.size() == 0) {
String failUser = "fail" + userinfo;
String failInfo = "用户:" + failUser + "抢购失败";
System.out.println(failUser);
redis.setnx(failUser, failInfo); //抢购失败业务逻辑
} else {
for (Object obj : list) {
String succUser = "succ" + obj.toString() + userinfo;
String succInfo =
"用户:" + succUser + "抢购成功,当前抢购人数" + (1 - (num - total));
System.out.println(succInfo);
redis.setnx(succUser, succInfo); //抢购成功业务逻辑
}
}
} else {
String failUser = "kcfail" + userinfo;
String failInfo = "用户:" + failUser + "商品抢购失败";
System.out.println(failUser);
redis.setnx(failUser, failInfo); //抢购失败业务逻辑
return;
}
redis.close();
}
private static String getRandomString(int length) {
String base = "abcdefghijklmnopqrstuvwxyz0123456789";
Random random = new Random();
StringBuffer sb = new StringBuffer();
for (int i = 0; i < length; i++) {
int number = random.nextInt(base.length());
sb.append(base.charAt(number));
}
return sb.toString();
}
}