多线程时synchronized到底应该锁定什么对象?
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 }