No.1.测试Synchronized加锁String字符串后的多线程同步状况
测试目的描述
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字符串后的多线程同步状况的更多相关文章
- C#实现把String字符串转化为SQL语句中的In后接的参数
实现把String字符串转化为In后可用参数代码: public string StringToList(string aa) { string bb1 = "("; if (!s ...
- Java String字符串深入详解
Java中字符串对象创建有两种形式,一种为字面量形式,如String str = "hello";,另一种就是使用new这种标准的构造对象的方法,如String str = new ...
- [ Java学习基础 ] String字符串的基本操作
字符串的拼接 String字符串虽然是不可变的字符串,但也同样可以进行拼接,只是会产生一个新的对象.String字符串拼接的时候可以使用"+"运算符或String的concat(S ...
- Redis学习系列二之.Net开发环境搭建及基础数据结构String字符串
一.简介 Redis有5种基本数据结构,分别是string.list(列表).hash(字典).set(集合).zset(有序集合),这是必须掌握的5种基本数据结构.注意Redis作为一个键值对缓存系 ...
- java基础18 String字符串和Object类(以及“equals” 和 “==”的解析)
一.String字符串 问:笔试题:new String("abc")创建了几个对象?答:两个对象,一个对象是 位于堆内存,一个对象位于字符串常量池 class Demo17 { ...
- JAVA基础——重新认识String字符串
深入剖析Java之String字符串 在程序开发中字符串无处不在,如用户登陆时输入的用户名.密码等使用的就是字符串. 在 Java 中,字符串被作为 String 类型的对象处理. String 类位 ...
- String字符串性能优化的几种方案
String字符串是系统里最常用的类型之一,在系统中占据了很大的内存,因此,高效地使用字符串,对系统的性能有较好的提升. 针对字符串的优化,我在工作与学习过程总结了以下三种方案作分享: 一.优化构建的 ...
- [lua]紫猫lua教程-命令宝典-L1-01-09. string字符串函数库
L1[string]01. ASCII码互转 小知识:字符串处理的几个共同的几点 1.字符串处理函数 字符串索引可以为负数 表示从字符串末尾开始算起 所有字符串处理函数的 字符串索引参数都使用 2.所 ...
- String字符串缓冲区、StringBuffer
String字符串缓冲区 1.StringBuffer类 StringBuffer又称为可变字符序列,字符串缓冲区支持可变的字符串, StringBuffer是个字符串的缓冲区,即就是它是一个容器,容 ...
随机推荐
- Git安装和使用(Windows)
Git介绍 分布式:Git版本控制系统是一个分布式的系统,是用来保存工程源代码历史状态的命令行工具. 保存点:Git的保存点可以追踪源码中的文件, 并能得到某一个时间点上的整个工程项目的状态:可以在该 ...
- Kubernetes 使用 ingress 配置 https 集群(十五)
目录 一.背景 1.1 需求 1.2 Ingress 1.3 环境介绍 二.安装部署 2.1.创建后端 Pod 应用 2.2 创建后端 Pod Service 2.3.创建 ingress 资源 2. ...
- Linux系统权限设置 - 运用指南
下面对linux系统下的有关权限操作命令进行了梳理总结,并配合简单实例进行说明.linux中除了常见的读(r).写(w).执行(x)权限以外,还有其他的一些特殊或隐藏权限,熟练掌握这些权限知识的使用, ...
- Qt5 QtQuick系列----QtQuick的Secne Graph剖析(2)--自定义QML类型 (继承QQuickItem)
"当下即永恒" --- 佚名 Qt用户可以方便地使用QML中的Rectangle等基本类型,但是当不够用时,或,需要开发更高级的界面时,可以自己定义QML类型. 自定义QML类型 ...
- 超简单的react和typescript和引入scss项目搭建流程
1.首先我们先创建一个react项目,react官网也有react项目搭建的命令 npx create-react-app my-app cd my-app 2.安装我们项目需要的样式依赖,这个项目我 ...
- linux初识1
linux 操作系统 概念性的理解 1.Linux内置解释器bash 相当于pyhon解释器 2.Linux的内部大多是使用python去书写 云计算 1.只需要 花钱,买腾讯,阿里云服务器 2.专人 ...
- web页面元素定位
所有web网页中有8种元素定位方式 靠单一的特征找元素:6种(id,class_name,tag_name,name,link_text(2))组合各种特征和关系来找元素:2种(xpath,css) ...
- day42——外键的限制和解决方法、外键的三种约束模式、修改表(单表查询)
day42 外键的限制和解决方法 可以添加外键关联的那个字段可以是 被唯一(unique)约束的字段 或者 主键 限制:+ 由于外键的使用,致使多个表之间产生了联系,当我们对这些表进行更新或删除操作的 ...
- 【C#】上机实验八
1. 设计一个窗体应用程序,模拟写字板应用程序的基本功能.具体功能要求如下: (1)“文件”菜单中有“新建”.“打开”.“保存”.“退出”子菜单. (2)“编辑”菜单中有“剪切”.“复制”.“粘贴”. ...
- Jenkins安装maven integration plugin失败解决方法
最近装了一个jenkins准备搞一个自动化测试的持续集成,但是在安装maven integration这个插件时报错,试了几次都是失败! 错误原因如下: javadoc安装失败: java.io.IO ...