最近面试一直被多线程所折磨。因为之前的公司一直没有太多高并发的处理,所以对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类只是实现这种主子线程调度的其中一种方法,应该还有其他的办法。今天太晚了就先调研到这里。
2015-04-09 01:19:39