最近几天一直在钻研多线程问题,因为这一块一直是弱项,之前一直近不下心来细细学习,这些天网上看了一些资料,感觉受益匪浅,在此记录一下。
昨天看到关于静态类的并发问题,开始看着挺蛋疼的,擦,静态类里面怎么可能会有并发问题,设计有问题吧,用静态类处理全局变量,找抽么@。@。于是蛋疼实验了一下,哈哈,蛮好玩的!如有错误之处,欢迎指正,谢谢!
下面是代码:
首先是一个工具类,用于时间相关计算
public final class TimeUtil { /* 这里故意声明了一个全局的calendar实例,让static方法改变这个实例(到底是实例还是句柄,待我灯下测试即知)的日期,凸显并发问题! */ private static Calendar calendar = Calendar.getInstance(); /** * 根据用户输入时间获取这一年的第几天 * @param date * @return dayOfYear */ public static int getDay(Date date) { calendar.setTime(date); return calendar.get(Calendar.DAY_OF_YEAR); } /** * 根据用户输入时间获取月份 * @param date * @return month 值为月份-1 */ public static int getMonth(Date date) { calendar.setTime(date); return calendar.get(Calendar.MONTH); } /** * 根据给定字符串获得对应日期 * @param str 格式 yyyy-MM-dd hh:mm:ss * @return */ public static Date getTimeByStr(String str) { SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss"); Date time = null; try { time = df.parse(str); } catch (ParseException e) { e.printStackTrace(); } return time; }接着是一个测试线程类
public class TestStaticThread implements Runnable { String timeStr; public TestStaticThread(String timeStr) { this.timeStr = timeStr; } @Override public void run() { try { //不加sleep时间效果不明显啊! Thread.sleep(100L); Date time = TimeUtil.getTimeByStr(timeStr); int day = TimeUtil.getDay(time); int month = TimeUtil.getMonth(time); System.out.println("线程[" + Thread.currentThread().getName() + "]指定日期为[" + timeStr + "], " + "经过幸苦的计算后, 得到的天数为[" + day + "], 得到的月份为[" + (month + 1) + "月]"); } catch (InterruptedException e) { e.printStackTrace(); } } }
public class StaticTest { public static void main(String[] args) { TestStaticThread testStaticThread1 = new TestStaticThread("2013-02-05 15:15:15"); TestStaticThread testStaticThread2 = new TestStaticThread("2013-04-10 18:45:30"); TestStaticThread testStaticThread3 = new TestStaticThread("2013-06-15 18:45:30"); Thread t1 = new Thread(testStaticThread1, "t1"); Thread t2 = new Thread(testStaticThread2, "t2"); Thread t3 = new Thread(testStaticThread3, "t3"); t1.start(); t2.start(); t3.start(); } }
线程[t2]指定日期为[2013-04-10 18:45:30], 经过幸苦的计算后, 得到的天数为[36], 得到的月份为[2月] 线程[t1]指定日期为[2013-02-05 15:15:15], 经过幸苦的计算后, 得到的天数为[36], 得到的月份为[2月] 线程[t3]指定日期为[2013-06-15 18:45:30], 经过幸苦的计算后, 得到的天数为[166], 得到的月份为[6月] Process finished with exit code 0
哈哈,果断错了有木有!!
那么,要改正很简单,有两种方法:
1、把getDay()和getMonth()设置成同步方法,即改成如下
public static synchronized int getDay(Date date) public static synchronized int getMonth(Date date)
public static int getDay(Date date) { Calendar calendar = Calendar.getInstance(); calendar.setTime(date); return calendar.get(Calendar.DAY_OF_YEAR); } public static synchronized int getMonth(Date date) { Calendar calendar = Calendar.getInstance(); calendar.setTime(date); return calendar.get(Calendar.MONTH); }但是第一种方法其实还是存在问题的,由于静态锁锁的是这个Class,并不是锁Object,所以如果程序别处用TimeUtile tu = new TimeUtil() 去实例化一个对象,然后一个线程去调用getDay()(用TimeUtil.getDay()),另一线程调用getMonth()(用tu.getMonth() 此处需要把getMonth()改成非静态方法,如果是静态方法是安全的!),其实还是会出现问题的!当然,我们可以重写这个类的构造方法,禁止实例化来保证安全。
而方法二有个弊端,就是如果这个工具类中方法很多,每次都Calendar calendar = Calendar.getInstance();,可能会有一点小问题,和内存相关,由于我现在还不是很清楚静态方法里面GC回收对象的机制是否和非静态方法里面的对象回收机制一样,所以不好给出结论,晚上自己再写程序试一下!
另外有一个小知识点,关于多线程的,那就是wait()方法调用后,线程会立即释放锁,但是notifyAll()只会唤醒其他在等待该锁资源而阻塞的线程,并不会释放当前拥有的锁,这一点非常关键!
此外,要特别感谢一下熔岩的博客,让我学到了不少新姿势,下面是他的关于多线程方面的专题链接:
点击打开链接