论证
synchronized 修饰代码块时 对于引用类型 比较的参数是地址 而不是值 我们真实开发时候需要追加id之类区别符号 字符串追加时
就会new出个新字符串 内存地址随之改变 这时候 需要我们做出一些改变
直接追加区别字符时
可以看到输出 是线程没有被锁住
public class ceshi {
public static void main(String[] args) {
Person person = new Person();
Student student = new Student();
new Thread(()->{
person.fun1(1);
},"A").start();
new Thread(()->{
student.fun2(1);
},"B").start();
return ;
}
}
class Person {
public void fun1(Integer id){
synchronized ("userId"+id){
System.out.println("存钱开始");
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("存钱结束");
}
}
}
class Student {
public void fun2(Integer id){
synchronized("userId"+id){
System.out.println("花钱开始");
System.out.println("花钱结束");
}
}
}
通过工具类追加区别字符时
可看到输出结果 线程被锁住了
工具类实现了把传入的拼接字符返回同一地址
public class ceshi {
public static void main(String[] args) {
Person person = new Person();
Student student = new Student();
new Thread(()->{
person.fun1(1);
},"A").start();
new Thread(()->{
student.fun2(1);
},"B").start();
return ;
}
}
class Person {
public void fun1(Integer id){
synchronized(LockUtils.getLock("userId"+id)){
System.out.println("存钱开始");
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("存钱结束");
}
}
}
class Student {
public void fun2(Integer id){
synchronized(LockUtils.getLock("userId"+id)){
System.out.println("花钱开始");
System.out.println("花钱结束");
}
}
}
工具类
package com.example.demo1.Thread;
import java.util.HashMap;
/**
* 由于synchronized只能锁对象的地址,所有像Long为1000的用户id是锁不住的
* 此类来解决这个问题
* synchronized (UserLockUtils.getLock("test" + userId)) {}
* 因为是全局的入参当然是"接口名+userId"最好了
*/
public class LockUtils {
private static HashMap<String,Object> mMapId = new HashMap<>(), mMapIdCache = new HashMap<>();
/**
* 缓存切换的开始时间,等待{@link #mCacheDeleteTime}时间后将清空切换数据
*/
private static long mCacheCreatTime;
/**
* 最大缓存数(当超出这一数值时,会自动清空),缓存切换等待时间
*/
private static int mMaxCache = 20000, mCacheDeleteTime = 10000;
public static synchronized Object getLock(String oldId) {
Object returnSt;
if (mMapId.size() < mMaxCache) {//数据比较少,普通的返回锁
if (!mMapId.containsKey(oldId)) {
mMapId.put(oldId, new Object());
}
returnSt = mMapId.get(oldId);
} else {//累加的残留数据太多,切换至缓存
//缓存开始时间
long nowMills = System.currentTimeMillis();
if (mMapIdCache.size() == 0) {
mCacheCreatTime = nowMills;
}
if (!mMapIdCache.containsKey(oldId)) {
mMapIdCache.put(oldId, mMapId.getOrDefault(oldId, new Object()));
}
returnSt = mMapIdCache.get(oldId);
//等待mCacheChangeTime时间后清除原始数据
if (nowMills - mCacheCreatTime > mCacheDeleteTime) {
mMapId.clear();
//原始和缓存对调即可实现切换
HashMap change = mMapId;
mMapId = mMapIdCache;
mMapIdCache = change;
}
}
return returnSt;
}
}