Java多线程的一些面试心得
最近面试一直被多线程所折磨。因为之前的公司一直没有太多高并发的处理,所以对JAVA的多线程并没有太多实战经验。今天面试的时候再多线程上被虐的很惨。晚上回来赶紧写一下demo去理解一下。
事例1:
1 public class A
2 {
3 public synchronized static void m1()
4 {
5 System.out.println("m1");
6 try
7 {
8 Thread.sleep(3000);
9 } catch (InterruptedException e)
10 {
11 // TODO Auto-generated catch block
12 e.printStackTrace();
13 }
14 }
15 public synchronized static void m2()
16 {
17 System.out.println("m2");
18 try
19 {
20 Thread.sleep(3000);
21 } catch (InterruptedException e)
22 {
23 // TODO Auto-generated catch block
24 e.printStackTrace();
25 }
26 }
27 public synchronized void m3()
28 {
29 System.out.println("m3");
30 try
31 {
32 Thread.sleep(3000);
33 }
34 catch (InterruptedException e)
35 {
36 // TODO Auto-generated catch block
37 e.printStackTrace();
38 }
39 }
40 public synchronized void m4()
41 {
42 System.out.println("m4");
43 try
44 {
45 Thread.sleep(3000);
46 } catch (InterruptedException e)
47 {
48 // TODO Auto-generated catch block
49 e.printStackTrace();
50 }
51 }
52 public void m5()
53 {
54 System.out.println("m5");
55 try
56 {
57 Thread.sleep(3000);
58 } catch (InterruptedException e)
59 {
60 // TODO Auto-generated catch block
61 e.printStackTrace();
62 }
63 }
64 public static void main(String[] args)
65 {
66 final A a1 = new A();
67 final A a2 = new A();
68 new Thread(new Runnable()
69 {
70
71 @Override
72 public void run()
73 {
74 A.m1();
75 A.m2();
76 a1.m3();
77 a1.m4();
78 }
79 },"T1").start();
80
81 new Thread(new Runnable()
82 {
83
84 @Override
85 public void run()
86 {
87 A.m1();
88 A.m2();
89 a1.m3();
90 a1.m4();
91 }
92 },"T2").start();
93 }
94 }
对于静态方法来说,他们的同步,实际上锁的是这个.class,这个类。
所以对于多个线程是无法并发访问 A.m1()和A.m1()的,也无法同时访问A.m1()和A.m2();
对于非静态方法,他们的同步,实际上锁的是这个对象的this实例。所以同时只能有一个线程去调用这个对象里面的同步方法。
由此可见:
T1访问A.m1() T2访问a1.m3(),可以并发访问
T1访问A.m2() T2访问a1.m4(),可以并发访问
但
T1访问a1.m3() T2访问a1.m3(),不可以并发访问,他们公用同一个this
T1访问a1.m3() T2访问a1.m4(),也不可以并发访问,他们公用同一个this
T1访问a1.m3() T2访问a2.m3(),可以并发访问,他们锁定的是两个this
T1访问a1.m3() T2访问a1.m5(),可以并发访问,m5没有被synchronized修饰
事例2:
现在我们需要编写这样一个代码:有五个txt文件,我们需要用五个线程对这5个文件进行字符统计,最后在汇总这5个文件的字符出现的次数。
1 import java.io.FileNotFoundException;
2 import java.io.FileReader;
3 import java.io.IOException;
4 import java.util.ArrayList;
5 import java.util.HashMap;
6 import java.util.List;
7 import java.util.Map;
8 import java.util.Set;
9 import java.util.concurrent.CountDownLatch;
10
11
12 class ThreadTest implements Runnable{
13 private int num;
14 public Map<Character, Integer> map = new HashMap<Character, Integer>(){};
15 private CountDownLatch latch = null;
16 public ThreadTest(int i, CountDownLatch latch)
17 {
18 this.num = i;
19 this.latch = latch;
20 }
21
22 @Override
23 public void run()
24 {
25 FileReader fr;
26 try
27 {
28 fr = new FileReader(num + ".txt");
29 }
30 catch (FileNotFoundException e)
31 {
32 System.out.println("File " + num + ".txt not find");
33 return ;
34 }
35 int ch = 0;
36 try
37 {
38 while((ch = fr.read())!=-1 )
39 {
40 char c = (char) ch;
41 if ( map.containsKey(c) )
42 {
43 int count = map.get(c);
44 count ++;
45 map.put(c, count);
46 } else{
47 map.put(c, 1);
48 }
49 }
50 latch.countDown();
51 }
52 catch (IOException e)
53 {
54
55 }
56 }
57 }
58
59 public class B
60 {
61 public static final int N = 5;
62 public static void main(String[] args) throws InterruptedException
63 {
64 List<ThreadTest> threadTestList = new ArrayList<ThreadTest>(){};
65 List<Thread> threadList = new ArrayList<Thread>(){};
66 CountDownLatch latch = new CountDownLatch(N);
67 for (int i = 0; i < N; i++)
68 {
69 ThreadTest threadTest = new ThreadTest(i, latch);
70 threadTestList.add(threadTest);
71 Thread thread = new Thread(threadTest);
72 threadList.add(thread);
73 }
74 for (int i = 0; i < N; i++ )
75 {
76 threadList.get(i).start();
77 }
78 latch.await();
79 Map<Character, Integer> result = new HashMap<Character, Integer>(){};
80 for ( ThreadTest threadTest : threadTestList )
81 {
82 Set<Character> charList = threadTest.map.keySet();
83 for (Character ch : charList)
84 {
85 if ( result.containsKey(ch) )
86 {
87 Integer count = result.get(ch);
88 count += threadTest.map.get(ch);
89 result.put(ch, count);
90 } else
91 {
92 result.put(ch, threadTest.map.get(ch));
93 }
94 }
95 }
96 for (Character ch : result.keySet() )
97 {
98 System.out.println(ch + " " + result.get(ch));
99 }
100 }
101
102 }
这里主要利用的是JAVA里面的CountDownLatch类。
创建CountDownLatch类时需要设定一个计数器。每当其他线程执行一次countDown方法时,计数器会减一,当计数器为0时,会唤醒所有正在执行await方法的线程。
66行执行了一次CountDownLatch类,并设置计数器为N(5)
69行在创建ThreadTest时将latch传入
50行在每个子线程执行完了统计之后会执行countDown方法
78行主线程执行await方法处于等待状态,当所有子线程执行完了countDown方法后,计数器为0,await会被唤醒,继续执行主线程的统计工作。
CountDownLatch类只是实现这种主子线程调度的其中一种方法,应该还有其他的办法。今天太晚了就先调研到这里。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· .NET10 - 预览版1新功能体验(一)