测试目的描述

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. 用浏览器控制台抓取shodan、搜索引擎、zone-h的结果

    0x00 前言 大部分内容来自参考连接的内容,只是一种爬取内容的思路. 在很久以前自己会有爬取zone-h做目标测试的需求,但是总是有各种反爬限制.而且个别网址还有前端自动生成内容的功能,使用Java ...

  2. 【转】Ubuntu环境搭建svn服务器

    记录一次使用Ubuntu环境搭建svn服务器的详细步骤 一.查看是否已经安装svn 命令:svn如果显示以下信息,说明已安装 二.卸载已安装的svn 命令:sudo apt-get remove -- ...

  3. 【剑指offer】面试题 22. 链表中倒数第 K 个节点

    面试题 22. 链表中倒数第 K 个节点

  4. char * const * (*a) (int b)

    char * const * (*a) (int b), 按照c++ program language的读法,从右往左读,* 读作pointer to 把(*a) (int b看作整体, (*a) ( ...

  5. opencv学习笔记D01

    目录 opencv学习笔记D01 一.图片读取 二.图片保存 三.图片展示 四.图片缩放 五.四种常用插值方式的比较 1.最近邻插值 2.双线性插值 3.区域插值 4.三次样条插值 我是尾巴: ope ...

  6. C#混音同时录制采集声卡和麦克风话筒

    在项目中,我们可能需要同时录制声卡的声音和麦克风的声音,比如直播间,在线教学.那么如何实现呢?当然是采用SharpCapture!下面开始演示关键代码,您也可以在文末下载全部源码: 设置授权 第一步: ...

  7. 每周分享五个 PyCharm 使用技巧(六)

    大家好,今天我又来给大家更新 PyCharm 的使用技巧. 从今年3月24号开始一直到今天,将近四个月的时间.包括本篇,一共更新了6篇文章,每篇 5 个小技巧,总计 30 个. 这30个使用技巧,全部 ...

  8. trie树(前缀树)详解——PHP代码实现

    trie树常用于搜索提示.如当输入一个网址,可以自动搜索出可能的选择.当没有完全匹配的搜索结果,可以返回前缀最相似的可能. 一.Tire树的基本性质 根节点不包含字符,除根节点外每一个节点都只包含一个 ...

  9. 用JS实现输出两个数范围内的随机数

    const rs = require("readline-sync"); function roundNum(min = 0, max = 0) { if (!isNaN(min) ...

  10. PHP 结合 Bootstrap 实现学生列表以及添加学生功能实现(继上篇登录及注册功能之后)

    本人是一位学生,正在学习当中,可能BUG众多,请见谅并指正,谢谢!!! 学生列表实现 HTML: <!DOCTYPE html> <html> <head> < ...