Java 线程安全 与 锁多线程内存模型 线程私有栈内存 每个线程 私有的内存区域 进程公有堆内存 同一个进程 共有的内存区域 为什么会有线程安全问题? 多个线程同时具有对同一资源的
          - 线程私有栈内存
- 每个线程 私有的内存区域
 
 - 进程公有堆内存
- 同一个进程 共有的内存区域
 
 
- 多个线程同时具有对同一资源的操作权限,又发生了同时对该资源进行读取、写入的情况,那么就会出现重复操作的情况
 
锁就是对于操作资源的一种权限
锁可以做什么?对于一个资源加锁后,每次只能有一个线程对该资源进行操作,当该线程操作结束后,才会解锁。
解锁之后,所有的线程获得竞争此资源的机会。
- 读读 不需要加锁
 - 写写 需要加锁
 - 读写 需要加锁
 
- 
方法前加synchronized关键字
- 功能:线程进入用synchronized声明的方法时就上锁,方法执行完自动解锁,锁的是当前类的对象
 - 调用synchronized声明的方法一定是排队运行的
 - 当A线程 调用object对象的synchronized声明的X方法时
- B线程可以调用其他非synchronized声明的方法
 - B线程不能调用其他synchronized声明的非X方法
 
 
 - 
synchronized锁重入
- 锁重入的概念:自己可以重复获得自己的内部锁。即synchronized声明的方法,可以调用本对象的其他synchronized方法。
 - 锁重入支持继承的环境,即子类的synchronized方法也可以调用父类的synchronized方法。
 
 - 
synchronized同步代码块
- 
synchronized关键字与synchronized代码块的区别
- synchronized声明的方法是将当前对象作为锁
 - synchronized代码块是将任意对象作为锁
 
 - 
当两个线程访问同一个对象的synchronized代码块时,只有一个线程可以得到执行,另一个线程只能等待当前线程执行完才能执行。
- 一半同步,一半异步
- 不在synchronized代码块中就是异步执行,在synchronized代码块中就是同步执行
 
 
 - 一半同步,一半异步
 
 - 
 
下面对“一半同步,一半异步”进行代码验证
- 创建项目ltl0002 ,文件Task的代码如下:
 
package ltl0002;
public class Task {
    public void doTask(){
        for (int i = 0; i < 100; i++) {
            System.out.println("no synchronized ThreadName = " + Thread.currentThread().getName() + " i = " + (i+1));
        }
        synchronized (this){
            for (int i = 0; i < 100; i++) {
                System.out.println("synchronized ThreadName = " + Thread.currentThread().getName() + " i = " + (i+1));
            }
        }
        
    }
}
- 两个线程类代码
 
package ltl0002;
public class MyThread1 implements Runnable{
    private Task task = new Task();
    public MyThread1(Task task){
        this.task = task;
    }
    @Override
    public void run() {
        task.doTask();
    }
}
package ltl0002;
public class MyThread2 implements Runnable{
    private Task task = new Task();
    public MyThread2(Task task){
        this.task = task;
    }
    @Override
    public void run() {
        task.doTask();
    }
}
文件Run.java代码如下:
package ltl0002;
public class Run {
    public static void main(String[] args) {
        Task task = new Task();
        MyThread1 myThread1 = new MyThread1(task);
        MyThread2 myThread2 = new MyThread2(task);
        Thread tr1 = new Thread(myThread1);
        Thread tr2 = new Thread(myThread2);
        tr1.start();
        tr2.start();
    }
}
程序运行结果如图所示

进入synchronized代码块之后,排队运行,运行结果如图所示

在第一张图我们可以看到,线程0 和 1交叉输出,说明是异步进行,而在第二张图可以看出线程0运行完之后,线程1才运行,说明它们是同步运行,验证完毕。
- 现有三个线程,线程一对num进行修改,线程二三对num进行读取,如何可以实现,线程一与线程二三同步执行,而线程二三异步执行呢?
现在创建项目ltl0003进行测试,Number文件代码如下 
package ltl0003;
/**
 * @author liTianLu
 * @Date 2022/4/23 15:53
 * @purpose 成员变量有int num,以及get set方法
 */
public class Number {
  private int num;
  private boolean change = false;
  public int getNum() {
    return num;
  }
  public void setNum(int num) {
    this.num = num;
  }
  public boolean isChangeing(){
    return change;
  }
  public void setChange(boolean change) {
    this.change = change;
  }
}
两个线程类的代码如下:
package ltl0003;
/**
 * @author liTianLu
 * @Date 2022/4/23 15:36
 * @purpose 更改num的值
 */
public class MyThread01 implements Runnable{
  static int num = 0;
  Number number;
  public MyThread01(Number num ){
    this.number = num ;
  }
  @Override
  public void run() {
    synchronized (this){
      number.setChange(true);
      for (int i = 0; i < 10000; i++) {
        number.setNum(num++);
      }
      number.setChange(false);
    }
  }
}
package ltl0003;
import static java.lang.Thread.sleep;
/**
 * @author liTianLu
 * @Date 2022/4/23 15:35
 * @purpose 读取num的值
 */
public class MyThread02 implements Runnable{
  Number number;
  public MyThread02(Number num ){
    this.number = num ;
  }
  @Override
  public void run() {
    for (int i = 0; i < 1000 ; i++) {
      //如果number正在更改,就休眠1ms
      while(number.isChangeing()){
        try {
          sleep(1);
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
      }
      System.out.println(Thread.currentThread().getName()+"的输出为: num = " + number.getNum());
    }
  }
}
主函数文件Run代码如下:
package ltl0003;
/**
 * @author liTianLu
 * @Date 2022/4/23 15:15
 * @purpose 解决锁问题 线程一对num进行修改,线程二三对num进行读取,此代码要实现:线程一与线程二三同步执行,而线程二三异步执行。
 */
public class Run {
  public static void main(String[] args) {
    Number number = new Number();
    number.setNum(0);
    MyThread01 myThread01 = new MyThread01(number);
    MyThread02 myThread02 = new MyThread02(number);
    Thread tr1 = new Thread(myThread01);
    Thread tr2 = new Thread(myThread02);
    Thread tr3 = new Thread(myThread02);
    tr1.start();
    tr2.start();
    tr3.start();
  }
}
实验结果如图所示

我们发现,线程2/3执行的时候,线程1已经执行完毕,且线程2、3异步进行。
第二种:Lock对象的使用- ReentrantLock类可以达到与synchronized同样的效果。
 - 用法:
 
ReentrantLock lock = new ReentrantLock (); 
lock.lock();//加锁
lock.unlock();//解锁
        
//使用try catch finally 可以确保finally 中的代码执行,在finally中解锁
try{
    while(true){
        lock.lock ();
        //操作代码
    }
}catch (Exception e) {
    e.printStackTrace();
}finally {
    lock.unlock ();
}
 