zoukankan      html  css  js  c++  java
  • No.1.测试Synchronized加锁String字符串后的多线程同步状况

    测试目的描述
    Synchronized关键字锁定String字符串可能会带来严重的后果, 尽量不要使用 synchronized(String a) 因为JVM中,因为字符串常量池具有缓冲功能!
    接下来, 测试使用""的形式引用字符串和使用new的String()来声明一个字符串, 再使用Synchronized关键字同步该字符串,看两个线程共同调用含有这个Synchronized锁定的字符串的代码块会发生什么同步问题.

    测试1: 默认使用""的形式引用字符串 来作为同步条件两个线程

    测试1工程结构如下
    image.png

    测试1: MyPrinter.java

    package com.szs.pojo;
    /**
     * 定义一个打印数字线程类,作为ThreadTest的属性类
     * @author Administrator
     *
     */
    public class MyPrinter{
    	public void print(String paramString){
    		try {
    			synchronized (paramString) {
    				while(true){
    					System.out.println("字符串内容:"+paramString
    							+"; 字符串hashCode值:"+paramString.hashCode()
    							+"; 当前线程名字:"+ Thread.currentThread().getName()
    							+"	实验1使用 "66666" 来同步");
    					Thread.sleep(1000);
    				}
    			}
    			
    		} catch (Exception e) {
    			e.printStackTrace();
    			// TODO: handle exception
    		}
    	}
    	
    }
    
    

    测试1 ThreadTest #线程的实现类

    package com.szs.pojo;
    
    public class ThreadTest extends Thread {
    	private MyPrinter myPrinter;
    	 
        public ThreadTest(MyPrinter myPrint) {
            this.myPrinter = myPrinter;
        }
    
    	@Override
    	public void run() {
    		
    		//测试1: 默认使用""的形式引用字符串 来同步两个线程
    		myPrinter.print("66666666666666");
    	}
    
    
    }
    
    
    测试1 Client.java #测试类
    package com.szs.client;
    
    import com.szs.pojo.MyPrinter;
    import com.szs.pojo.ThreadTest;
    
    /**
     * 实验测试Synchronized锁定字符串常量池中的一个字符串
     * 实例化两个ThreadTest线程启动类做测试
     * @author Administrator
     */
    public class Client {
    	public static void main(String[] args) {
    		//声明两个线程,争取一个字符串
    		MyPrinter myPrinter = new MyPrinter();
    		
    		ThreadTest testA = new ThreadTest(myPrinter);
    		
    		ThreadTest testB = new ThreadTest(myPrinter);	
    		
    		//测试1: 默认使用""的形式引用字符串 来同步
    		testA.setName("a");
    		testB.setName("b");
    		
    		testA.start();
    		testB.start();
    		
    	}
    }
    

    测试1 测试结果

    字符串内容:66666666666666; 字符串hashCode值:995606336; 当前线程名字:a	实验1使用 "66666" 来同步
    字符串内容:66666666666666; 字符串hashCode值:995606336; 当前线程名字:a	实验1使用 "66666" 来同步
    字符串内容:66666666666666; 字符串hashCode值:995606336; 当前线程名字:a	实验1使用 "66666" 来同步
    字符串内容:66666666666666; 字符串hashCode值:995606336; 当前线程名字:a	实验1使用 "66666" 来同步
    字符串内容:66666666666666; 字符串hashCode值:995606336; 当前线程名字:a	实验1使用 "66666" 来同步
    字符串内容:66666666666666; 字符串hashCode值:995606336; 当前线程名字:a	实验1使用 "66666" 来同步
    字符串内容:66666666666666; 字符串hashCode值:995606336; 当前线程名字:a	实验1使用 "66666" 来同步
    ...........
    

    测试2: 使用new的String对象来引用字符串 来同步两个线程

    测试2工程结构如下
    image.png

    测试2 MyPrinter.java
    package com.szs.pojo;
    /**
     * 定义一个打印数字线程类,作为ThreadTest的属性类,显示线程争取情况
     * @author Administrator
     *
     */
    public class MyPrinter extends Thread{
    	public void print(String paramString){
    		try {
    			synchronized (paramString) {
    				while(true){
    					System.out.println("字符串内容:"+paramString
    							+"; 字符串hashCode值:"+paramString.hashCode()
    							+"; 当前线程名字:"+ Thread.currentThread().getName()
    							+"	实验2使用new String("6666")来同步");
    					Thread.sleep(1000);
    				}
    			}
    			
    		} catch (Exception e) {
    			e.printStackTrace();
    			// TODO: handle exception
    		}
    	}
    }
    
    测试2 ThreadTest.java
    package com.szs.pojo;
    /**
     * 定义线程启动类 ,使用MyPrint作为属性类,调用其打印数字的方法
     * @author Administrator
     *
     */
    public class ThreadTest extends Thread {
    	private MyPrinter myPrinter;
    	 
        public ThreadTest(MyPrinter myPrinter) {
            this.myPrinter = myPrinter;
        }
    
    	@Override
    	public void run() {
    
    		String newString = new String("66666666");
    		
    		//这里会进行两次调用,每个线程分别new一个对象,两个String对象地址不一样!
    		myPrinter.print(newString);
    	}
    
    
    }
    
    
    测试2 Client.java 测试类
    package com.szs.client;
    
    import com.szs.pojo.MyPrinter;
    import com.szs.pojo.ThreadTest;
    
    /**
     * 实验测试Synchronized锁定字符串常量池中的一个字符串
     * 
     *  测试2: 使用new的String来引用字符串 来同步两个线程,结果显示线程可以交替执行
     * 实例化两个ThreadTest线程启动类做测试
     * @author Administrator
     */
    public class Client {
    	public static void main(String[] args) {
    		//声明两个线程,同步争取一个字符串
    		
    		MyPrinter myPrinter = new MyPrinter();
    		
    		ThreadTest testA = new ThreadTest(myPrinter);
    		
    		ThreadTest testB = new ThreadTest(myPrinter);	
    		
    		//测试2: 
    		testA.setName("a");
    		testB.setName("b");
    		
    		testA.start();
    		testB.start();
    		
    	}
    }
    
    

    测试2 测试结果

    (测试显示字符串hashCode值一致,其实这两个线程分别new的对象的内存地址是不一样的,只是hashcode一致,这里不多做解释)

    字符串内容:66666666; 字符串hashCode值:1900542720; 当前线程名字:a	实验2使用new String("6666")来同步
    字符串内容:66666666; 字符串hashCode值:1900542720; 当前线程名字:b	实验2使用new String("6666")来同步
    字符串内容:66666666; 字符串hashCode值:1900542720; 当前线程名字:a	实验2使用new String("6666")来同步
    字符串内容:66666666; 字符串hashCode值:1900542720; 当前线程名字:b	实验2使用new String("6666")来同步
    字符串内容:66666666; 字符串hashCode值:1900542720; 当前线程名字:a	实验2使用new String("6666")来同步
    字符串内容:66666666; 字符串hashCode值:1900542720; 当前线程名字:b	实验2使用new String("6666")来同步
    字符串内容:66666666; 字符串hashCode值:1900542720; 当前线程名字:b	实验2使用new String("6666")来同步
    ................
    

    总结

    在测试1中:

    • 使用引号直接引用的String字符串来作为Synchronized的同步条件;测试并同步两个线程,结果显示只有线程"a"一致占用打印机进行打印.
    • 简单分析
    • String类型的对象是不可变的,当你使用 " " 的形式引用字符串时(如上面测试1的"6666666666"), 如果JVM发现字符串常量池中已经有一个这样的对象, 那么它就使用那个对象而不再生成一个新的字符串对象--改为直接引用这个"6666666666"字符串.
    • 这时两个线程一块Synchronized的这个"66666"字符串就会出现问题,
      • 线程"a"先获得锁后,就一直执里面的while循环, 无限进行打印;
      • 线程"b"就一直只能干等着,处在等待状态.
      • 其实还有, System.out.println("66666"=="66666"); 输出的值为True的, 同一个"66666"同一地址.

    在测试2中:

    • 使用new的String来引用字符串来作为同步条件 来Synchronized同步两个线程,结果显示线程可以交替执行.
      • 简单分析
      • 这两个线程"a"和"b"分别run()的时候,都会调用代码 String newString = new String("66666666"); 产生两个String对象,这两个String对象的地址不一样, 故不会产生进程"a"一直占用CPU资源进行打印输出的问题.
      • 同样你也可以声明两个字符串都 = new String("66666666"),然后用"=="来进行判断这两个字符串的内存地址,结果为false.
    • 最后总结, 尽量不要使用String常量加锁 ,会出现死锁问题,可以使用new String()后的字符串对象加锁.或者,换成其他更好的办法/替代方案来解决实际问题.
  • 相关阅读:
    beego框架学习(一)安装
    专题 :JSON处理
    Java中getClassLoader().getResource()和getResource()的区别
    加载WebApplicationContext的方式
    Web.xml配置详解之context-param
    “Could not open ServletContext resource [/WEB-INF/applicationContext.xml]”解决方案
    如何解决 Eclipse中出现-访问限制由于对必需的库XX具有一定限制,因此无法访问类型
    JDK各个JAR包的作用
    eclipse汉化
    模板专题(一)函数模板
  • 原文地址:https://www.cnblogs.com/zhazhaacmer/p/11052751.html
Copyright © 2011-2022 走看看