一、前述
最近看了多线程的一本书,寻思着自己做做笔记,同时也分享下,打算写一个系列的博客,结合书本和自己的一些理解,希望大家支持,提出不好的地方
二、synchronized同步方法
2.1 方法内的变量为线程安全
相信看到这里的人,肯定平时的开发中已经接触过 线程安全 与 非线程安全 相关的技术点,或者说一些平时的项目,或者我们 java 基础里的集合,里面就有很多线程安全的,以及非线程安全的等等,其实这些都是我们在学习或者运用多线程肯定会遇到的问题。
非线程安全就是会在多个线程对同一个对象中的实例变量进行并发访问时会发生,后果就是会产生数据脏读,就是说后面的线程读到的数据是被更改过的,不符合预期结果的,而线程安全就是获得的实例变量的值是经过同步保护处理的,不会出现脏读
在这里我们要注意的一点就是,非线程安全问题存在于 实例变量 中,如果是方法内部的私有变量,则不存在非线程安全问题,它所得到的结果就是线程安全的了
下面我们来看一个 demo, 目的就是实现方法内部声明一个变量时,是不存在非线程安全问题的
public class SynTestA {
public void addA(String name){
try {
int num = 0;
if(name.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(name + " num = " + num);
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
public class ThreadA extends Thread{
private SynTestA synTestA;
public ThreadA(SynTestA synTestA){
super();
this.synTestA = synTestA;
}
@Override
public void run(){
super.run();
synTestA.addA("a");
}
}
public class ThreadB extends Thread{
private SynTestA synTestA;
public ThreadB(SynTestA synTestA){
super();
this.synTestA = synTestA;
}
@Override
public void run(){
super.run();
synTestA.addA("b");
}
}
public class Run {
public static void main(String[] args) {
SynTestA synTestA = new SynTestA();
ThreadA threadA = new ThreadA(synTestA);
threadA.start();
ThreadB threadB = new ThreadB(synTestA);
threadB.start();
}
}
运行结果:
a set over
b set over
b num = 200
a num = 100
从运行结果来看,没有出现脏读的问题,如果出现脏读,那么这里可能出现类似 b num = 100 这样的脏数据。所以方法中的变量不存在非线程安全的问题,永远都是线程安全的,从 jvm 的内存结构来看,这个 num 的局部变量是线程私有的,因为 jvm 中的虚拟机栈是线程私有的,而虚拟机栈就包括了局部变量表。
每个方法在执行的同时都会创建一个栈帧(Stack Frame)用于存储局部变量表、操作数栈、动态链接、方法出口等信息。当一个方法刚开始执行的时候,这个方法的操作数栈是空的,在方法的执行过程中,会有各种字节码指令往操作数栈中写入和提取内容,也即入栈/出栈操作。