测试目的描述

Synchronized关键字锁定String字符串可能会带来严重的后果, 尽量不要使用 synchronized(String a) 因为JVM中,因为字符串常量池具有缓冲功能!

接下来, 测试使用""的形式引用字符串和使用new的String()来声明一个字符串, 再使用Synchronized关键字同步该字符串,看两个线程共同调用含有这个Synchronized锁定的字符串的代码块会发生什么同步问题.

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

测试1工程结构如下

测试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()
+"\t实验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工程结构如下

测试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()
+"\t实验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()后的字符串对象加锁.或者,换成其他更好的办法/替代方案来解决实际问题.

No.1.测试Synchronized加锁String字符串后的多线程同步状况的更多相关文章

  1. C#实现把String字符串转化为SQL语句中的In后接的参数

    实现把String字符串转化为In后可用参数代码: public string StringToList(string aa) { string bb1 = "("; if (!s ...

  2. Java String字符串深入详解

    Java中字符串对象创建有两种形式,一种为字面量形式,如String str = "hello";,另一种就是使用new这种标准的构造对象的方法,如String str = new ...

  3. [ Java学习基础 ] String字符串的基本操作

    字符串的拼接 String字符串虽然是不可变的字符串,但也同样可以进行拼接,只是会产生一个新的对象.String字符串拼接的时候可以使用"+"运算符或String的concat(S ...

  4. Redis学习系列二之.Net开发环境搭建及基础数据结构String字符串

    一.简介 Redis有5种基本数据结构,分别是string.list(列表).hash(字典).set(集合).zset(有序集合),这是必须掌握的5种基本数据结构.注意Redis作为一个键值对缓存系 ...

  5. java基础18 String字符串和Object类(以及“equals” 和 “==”的解析)

    一.String字符串 问:笔试题:new String("abc")创建了几个对象?答:两个对象,一个对象是 位于堆内存,一个对象位于字符串常量池 class Demo17 { ...

  6. JAVA基础——重新认识String字符串

    深入剖析Java之String字符串 在程序开发中字符串无处不在,如用户登陆时输入的用户名.密码等使用的就是字符串. 在 Java 中,字符串被作为 String 类型的对象处理. String 类位 ...

  7. String字符串性能优化的几种方案

    String字符串是系统里最常用的类型之一,在系统中占据了很大的内存,因此,高效地使用字符串,对系统的性能有较好的提升. 针对字符串的优化,我在工作与学习过程总结了以下三种方案作分享: 一.优化构建的 ...

  8. [lua]紫猫lua教程-命令宝典-L1-01-09. string字符串函数库

    L1[string]01. ASCII码互转 小知识:字符串处理的几个共同的几点 1.字符串处理函数 字符串索引可以为负数 表示从字符串末尾开始算起 所有字符串处理函数的 字符串索引参数都使用 2.所 ...

  9. String字符串缓冲区、StringBuffer

    String字符串缓冲区 1.StringBuffer类 StringBuffer又称为可变字符序列,字符串缓冲区支持可变的字符串, StringBuffer是个字符串的缓冲区,即就是它是一个容器,容 ...

随机推荐

  1. Linux服务器安装rocketMQ单机消息队列

    首先下载rocketMQ 1.解压: > unzip rocketmq-all-4.3.0-source-release.zip > cd rocketmq-all-4.3.0/ > ...

  2. Django ORM 数据库设置和读写分离

    一 Django的数据库配置 (一)修改settings.py文件关于数据库的配置: Django默认使用sqlite: DATABASES = { 'default': { 'ENGINE': 'd ...

  3. 15 Filter过滤器和Listener监听器

    1.Filter:过滤器 (1) 概念:生活中的过滤器:净水器,空气净化器,土匪.web中的过滤器:当访问服务器的资源时,过滤器可以将请求拦截下来,完成一些特殊的功能.过滤器的作用:一般用于完成通用的 ...

  4. 【NOIP2017】宝藏 题解(状压DP)

    题目描述 参与考古挖掘的小明得到了一份藏宝图,藏宝图上标出了 nnn 个深埋在地下的宝藏屋, 也给出了这 nnn 个宝藏屋之间可供开发的m mm 条道路和它们的长度. 小明决心亲自前往挖掘所有宝藏屋中 ...

  5. leetcode tree相关题目总结

    leetcode tree相关题目小结 所使用的方法不外乎递归,DFS,BFS. 1. 题100 Same Tree Given two binary trees, write a function ...

  6. 最简单的Python3多线程实现

    最简单的实现了一下Python的多线程: import threading def write_dbs(i): print(i) if __name__ == '__main__': for i in ...

  7. SpringCloud之熔断器Hystrix及服务监控Dashboard

    目的:     服务雪崩效应 服务熔断服务降级 Hystrix默认超时时间设置 Hystrix服务监控Dashboard 服务雪崩效应 雪崩效应就是一种不稳定的平衡状态也是加密算法的一种特征,它指明文 ...

  8. Android自动化测试之Monkey 转自:LupuX

    版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/u011436666/article/details/53998332 在之前的文章Android自动 ...

  9. Vasya and Shifts CodeForces - 832E (高斯消元)

    大意: 给定$4n$个$m$位的五进制数, $q$个询问, 每个询问给出一个$m$位的五进制数$b$, 求有多少种选数方案可以使五进制异或和为$b$. 高斯消元入门题 每次询问相当于就是给定了$m$个 ...

  10. 转:JVM的符号引用和直接引用

    在JVM中类加载过程中,在解析阶段,Java虚拟机会把类的二级制数据中的符号引用替换为直接引用. 1.符号引用(Symbolic References): 符号引用以一组符号来描述所引用的目标,符号可 ...