synchronized学习-各种情况测试

        以前写过synchronized的测试笔记,今天又发现synchronized的一些问题,继续学习。先把以前的贴出来:http://709002341.iteye.com/admin/blogs/2253666

         已经做过的测试就不做了,今天只做用synchronized锁住String类型和锁住基本类型的测试。

         其中8个基本类型没什么区别,就用Integer代替了。

         首主程序的测试代码:

/**
	 * main:
	 * 
	 * @param args
	 *            void 返回类型
	 * @throws InterruptedException 
	 */
	public static void main(String[] args) throws InterruptedException {
		// TODO Auto-generated method stub
		long start = System.currentTimeMillis();
		Thread1 t1 = new Thread1(1);
		Thread1 t2 = new Thread1(2);
		Thread1 t3 = new Thread1(3);
		t1.start();
		t2.start();
		t3.start();
		t1.join();
		t2.join();
		t3.join();
		long end = System.currentTimeMillis();
		System.out.println("一共执行了:"+(end - start));
	}

    以下所有的测试程序都是这个测试代码测试。

        

         首先第一个测试:

static class Thread1 extends Thread {
		private Object o = new Object();
		private int i;
		private Integer j = new Integer(1);
		public Thread1(int i){
			this.i = i;
		}
		public void run() {
			synchronized(j){
				System.out.println("线程"+i+"在执行---------");
				try {
					//睡2秒
					TimeUnit.SECONDS.sleep(2);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
	}

 结果:

线程1在执行---------
线程2在执行---------
线程3在执行---------
一共执行了:2004

 第二个测试:

static class Thread1 extends Thread {
		private Object o = new Object();
		private int i;
		private Integer j = 1;
		public Thread1(int i){
			this.i = i;
		}
		public void run() {
			synchronized(j){
				System.out.println("线程"+i+"在执行---------");
				try {
					//睡2秒
					TimeUnit.SECONDS.sleep(2);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
	}

 结果:

线程1在执行---------
线程3在执行---------
线程2在执行---------
一共执行了:6008

     这是因为用new创建出来的Integer对象在堆上,具体于某个对象,所以三个线程对象的j都是不同的。但是直接赋值创建的基础类型的对象实体是在常量池中,所以不论多少个线程对象,j指向的对象是同一位置的。

     再看两个:

static class Thread1 extends Thread {
		private Object o = new Object();
		private int i;
		private Integer j = 1111;
		public Thread1(int i){
			this.i = i;
		}
		public void run() {
			synchronized(j){
				System.out.println("线程"+i+"在执行---------");
				try {
					//睡2秒
					TimeUnit.SECONDS.sleep(2);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}
	}

     结果为:

线程1在执行---------
线程2在执行---------
线程3在执行---------
一共执行了:2007
static class Thread1 extends Thread {
		private Object o = new Object();
		private int i;
		private static Integer j = 1111;
		public Thread1(int i){
			this.i = i;
		}
		public void run() {
			synchronized(j){
				System.out.println("线程"+i+"在执行---------");
				try {
					//睡2秒
					TimeUnit.SECONDS.sleep(2);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}

 结果为:

线程1在执行---------
线程3在执行---------
线程2在执行---------
一共执行了:6004

       这也佐证了这个观点:Byte,Short,Integer,Long,Character这5种整型的包装类也只是在对应值小于等于127时才可使用常量池。

       再看一个比较奇怪的:

static class Thread1 extends Thread {
		private Object o = new Object();
		private int i;
		private static Integer j = 1;
		public Thread1(int i){
			this.i = i;
		}
		public void run() {
			synchronized(j){
				j = 2;    // ---------------①
				System.out.println("线程"+i+"在执行---------");
				try {
					//睡2秒
					TimeUnit.SECONDS.sleep(2);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}
	}

      执行结果:

线程1在执行---------
线程2在执行---------
线程3在执行---------
一共执行了:4007

     这是因为三个对象创建的j的引用其实都是同一个,锁的是引用j指向的对象。所以第一次访问的时候锁的是常量池中的1,第二次和第三次访问锁的是常量池中的2,所以出现这种情况:第一个线程独立,第二个线程和第三个线程互斥。

      锁住String类型和锁住Integer类型是类似的,因为String类型也保存在常量池中。

       不同的是,String类型有intern方法。但是intern方法是将字符串放入常量池,而不改变引用的指向。所以下面代码:

static class Thread1 extends Thread {
		private Object o = new Object();
		private int i;
		private static String j = new String("sssssssssssssssssss");
		public Thread1(int i){
			this.i = i;
		}
		public void run() {
			synchronized(j){
				j.intern();         //------------①
				System.out.println("线程"+i+"在执行---------");
				try {
					//睡2秒
					TimeUnit.SECONDS.sleep(2);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}
	}

    结果为:

线程1在执行---------
线程2在执行---------
线程3在执行---------
一共执行了:6005

    但是如果①改为这样:j = "s";执行结果就是:

线程1在执行---------
线程2在执行---------
线程3在执行---------
一共执行了:4006

     这是因为j = "s";不止在常量池中创建一个字符串对象s,而且将引用j指向s。

      另外我测试了一下集合:

static class Thread1 extends Thread {
		private Object o = new Object();
		private int i;
		private static List<String> l = new ArrayList<String>();
		public Thread1(int i){
			this.i = i;
		}
		public void run() {
			synchronized(l){
				l.add("sss");
				System.out.println("线程"+i+"在执行---------");
				try {
					//睡2秒
					TimeUnit.SECONDS.sleep(2);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}
	}

 结果为:

线程1在执行---------
线程3在执行---------
线程2在执行---------
一共执行了:6008

       可知引用指向的对象没有变,虽然他使用了add方法。

       最后总结一下(个人总结,大神请喷):

       使用synchronized关键字,锁的是对象,在java中一切都是对象(基本类型除外)。

       synchronized锁static方法的时候,锁的是整个类,可以认为锁住了方法区中类的那块区域。

       synchronized锁普通方法的时候,锁的是当前方法所在的对象,可以认为是锁住了当前对象在堆中的位置。

       synchronized锁方法块的时候,比如执行 synchronized(Object o),那么synchronized锁住的就是引用o指向的内存地址。如果o是基础类型中的Byte,Short,Integer,Long,Character并且范围在(-128,127)的时候(在常量池中保存的数据),锁住的就是该对象在常量池中的位置(多个Integer对象同一个位置,共享一把锁)

       如何判断两个synchronized是不是同一把锁?   只要synchronized(Object o1)和synchronized(Object o2)中,o1==o2返回true,就说明两个synchronized方法块使用的是同一把锁

猜你喜欢

转载自709002341.iteye.com/blog/2275542
今日推荐