写过多线程的人 对synchronized这个关键字肯定很熟悉。我记得去年面试的时候,有一次二面就是问得它。让详细得说出它的作用,它锁的是什么东西。。 当初回答的很模糊,才知道自己了解的并不清楚。回来查资料,自己写程序测试。
在java里,一切东西都是对象,学java的人一定要有这个概念。一切都是,java里所有类都默认继承object,你的一切代码,不是对象就是对象的动作。你操作的所有东西也都是对象,基础类型也有基本的对应的包装类,也是对象。synchronized关键字,锁住的也是对象。目前我只发现它锁的两个东西: 一个是类的实例,就是具体的对象,还有一个就是实体类(也是对象)。
上代码:
package codeTest; import java.util.ArrayList; import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** * @ClassName: ThreadTest4 * @Description: TODO(测试synchronized) * @author xuejupo [email protected] * @date 2015-11-1 下午10:24:00 * */ public class ThreadTest4 { public static void main(String[] a) { long start = System.currentTimeMillis(); ExecutorService pool = Executors.newFixedThreadPool(100); List<Callable<String>> tasks = new ArrayList<Callable<String>>(); for (int i = 0; i < 100; i++) { synchroTest t = new synchroTest(); tasks.add(t); } try { pool.invokeAll(tasks); pool.shutdown(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } long end = System.currentTimeMillis(); System.out.println("线程执行了" + (end - start) + "毫秒"); } } class synchroTest implements Callable<String> { public synchronized static void test1() { try { Thread.sleep(10); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } @Override public String call() throws Exception { // TODO Auto-generated method stub test1(); return null; } }
结果是:
线程执行了1009毫秒
在上面的测试程序里,我用synchronized锁住了static方法,可以看到,100个线程也丝毫没有发挥多线程的优势。一个需要执行10毫秒的方法,100个线程用了1000多毫秒才执行完。synchronized锁住了static方法,相当于锁住了整个线程类。当第一个线程调用test1方法时,第二个线程创建了也只能等待。第一个线程执行完毕释放锁,第二个线程开始执行。。。。 其实就是个单线程工作。。
第二个测试程序,将线程类里test方法前的static关键字去掉,代码就不贴了,结果如下:
线程执行了22毫秒
充分发挥了多线程的优势.. 这是因为在第一个线程实例化了线程类synchroTest,申请了线程锁,他只是锁住了自己的实例t1,第二个线程实例化了线程类synchroTest,申请线程锁,他也只是锁住了自己的线程类t2.所以这个测试除了申请锁和释放锁的消耗,跟无锁的线程是没有任何区别的。
把线程类改成这样:
class synchroTest implements Callable<String> { public static Object b = new Object(); public void test(){ synchronized (b) { try { Thread.sleep(10); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } @Override public String call() throws Exception { // TODO Auto-generated method stub test(); return null; } }
结果:
线程执行了1009毫秒
跟第一个测试类似,第一个线程锁住了一个static的对象b,第二个对象视图申请b的锁,因为static对象在内存中只有一份,所以第二个线程申请锁的对象b,其实跟第一个线程锁住的是同一个对象。所以他只能等待第一个对象释放锁。
把object前的static去掉,结果是:
线程执行了21毫秒
说明申请线程对象的时候,申请了100个不同的对象,每个不同的对象里面都申请了不一样的object对象,锁其实锁住的是不同对象里面的不同的object。
还有个测试,说明其实锁住实例对象里面的object 跟锁住实例对象是不一样的。一个锁。假设对象A里有个对象b,那么某线程申请b的锁,对A是没有影响的,别的线程还是可以访问线程A的其他synchronized方法。
代码:
class synchroTest implements Callable<String> { public static Object b = new Object(); public void test(){ synchronized (b) { try { Thread.sleep(10); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } public synchronized void test1(){ try { Thread.sleep(10); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } @Override public String call() throws Exception { // TODO Auto-generated method stub test(); //-----(1) test1(); //---------(2) return null; } }
结果:
线程执行了1017毫秒
分析一下:当第一个线程t1在执行(1)的时候,他将b锁住,这时候没有其他线程可以申请到b的锁,所以只有t1在执行。可是当t1执行完(1),开始执行(2)的时候,线程t2就可以申请b的锁了,所以线程t2和线程t1可以并发执行。这样的结果跟单纯执行object为static时方法test的结果类似。