阅读java多线程编程核心技术的理解,特意记录下来。以便以后复习。 非线程安全问题会在多个线程对同一个对象中的实例变量进行并发访问时发生,产生的后果就是脏读。也就是读到的数据实际上是被更改过的。
被测试的类
/* @author hhf @date 2020/8/5-8:23 */ //在一个方法内部声明变量,并不存在非线程安全问题 public class HasSelfPrivateNum { public void addI(String username){ try { int num = 0 ; //方法内部声明的变量 if(username.equals("a")){ //线程threadA运行时 num = 100; System.out.println("a set over!"); Thread.sleep(2000); }else { //线程threadB运行时 num = 200; System.out.println("b set over"); } System.out.println(username + "num = " + num); } catch (InterruptedException e) { e.printStackTrace(); } } }线程类A
public class ThreadA extends Thread{ private HasSelfPrivateNum numRef; public ThreadA(HasSelfPrivateNum numRef) { super(); this.numRef = numRef; } @Override public void run() { super.run(); numRef.addI("a"); } }线程类B
public class ThreadB extends Thread{ private HasSelfPrivateNum numRef; public ThreadB(HasSelfPrivateNum numRef) { super(); this.numRef = numRef; } @Override public void run() { super.run(); numRef.addI("b"); } }测试类
/* @author hhf @date 2020/8/5-8:31 */ public class Run { public static void main(String[] args) { HasSelfPrivateNum hasSelfPrivateNum = new HasSelfPrivateNum(); ThreadA threadA = new ThreadA(hasSelfPrivateNum); threadA.start(); ThreadB threadB = new ThreadB(hasSelfPrivateNum); threadB.start(); } }如果不出现错误的的话a的num应该等于100
测试结果:正确
**
**
被测试的类 更改HasSelfPrivateNum类如下
/* @author hhf @date 2020/8/5-8:23 */ //多个线程访问一个对象的中的实例变量,可能出现非线程安全问题 public class HasSelfPrivateNum { private int num = 0 ; //变量并不是在方法内部 public void addI(String username){ try { if(username.equals("a")){ //线程threadA运行时 num = 100; System.out.println("a set over!"); Thread.sleep(2000); }else { //线程threadB运行时 num = 200; System.out.println("b set over"); } System.out.println(username + "num = " + num); } catch (InterruptedException e) { e.printStackTrace(); } } }测试结果: 出现错误a应该为100
在threadA线程睡眠时,threadB线程修改了num的值。num是共享的,所以当threadA的sout时输出变成了200
更改HasSelfPrivateNum类如下
/* @author hhf @date 2020/8/5-8:23 */ //多个线程访问一个对象的中的实例变量,可能出现非线程安全问题,使用synchronized保证线程安全 public class HasSelfPrivateNum { private int num = 0 ; // 安全的 synchronized public void addI(String username){ try { if(username.equals("a")){ num = 100; System.out.println("a set over!"); Thread.sleep(2000); }else { num = 200; System.out.println("b set over"); } System.out.println(username + "num = " + num); } catch (InterruptedException e) { e.printStackTrace(); } } }测试结果:正确
为什么使用synchronized输出结果不同。
threadA线程在调用addI方法时,在hasSelfPrivateNum对象上加锁了。所以使用hasSelfPrivateNum参数构造的对象threadB无法在threadA调用addI方法时,也调用addI方法即使threadA线程在睡眠状态。只有threadA线程在结束addI方法时,hreadB线程才能addI方法。