synchronized锁字符串
示例一
在没有使用synchronized锁的情况下:
import java.util.HashMap;
import java.util.Map;
public class NonSynchronizedSchoolExample {
private static final Map<String, Integer> schoolCountMap = new HashMap<>(); // 存储每个学校的交卷数量
public static void main(String[] args) {
// 创建三个线程,模拟不同学校的学生交卷
Thread thread1 = new Thread(new SubmitPaperTask("西华师范大学"), "Thread-1");
Thread thread2 = new Thread(new SubmitPaperTask("西南石油大学"), "Thread-2");
Thread thread3 = new Thread(new SubmitPaperTask("西南石油大学"), "Thread-3");
thread1.start();
thread2.start();
thread3.start();
}
// 创建任务类,模拟学生交卷
static class SubmitPaperTask implements Runnable {
private final String school;
public SubmitPaperTask(String school) {
this.school = universityName;
}
@Override
public void run() {
// 直接访问并修改 HashMap,未考虑线程安全
Integer count = schoolCountMap.get(school);
if (count == null) {
count = 0; // 如果没有该学校的记录,默认值为0
}
// 模拟学生交卷
System.out.println(school + " 的学生正在交卷...");
try {
Thread.sleep(1000); // 模拟交卷时间
schoolCountMap.put(school, count + 1); // 增加该学校的交卷数量
System.out.println(school + " 的学生交卷完毕! 当前交卷数量: " + (count + 1));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
在没有使用synchronized的情况下,结果可能出现:
西华师范大学 的学生正在交卷...
西南石油大学 的学生正在交卷...
西华师范大学 的学生交卷完毕! 当前交卷数量: 1
西南石油大学 的学生交卷完毕! 当前交卷数量: 1
西南石油大学 的学生正在交卷...
西南石油大学 的学生交卷完毕! 当前交卷数量: 1
示例二
使用synchronized锁的情况下:
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
@RestController
public class SynchronizedSchoolController {
// 存储每个学校的交卷数量
private static final Map<String, Object> lockMap = new HashMap<>(); // 存储每个学校的锁对象
// 接收交卷请求
@GetMapping("/submitPaper/{school}")
public String submitPaper(@PathVariable String school) {
synchronized (this) { // 锁住该学校的锁对象
// 获取当前学校的交卷数量,如果没有则初始化为0
Integer count = schoolCountMap.get(school);
if (count == null) {
count = 0; // 如果没有该学校的记录,默认值为0
}
// 模拟学生交卷
try {
Thread.sleep(1000); // 模拟交卷时间
schoolCountMap.put(school, count + 1); // 增加该学校的交卷数量
return school + " 的学生交卷完毕! 当前交卷数量: " + (count + 1);
} catch (InterruptedException e) {
e.printStackTrace();
return "交卷失败!";
}
}
}
}
在这种情况下,synchronized锁住的当前实例对象,在这种情况下,我们都每一个线程都是串行执行的。
示例三:
我现在想要改进代码,我可以用synchronized锁住(school)这个字符串,这样不同学校的线程就是并行的,相同学校的就是串行执行的。
。。。
synchronized (school)
。。。
使用synchronized锁school字符串的情况下,如果我们使用http接口的发送去请求的话,spring的底层不是发送传递的“西华师范大学”“西南石油大学”这样的字符串常量,而是通过new String(“西华师范大学”)这样的方式去传递string对象。这种情况下锁的资源是三个不同的对象,没有同一个资源的互斥,就会发送并行。
这涉及到 字符串池(String Pool)和 字符串对象的创建方式:
字符串常量(字符串池):
在 Java 中,字符串常量(例如 "西华师范大学"
)会被存储在一个特殊的内存区域,称为 字符串池。当你创建一个字符串常量时,JVM 会检查池中是否已经存在相同的字符串对象,如果存在,就会返回池中的引用,否则将该字符串放入池中。
String str1 = "西华师范大学"; // 会被存储在字符串池中
String str2 = "西华师范大学"; // str1 和 str2 引用同一个对象
通过 new String()
创建字符串对象:
通过 new String("西华师范大学")
创建的字符串对象不再从字符串池中获取对象,而是直接在堆内存中创建一个新的 String
对象。这意味着,每次调用 new String()
都会创建一个新的对象,即使其内容与字符串池中的常量相同。
String str1 = new String("西华师范大学"); // 会在堆中创建一个新的 String 对象
String str2 = new String("西华师范大学"); // str1 和 str2 引用不同的对象
所以直接synchronized (school)还是会出现异常。
示例四:
为了解决示例三的问题,我们想到了直接synchronized ()字符串常量。 因为字符串常量都是存放在字符串常量池当中的,是唯一的,能够形成资源互斥。
synchronized(school.intern())
但是字符串常量池里面的字符串是全局唯一的,可能会阻塞相同锁资源的不同操作,所以进一步改进:
我们通过ConcurrentMap创建一个锁对象
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
@RestController
public class SynchronizedSchoolController {
// 使用 ConcurrentHashMap,确保线程安全
private static final ConcurrentMap<String, Integer> schoolCountMap = new ConcurrentHashMap<>(); // 存储每个学校的交卷数量
private static final ConcurrentMap<String, Object> lockMap = new ConcurrentHashMap<>(); // 存储每个学校的锁对象
// 接收交卷请求
@GetMapping("/submitPaper/{school}")
public String submitPaper(@PathVariable String school) {
// 获取每个学校的锁对象,确保每个学校有独立的锁
Object lock = lockMap.computeIfAbsent(school, key -> new Object());
synchronized (lock) { // 锁住该学校的锁对象
// 获取当前学校的交卷数量,如果没有则初始化为0
Integer count = schoolCountMap.get(school);
if (count == null) {
count = 0; // 如果没有该学校的记录,默认值为0
}
// 模拟学生交卷
try {
Thread.sleep(1000); // 模拟交卷时间
schoolCountMap.put(school, count + 1); // 增加该学校的交卷数量
return school + " 的学生交卷完毕! 当前交卷数量: " + (count + 1);
} catch (InterruptedException e) {
e.printStackTrace();
return "交卷失败!";
}
}
}
}