场景描述
多线程处理大批量商品时,相同的商品只能有一个执行。
实现思路
ConcurrentHashMap(线程安全Map集合) + synchronized(静态函数锁)的方式,确保MAP集合能正确执行put和remove方法;
当前key被锁时,其余相同key通过自旋获取锁状态,直至锁被释放为止;
1.代码
import java.util.Random;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
public class test {
// 线程安全的Map集合,用来判断KEY是否执行完毕
static ConcurrentHashMap<String,String> lockMap = new ConcurrentHashMap();
public static void main(String[] args) {
String [] keys = {"K001","K002","K003","K004","K005","K006"};
Random rand = new Random();
for(int i=0;i<100;i++){
// 随机获取KEY
int index = rand.nextInt(keys.length);
String key = keys[index];
new Thread(new Runnable() {
@Override
public void run() {
Thread thread = Thread.currentThread();
while (true){
// 上锁
if(!lock(key)){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
continue;
}
try{
System.out.println(thread.getName() + "\t" + key + "\tstart");
int sleepTime = rand.nextInt(3) + 1;
System.out.println(thread.getName() + "\t" + key + "\tsleep:" + sleepTime + "S");
Thread.sleep(sleepTime * 1000);
System.out.println(thread.getName() + "\t" + key + "\tend");
}catch (Exception e){
e.printStackTrace();
}finally {
// 解锁
unlock(key);
break;
}
}
}
}).start();
}
}
// 上锁
static synchronized boolean lock(String key){
String lock = lockMap.get(key);
if(lock == null ) lockMap.put(key, UUID.randomUUID().toString());
return lock == null ? true:false;
}
// 解锁
static synchronized void unlock(String key) {
lockMap.remove(key);
}
}
2.校验结果
将控制台打印的内容,拷贝至Excel表格,然后通过筛选显示K001线程执行流程。通过下图可以看出,只有当上一个Key为K001的线程执行完毕,下一个才会执行;
3.代码优化
现在准备将之前的代码优化下,使它具备美观的同时更实用;
1.创建一个与锁操作有关的类。
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
public class SimpleLock{
static ConcurrentHashMap<String,String> lockMap = new ConcurrentHashMap();
FunctionLock fun;// 匿名类
int interval = 1;// 获取锁间隔,单位:秒
public SimpleLock(FunctionLock fun){
this.fun = fun;
}
// 上锁
static synchronized boolean lock(String key){
String lock = lockMap.get(key);
if(lock == null ) lockMap.put(key, UUID.randomUUID().toString());
return lock == null ? true:false;
}
// 解锁
static synchronized void unlock(String key) {
lockMap.remove(key);
}
// 运行
public void run(String key){
Thread thread = Thread.currentThread();
while (true){
// 上锁
if(!this.lock(key)){
try {
Thread.sleep(interval * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
continue;
}
try{
this.fun.run();// 匿名函数
}catch (Exception e){
e.printStackTrace();
}finally {
// 解锁
this.unlock(key);
break;
}
}
}
public interface FunctionLock {
// 匿名函数
public abstract void run() throws Exception;
}
}
2.在调用时,我们只需要实现匿名函数里的代码,将上锁、解锁等操作交由锁对象实现。
import com.test.common.util.SimpleLock;
import java.util.Random;
public class test {
public static void main(String[] args) {
String [] keys = {"K001","K002","K003","K004","K005","K006"};
Random rand = new Random();
for(int i=0;i<100;i++){
// 随机获取KEY
int index = rand.nextInt(keys.length);
String key = keys[index];
new Thread(new Runnable() {
@Override
public void run() {
Thread thread = Thread.currentThread();
new SimpleLock(new SimpleLock.FunctionLock() {
@Override
public void run() throws Exception {
System.out.println(thread.getName() + "\t" + key + "\tstart");
int sleepTime = rand.nextInt(3) + 1;
System.out.println(thread.getName() + "\t" + key + "\tsleep:" + sleepTime + "S");
Thread.sleep(sleepTime * 1000);
System.out.println(thread.getName() + "\t" + key + "\tend");
}
}).run(key);
}
}).start();
}
}
}
3.给锁(SimpleLock)对象,添加回调机制
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
public class SimpleLock{
static ConcurrentHashMap<String,String> lockMap = new ConcurrentHashMap();
FunctionLock fun;// 匿名类
int interval = 1;// 获取锁间隔,单位:秒
public SimpleLock(FunctionLock fun){
this.fun = fun;
}
// 上锁
static synchronized boolean lock(String key){
String lock = lockMap.get(key);
if(lock == null ) lockMap.put(key, UUID.randomUUID().toString());
return lock == null ? true:false;
}
// 解锁
static synchronized void unlock(String key) {
lockMap.remove(key);
}
// 运行
public Object run(String key) throws Exception{
Object result = null;
while (true){
// 上锁
if(!this.lock(key)){
try {
Thread.sleep(interval * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
continue;
}
try{
result = this.fun.run();// 匿名函数
}catch (Exception e){
throw e;
}finally {
// 解锁
this.unlock(key);
break;
}
}
return result;
}
public interface FunctionLock {
// 匿名函数
public abstract Object run() throws Exception;
}
}