package com.thread.main;
import org.junit.jupiter.api.Test;
/**
* Created by Administrator on 2017/6/13.
*/
public class MyThread{
@Test
public void main() {
new B().start();
new C().start();
try {
Thread.sleep(3000);
} catch (Exception e) {
e.printStackTrace();
}
}
}
class Home {
private static Home home;
private Home(){}
public static Home getInstance(){
if (home == null) {
synchronized (Home.class) {
home = new Home();
}
}
return home;
}
public static String name;
public synchronized String into(String name1) {
System.out.println(name1+"进来了");
this.name = name1;
if ("张三".equals(name1)) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return name;
}
}
class B extends Thread{
@Override
public void run() {
Home home = Home.getInstance();
System.out.println("B:"+home.into("张三"));
}
}
class C extends Thread{
@Override
public void run() {
Home home = Home.getInstance();
System.out.println("C:"+home.into("李四"));
}
}
打印结果:
张三进来了
李四进来了
C:李四
B:李四
synchronized不是方法同步吗,为什么针对多个线程同时访问的时候会出现这个问题呢,小弟这点没搞懂,请各位解答下,谢谢
首先 如果你Home.getInstance(); 得到的如果是两个不一样的对象,那么这个现象就很好解释,因为是不同对象,那synchronized只是对相同对象两个线程调用才会起作用。
所以先执行张三 name=张三 然后sleep 再李四 name=李四 返回c:李四 然后sleep结束 name已经被修改为李四,然后就是b:李四。
其实在运行过程你也会发现,先是马上打印 张三 李四C:李四 这恰恰说明了 synchronized没有起作用
那为什么会得到两个不一样的对象呢?你发现你写的getInstance()的问题了没有?假设 B类和C类同时进入了 if,然后就会发生张三得到new Home();
李四再次得到new Home();
所以改成
public static Home getInstance(){
synchronized (Home.class) {
if (home == null) {
home = new Home();
}
}
return home;
}
对象用volatile修饰
private volatile static Home home;
分析了一下你的代码,原因应该是你的B和C的run方法,其中的两条语句不是同步的,比如你B线程执行了run方法,刚执行完还没执行打印语句就发生了阻塞,然后C线程去执行了代码,加之你的Home类中的name是静态变量,所以在C执行run之后就已经发生了变化,所以在B线程恢复执行的时候就变成了最新的name数值,有两点改进:
1、将run方法全部封装在Home的同步块中
2、将Home中的name设置为成员变量或者返回的时候返回一个新的string对象