1 import org.junit.Test;
2
3 import java.sql.Timestamp;
4 import java.util.HashMap;
5 import java.util.Map;
6 import java.util.concurrent.Executor;
7 import java.util.concurrent.ExecutorService;
8 import java.util.concurrent.Executors;
9 import java.util.concurrent.TimeUnit;
10
11 /**
12 * @Description: synchronized加锁的对象测试
13 * @date: 2020-02-20 16:38
14 * @author: yff
15 */
16
17 /*
18 * synchronized
19 * 1,锁定普通方法时,锁定的是当前对象,仅对当前对象生效,当不同对象(用new创建出来的对象),来执行代码时,不能锁定代码,不能保证只有一个线程进入
20 * 2,锁定static时,锁定的是class类,不管创建几个相同类的对象来执行代码,都只能有一个线程进入
21 * 3(实验目的),锁定特定对象时,只能是最先拥有这个对象的线程才能进入
22 *先说一下业务要求:一次请求,在后台生成多线程同时查库,然后把多个查库的结果同时塞入map中。全程没有写库的操作。
23 *例如代码中,通过主类启动两个线程(模拟两个请求),每个请求创建各自的线程池处理各自的业务(互不能影响),各自的线程池再创建各自的两(多)个Runnable任务(提升效率)。
24 * 注意:TestRunnable中的map是临界资源
25 *总计两个线程池 pool1 与 pool 2 ,
26 * 四个线程 pool1-thread-1 pool1-thread-2 pool2-thread-1 pool2-thread-2
27 * ①当synchronized锁定this对象时:四个线程能相互影响,数据完全混乱,不符合业务要求
28 * ②当synchronized锁定TestRunnable.class类时:四个线程全都相互排斥,能保证数据安全
29 *但是多线程的目的原本是想让一个请求(线程池)中的两个线程相互协调提升效率。如果这样加锁,
30 *就会造成请求A在操作临界资源时,虽然可以让相同线程池中的其他线程排队等待,但是也会使其他请求中的多个线程也一并在外排队等待。
31 *而实际情况是,每次请求都会声明一个map,多个请求之间并不会相互影响,因为它们操作两个map。
32 * ③当synchronized锁定map时:同一个线程池中的线程相互等待,不同线程池的线程可以同时执行代码块。这也恰好符合业务逻辑,
33 *每个请求声明多个线程同时协作,在临界资源时等待。不同请求由于操作的资源不是同一个,所以不必相互等待(指的是塞入的不是一个map,但如果有写库场景的话需要等待)。
34 *把库看成map,如果写入操作是一个临界资源,则要等待,相当于一个线程池中的线程相互等待,如果写入的是多个map,则可以看成多个库,自然不用相互等待,道理都是一样的,活学活用吧。
35 * */
36 public class Main {
37
38
39 @Test
40 public void main() {
41 Thread testThread = new TestThread();
42 testThread.setName("启动线程1");
43 testThread.start();
44 Thread testThread2 = new TestThread();
45 testThread2.setName("启动线程2");
46 testThread2.start();
47 try {
48 testThread.join();
49 testThread2.join();
50 } catch (InterruptedException e) {
51 System.err.println("error!!!!!!!!!");
52 }finally {
53 System.out.println("main结束了");
54 }
55 }
56
57
58 class TestThread extends Thread {
59 @Override
60 public void run() {
61 Executor executor;
62 synchronized (TestThread.class){
63 System.out.println(new Timestamp(System.currentTimeMillis())+Thread.currentThread().getName()+"先进入了run创建了pool");
64 executor = Executors.newFixedThreadPool(4);
65 }
66 Map map = new HashMap(4);
67 map.put("key1", "value1");
68 map.put("key2", "value2");
69 Runnable t1 = new TestRunnable(map);
70 Runnable t2 = new TestRunnable(map);
71 executor.execute(t1);
72 executor.execute(t2);
73 try {
74 ((ExecutorService) executor).shutdown();
75 boolean flag;
76 while (true) {
77 flag = ((ExecutorService) executor).awaitTermination(4, TimeUnit.SECONDS);
78 if (flag) {
79 System.out.println(new Timestamp(System.currentTimeMillis())+Thread.currentThread().getName()+"终于执行完了");
80 break;
81 }else{
82 System.out.println(new Timestamp(System.currentTimeMillis())+Thread.currentThread().getName()+"还没有执行完");
83 }
84 }
85 } catch (InterruptedException e) {
86 System.err.println("error!!!!!!!!!!!");
87 } finally {
88 System.out.println(new Timestamp(System.currentTimeMillis())+Thread.currentThread().getName()+"Thread结束了");
89 }
90 }
91 }
92
93 class TestRunnable implements Runnable {
94
95 private Map map;
96
97 public TestRunnable(Map map) {
98 this.map = map;
99 }
100
101 @Override
102 public void run() {
103 synchronized (map) {
104 try {
105 System.out.println(new Timestamp(System.currentTimeMillis())+Thread.currentThread().getName()+"线程开始睡眠3秒");
106 Thread.sleep(3000);
107 System.out.println(new Timestamp(System.currentTimeMillis())+Thread.currentThread().getName()+"线程结束睡眠3秒");
108 } catch (InterruptedException e) {
109 System.err.println("error!!!!!!!");
110 }
111 }
112 }
113 }
114
115 }