一个计时器,同时开启100个线程,每个线程休眠1ms钟后,将全局静态变量count加1,这100个线程创建完之后,休眠500ms,计算总耗时,程序如下:
public class Counter { public volatile static int count = 0; public static void inc() { // 这里延迟1毫秒,使得结果明显 try { Thread.sleep(1); } catch (InterruptedException e) { } count++; } public static void main(String[] args) { long time1 = System.currentTimeMillis(); System.out.println(); for (int i = 0; i < 100; i++) { new Thread(new Runnable() { @Override public void run() { Counter.inc(); } }).start(); } try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } // 这里每次运行的值都有可能不同,可能为100 System.out.println("Counter.count=" + Counter.count); long time2 = System.currentTimeMillis(); System.out.println("耗时:"+(time2-time1)); } }
运行结果:
Counter.count=96
耗时:506
Counter.count不等于100是因为count++线程不安全。
加上同步代码块:
public class Counter { public volatile static int count = 0; static Object obj = new Object(); public static void inc() { // 这里延迟1毫秒,使得结果明显 try { Thread.sleep(1); } catch (InterruptedException e) { } synchronized (obj) { count++; } } public static void main(String[] args) { long time1 = System.currentTimeMillis(); System.out.println(); for (int i = 0; i < 100; i++) { new Thread(new Runnable() { @Override public void run() { Counter.inc(); } }).start(); } try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } // 这里每次运行的值都有可能不同,可能为1000 System.out.println("Counter.count=" + Counter.count); long time2 = System.currentTimeMillis(); System.out.println("耗时:"+(time2-time1)); } }
运行结果:
Counter.count=100
耗时:507
那么synchronized对运行的速度有多大的影响,于是我分别100,1000,10000,100000与500000次,将未同步、synchronized (obj)与synchronized (Counter.class)结果对比,结果如下:
线程数量 | 未同步<测试更多的数据> | synchronized (obj) | synchronized (Counter.class) |
100 | Counter.count=97 耗时:508 <98-506><98-507><97-507><100-508> |
Counter.count=100 耗时:507 <100-507><100-507><100-508><100-508> |
Counter.count=100 耗时:506 <100-508><100-508><100-506><100-509> |
1000 | Counter.count=997 耗时:572 <993-562><994-562><995-561><990-561> |
Counter.count=1000 耗时:562 <1000-572><1000-560><1000-559><1000-560> |
Counter.count=1000 耗时:570 <1000-585><1000-560><1000-562><1000-561> |
10000 | Counter.count=9947 耗时:1067 <9951-1077><9962-1079><9936-1066><9953-1081> |
Counter.count=10000 耗时:1074 <10000-1075><10000-1068><10000-1079><10000-1074> |
Counter.count=10000 耗时:1072 <10000-1089><10000-1076><10000-1108><10000-1076> |
100000 | Counter.count=99399 耗时:6098 <99347-6111><99374-6099><99416-6196><99459-6137> |
Counter.count=100000 耗时:6103 <100000-6101><100000-6104><100000-6155><100000-6107> |
Counter.count=100000 耗时:6150 <100000-6287><100000-6182><100000-6159><100000-6104> |
500000 | Counter.count=497242 耗时:28892 <497394-30674><497274-29864><497129-29470><497216-29529> |
Counter.count=500000 耗时:31232 <500000-29297><500000-29053><500000-29166><500000-29006> |
Counter.count=500000 耗时:29251 <500000-29743><500000-29334><500000-29600><500000-29848> |
将上面耗时时间求平均值,得到:
线程数量 | 未同步平均用时 | synchronized (obj)平均用时 | synchronized (Counter.class)平均用时 |
100 | 507.2 | 507.4 | 507.4 |
1000 | 563.6 | 562.6 | 567.6 |
10000 | 1074 | 1074 | 1084.2 |
100000 | 6128.2 | 6114 | 6176.4 |
500000 | 29685.8 | 29550.8 | 29555.2 |
于是,得出结论:
1、synchronized可以解决多线程造成的count++不准确的问题。
2、随着线程数量的增加,加过synchronized后的代码,并不会对速度产生很明显的影响。
3、从1万与10万的结果来看,经过synchronized(obj)处理后的程序,要比synchronized(Counter.class)处理后的程序大约要快10ms。而100、1000与50万,synchronized(obj)与synchronized(Counter.class)执行速度几乎相等。从总体来看,随着线程数量的增加,synchronized(Counter.class)执行的效率并没有明显的低于synchronized(obj)。