59、synchronized同步代码块
synchronized同步方法的问题
有些情况下,在方法上面加synchronized同步,会有性能问题。
请看下面代码,来计算下两个线程执行的耗时:
package com.sutaoyu.Thread; public class SynchronizedTest02 { public static long begin1; public static long end1; public static long begin2; public static long end2; public static void main(String[] args) { final LongTask longTask = new LongTask(); Thread t1 = new Thread() { public void run() { begin1 = System.currentTimeMillis(); //执行耗时较长的任务方法 longTask.changeNum(true); end1 = System.currentTimeMillis(); } }; Thread t2 = new Thread() { public void run() { begin2 = System.currentTimeMillis(); //执行耗时较长的任务方法 longTask.changeNum(false); end2 = System.currentTimeMillis(); } }; t1.start(); t2.start(); //先让主线程睡眠,保证t1和t2线程执行完毕之后再计算时间 try { Thread.sleep(10000); }catch(InterruptedException e) { e.printStackTrace(); } long begin = 0; long end = 0; //将先执行的线程的时间和最后执行线程的时间获取到 if(begin1 > begin2) { begin = begin2; }else { begin = begin1; } if(end1 > end2) { end = end1; }else { begin = begin1; } if(end1 > end2) { end = end1; }else { end = end2; } System.out.println("两个线程总共耗时:" + (end -begin)/1000 + "秒"); } }
使用同步代码块完善上面代码
上面代码打印结果是6秒,里面使用Thread.sleep方法来模拟了一个执行耗时较长的代码,假设这段代码并不会涉及到安全问题,所以就没有比较加入同步了。
来使用synchronized代码块来修改下上面的LongTask类。
package com.sutaoyu.Thread; public class SynchronizedTest02 { public static long begin1; public static long end1; public static long begin2; public static long end2; public static void main(String[] args) { final LongTask longTask = new LongTask(); Thread t1 = new Thread() { public void run() { begin1 = System.currentTimeMillis(); //执行耗时较长的任务方法 longTask.changeNum(true); end1 = System.currentTimeMillis(); } }; Thread t2 = new Thread() { public void run() { begin2 = System.currentTimeMillis(); //执行耗时较长的任务方法 longTask.changeNum(false); end2 = System.currentTimeMillis(); } }; t1.start(); t2.start(); //先让主线程睡眠,保证t1和t2线程执行完毕之后再计算时间 try { Thread.sleep(10000); }catch(InterruptedException e) { e.printStackTrace(); } long begin = 0; long end = 0; //将先执行的线程的时间和最后执行线程的时间获取到 if(begin1 > begin2) { begin = begin2; }else { begin = begin1; } if(end1 > end2) { end = end1; }else { begin = begin1; } if(end1 > end2) { end = end1; }else { end = end2; } System.out.println("两个线程总共耗时:" + (end -begin)/1000 + "秒"); } }
修改后将需要同步的代码放到synchronized代码块中,再次运行SynchronizedTest02类,打印结果是3秒,因为那段耗时较长的代码是在异步情况下运行,所以节省了一些时间。
注意:多个线程在执行synchronized同步代码块时,代码块括号里面可以传入任意对象,但一定要保证多个线程访问的是同一个对象。
比如将LongTask中的成员变量Object obj = new Object();移动到changeNum方法中,在t1和t2两个线程中分别调用了changeNum方法,这样会各自创建一个Object对象,仍然会导致线程安全问题发生。