1 线程状态
   
1 打印线程的所有状态
private static void printState() {
    for(Thread.State item:Thread.State.values())
        System.out.println(item);
}
NEW
RUNNABLE
BLOCKED
WAITING 等待
TIMED_WAITING 超时等待 有明确结束时间
TERMINATED
2 线程状态:
- new:新建状态,当线程被创建,但未启动(start()之前
- 
     runnable:运行状态
- 运行:得到时间片运行中状态
- 就绪:未得到时间片就绪状态 (保存了上下文
 
- 
     blocked:阻塞状态,如果
 
 遇到锁
 
 线程就会变为阻塞状态,等到另一个线程释放锁
- 
     waiting:
 
 休眠
 
 等待状态(无明确等待时间
- 
     timed_waiting:
 
 休眠
 
 等待状态(有明确结束时间
- terminated:销毁状态,线程结束之后会变成此状态
3 获取线程状态:
public static void main(String[] args) throws InterruptedException {
        //printState();
        Thread t1=new Thread(()->{
            System.out.println("当前线程状态2:"+Thread.currentThread().getState());
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        System.out.println("当前线程状态:"+t1.getState());
        t1.start();
        //让主线程休眠1秒
        Thread.sleep(1000);
        System.out.println("当前线程状态3:"+t1.getState());
        //等子线程执行完
        t1.join();
        System.out.println("当前线程状态4:"+t1.getState());
    }
    
    
    2 线程安全
   
1 线程安全问题:多线程执行环境下,程序执行结果和预期不符;
2 导致线程不安全的原因:
- 
抢占式执行(狼多肉少 
- 
多个线程同时修改同一个变量 
  static class Counter{
        private int number=0;
        private int MAX_COUNT=0;
        public Counter(int MAX_COUNT){
            this.MAX_COUNT=MAX_COUNT;
        }
        //++方法
        public void increment(){
            for (int i = 0; i < MAX_COUNT; i++) {
                number++;
            }
        }
        public void decrement(){
            for (int i = 0; i < MAX_COUNT; i++) {
                number--;
            }
        }
        public int getNumber(){
            return number;
        }
    }
    public static void main(String[] args) throws InterruptedException {
        Counter c=new Counter(100000);
        Thread th=new Thread(()->{
            c.increment();
        });
        Thread th1=new Thread(()->{
            c.decrement();
        });
        // c.increment();单线程时++——没问题
        // c.decrement();
        //启动多线程进行执行
        th.start();
        th1.start();
        //等待两个线程执行完
        th.join();
        th1.join();
        System.out.println("最终结果"+ c.getNumber());
    }
结果应该是0
最终结果8100
换种方式 使用全局变量接受结果后 结果为0
static class Counter{
        private int number=0;
        private int MAX_COUNT=0;
        public Counter(int MAX_COUNT){
            this.MAX_COUNT=MAX_COUNT;
        }
        //++方法
        public int increment(){
            int tep=0;
            for (int i = 0; i < MAX_COUNT; i++) {
//                number++;
                tep++;
            }
            return tep;
        }
        public int decrement(){
            int tep=0;
            for (int i = 0; i < MAX_COUNT; i++) {
//                number--;
                tep--;
            }
            return tep;
        }
        public int getNumber(){
            return number;
        }
    }
    //全局变量接收线程执行结果
    static int n1=0;
    static int n2=0;
    public static void main(String[] args) throws InterruptedException {
        Counter c=new Counter(100000);
        Thread th=new Thread(()->{
          n1 = c.increment();
        });
//        th.start(); th.join();
        Thread th1=new Thread(()->{
            n2=c.decrement();
        });
//        c.increment();
//        c.decrement();
        th.start();
        th1.start();
        th.join();
        th1.join();
//        System.out.println("最终结果"+ c.getNumber());
        System.out.println("最终结果"+ (n1+n2));
    }
}
0
- 
     操作是非原子性操作(tep++ 1.查询当前tep的值 2.tep-1操作 3.刷新tep的最新值
 
 1 线程从主内存读出最新的值Value
 
 2 放到线程的虚拟机栈的栈帧中的操作栈先压入,再弹出,供CPU计算+1,计算完再压入再弹出
 
 3 线程新值只写回到内存中
     
   
- 内存可见性问题
是指当某个线程正在使用对象状态而另一个线程在同时修改该状态,需要确保当一个线程修改了对象状态后,其他线程能够立即看到发生的状态变化。 由于线程之间的交互都发生在主内存中,但对于变量的修改又发生在自己的工作内存中,经常会造成读写共享变量的错误,我们也叫可见性错误。
使用 volatile解决内存可见性问题(也可解决指令重排序问题
package ThreadSafe;
import java.time.LocalDateTime;
/**
 * 内存可见性问题
 * 
 */
public class ThreadSafe1 {
    private static volatile boolean flag=true;
    public static void main(String[] args) {
        //创建子线程
        Thread t1=new Thread(()->{
            System.out.println("线程1:开始执行"+ LocalDateTime.now());
            while(flag){
            }
            System.out.println("线程1:执行结束"+LocalDateTime.now());
        });
        t1.start();
        Thread t2=new Thread(()->{
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("线程2修改flag=false"+LocalDateTime.now());
            flag=false;
        });
        t2.start();
    }
}
/**
 * 线程1:开始执行2022-04-03T10:40:54.170
 * 线程2修改flag=false2022-04-03T10:40:55.126
 */
    JMM Java Memory Model (JAVA 内存模型 ):
    
    是描述线程之间如何通过内存来进行交互。 具体说来, JVM中存在一个主存区(Main Memory或Java Heap Memory),对于所有线程进行共享,而每个线程又有自己的工作内存(Working Memory),工作内存中保存的是主存中某些变量的拷贝,线程对所有变量的操作并非发生在主存区,而是发生在工作内存中,而线程之间是不能直接相互访问,变量在程序中的传递,是依赖主存来完成的。其⽬的是屏蔽掉各种硬件和操作系统的内存访问差异,以实现让Java程序在各种平台下都能达到⼀致的并发效果.
   
    Java内存模型的抽象示意图如下
    
     
   
- 
指令重排序:编译或运行指令重排序(”jvm自作聪明(优化)“) 编译器优化的本质是调整代码的执⾏顺序,在单线程下没问题,但在多线程下容易出现混乱,从⽽造成 
 
 线程安全问题。
 
   
    
    
    3 解决线程安全问题的手段:
   
    1 使用Volatile解决内存可见性问题和指令重排序问题
    
     
   
- 
代码在写⼊ volatile 修饰的变量的时候: 改变线程⼯作内存中volatile变量副本的值,将改变后的副本的值从⼯作内存刷新到主内存 
- 
代码在读取 volatile 修饰的变量的时候: 从主内存中读取volatile变量的最新值到线程的⼯作内存中,从⼯作内存中读取volatile变量的副本 
- 
缺点: 
 
 volatile 虽然可以解决内存可⻅性和指令重排序的问题,但是解决不了原⼦性问题,因此对于 ++ 和 –操作的线程⾮安全问题依然解决不了
    
    
    4 使用锁是java中解决线程安全问题最主要的手段:
   
- 
内存锁:synchronized 
- 
可重入锁:ReentrantLock 
    
    
    5 synchronized基本用法(非公平锁 互斥锁 可重入锁):
   
1 修饰静态方法:
public class TreadDemo_synchronized {
    private static int number=0;
    static class Counter{
        private static int MAX_COUNT=200000;
        //++方法
        public synchronized static void increment(){
            for (int i = 0; i < MAX_COUNT; i++) {
                number++;
            }
        }
        public synchronized static void decrement(){
            for (int i = 0; i < MAX_COUNT; i++) {
                number--;
            }
        }
        public static int getNumber(){
            return number;
        }
    }
    public static void main(String[] args) throws InterruptedException {
        Thread th=new Thread(()->{
            Counter.increment();
        });
        th.start();
        Thread th1=new Thread(()->{
            Counter.decrement();
        });
        th1.start();
        th.join();th1.join();
        System.out.println("最终结果"+ number);
    }
}
2 修饰普通方法:
package ThreadSafe;
public class TreadDemo_synchronized2 {
    private static int number=0;
    static class Counter{
        private int MAX_COUNT=0;
        public Counter(int MAX_COUNT){
            this.MAX_COUNT=MAX_COUNT;
        }
        //++方法
        public synchronized void increment(){
            for (int i = 0; i < MAX_COUNT; i++) {
                number++;
            }
        }
        public synchronized  void decrement(){
            for (int i = 0; i < MAX_COUNT; i++) {
                number--;
            }
        }
        public  int getNumber(){
            return number;
        }
    }
    public static void main(String[] args) throws InterruptedException {
        Counter c=new Counter(100000);
        Thread th=new Thread(()->{
            c.increment();
        });
        th.start();
        Thread th1=new Thread(()->{
            c.decrement();
        });
        th1.start();
        th.join();th1.join();
        System.out.println("最终结果"+ number);
    }
}
3 修饰代码块:
- 
修饰类:synchronized(类名.class) 
- 
修饰类实例:synchronized(this) 
public class ThreadSynchronized3 {
    private static int number = 0;
 
    static class Counter {
        private static final int count = 100000;
 
        // 自定义锁对象
        final Object myLock = new Object();
 
        // ++方法
        public void increase() {
            for (int i = 0; i < count; i++) {
                synchronized (myLock) {
                    number++;
                }
            }
        }
 
        // --方法
        public void desc() {
            for (int i = 0; i < count; i++) {
                synchronized (myLock) {
                    number--;
                }
            }
        }
    }
 
    public static void main(String[] args) throws InterruptedException {
        Counter counter = new Counter();
        Thread thread1 = new Thread(counter::increase);
        thread1.start();
        Thread thread2 = new Thread(counter::desc);
        thread2.start();
 
        thread1.join();
        thread2.join();
        System.out.println("最终结果:" + number);
    }
}
    注意事项:对于同一个业务的多个线程加锁对象,一定要是
    
     同一个对象加同一把锁
    
   
public class ThreadSynchronized {
    private static final int count = 100000;
    static int num = 0;
 
    public static void main(String[] args) throws InterruptedException {
        Object obj = new Object();
        Object obj2 = new Object();
        Thread t1 = new Thread(() -> {
            synchronized (obj) {
                for (int i = 0; i < count; i++) {
                    num++;
                }
            }
        }, "线程1");
        t1.start();
        Thread t2 = new Thread(() -> {
            synchronized (obj2) {
                for (int i = 0; i < count; i++) {
                    num--;
                }
            }
        }, "线程2");
        t2.start();
        t1.join();
        t2.join();
        System.out.println("最终执⾏结果:" + num);
    }
}
    
    
    6 syntronized特性:
   
1 互斥性(排他性
    synchronized 会起到互斥效果, 某个线程执⾏到某个对象的 synchronized 中时, 其他线程如果也
    
    执⾏到同⼀个对象 synchronized 就会阻塞等待.
   
- 进⼊ synchronized 修饰的代码块, 相当于 加锁
- 退出 synchronized 修饰的代码块, 相当于 解锁
2 刷新内存(内存可见性问题
3 synchronized 的⼯作过程:
- 获得互斥锁
- 从主内存拷⻉变量的最新副本到⼯作的内存
- 执⾏代码
- 将更改后的共享变量的值刷新到主内存
- 释放互斥锁
    4 可重入:
    
    synchronized⽤的锁是存在Java对象头⾥的,里面有是否加锁的标志,以及拥有当前?的id
   
- 查询当前对象头是否加锁
- 判断隐藏对象头中的线程id是否等于当前线程的id
 public class ThreadDemo_synchronized5 {
     public static void main(String[] args) {
         synchronized( ThreadDemo_synchronized5.class){
             System.out.println("当前主线程获得了?");
 a b: (true) synchronized( ThreadDemo_synchronized5.class){
                 System.out.println("当前主线程再次获得?");
             }
         }
     }
 }
    
    
    7 synchronized实现原理:
   
    保证任何时候只有
    
     一个线程能够执行指定区域的代码
    
   
    1 jvm层面依靠
    
     监视器Monitor
    
    实现(使用了一个对象的隐藏对象头 里面的两个a,b属性实现)
   
- 
jdk1.6之前使用比较少(默认重量级锁实现,所以性能较差) 
- 
jdk1.6优化了 
 
 无锁(没有线程)——>偏向锁(一个线程)——>轻量级锁(少量线程)——>重量级锁(较多线程 锁升级)
2 从操作系统的层面实现,基于操作系统的互斥锁(Mutax)
    
    
    8 公平锁和非公平锁的区别:
   
1 公平锁一定要执行的步骤:
- 上一个线程释放线程后执行唤醒操作
- 自旋后 最前面阻塞休眠的线程 从阻塞状态切换为运行状态
2 非公平锁:来得早不如来得巧
    
    
    9 ReentrantLock
   
1 Lock的实现步骤:
// 1.创建锁对象
Lock lock=new ReentrantLock();
// 2.加锁操作
lock.lock();
try{
    //业务代码(可能会非常复杂 -> 导致异常)
    System.out.println("你好! ReentrantLock");
}finally {
    // 3.释放锁
    lock.unlock();
}
2 Lock注意事项:
- 
释放锁的unlock() 操作一定要放在finally里面,若没有放在这里可能会导致永久占用资源问题 
- 
加锁操作lock() 一定要放在try之前,或者try首行 
 
 1. 未加锁却执行了释放锁的操作
 
 2. 释放锁的错误信息会覆盖掉业务代码的报错信息,从而增加调试代码的复杂度
- 
指定Lock类型: - 
       非公平锁:默认会创建非公平锁,性能高
 
 new ReentrantLock()
 
- 
       公平锁:
 
 new ReentrantLock(true)
 
 
- 
       非公平锁:默认会创建非公平锁,性能高
    
    
    10 synchronized VS Lock
   
- Lock 粒度可以更⼩ ,更灵活。有更多方法比如:tryLock();
- 锁类型不同:Lock默认是非公平锁,但可以指定为公平锁;synchronized只能为公平锁
- 调用lock方法和synchronized方法线程等待状态不同;lock -》waiting synchronized-》blocked
- Lock 需要⼿动操作锁,⽽ Synchronized 是 JVM ⾃动操作的
- Lock 只能修饰代码块,⽽ Synchronized 可以修饰⽅法、静态⽅法、代码块
 
