线程学习笔记

122 阅读3分钟

多线程三大特性
原子性,可见性,有序性

线程安全:当多个线程访问某个类时,不管运行时环境采用何种调度方式或者这些进程将如何交替执行,并且在主调代码中不需要任何额外的同步或协调,这个类都表现出正确的行为,那么就称为这个类是线程安全的

原子性(加锁)
synchronized 依赖jvm
修饰代码块时,作用于调用的对象,范围是大括号的代码
修饰方法时,作用于调用的对象,范围是整个方法
修饰静态方法时,作用于所有对象,范围是整个静态方法
修饰类,作用于所有对象,范围是括号括起来的部分
synchornized是不可中断锁
Lock是可中断锁

可见性
导致共享变量在线程间不可见的原因:

  • 线程交叉执行
  • 重排序结合线程交叉执行
  • 共享变量更新后没有在工作内存与主内存之间及时更新

volatile的可见性通过加入内存屏障和禁止重排序优化来实现的

  • 对volatile变量写操作时,会在写操作后加入一条store屏障指令,将本地内存中的共享变量刷新到主内存
  • 对volatile变量读操作时,会在读操作前加入一条load屏障指令,从主内存中读取共享变量

有序性
Java内存模型中,允许编译器和处理器对指令进行重排序,但是重排序过程不会影响到单线程程序的执行,却会影响到并发执行的正确性

原子性:atomic包,CAS算法,synchronized,lock
可见性:synchornized,volatile
有序性: happens-before

CPU的多级缓存
意义:

  • 时间局限性:如果某个数据被访问,那么不久将来他很可能会被再次访问
  • 看见局限性:如果某个数据被访问,那么他相邻的数据很快也可能被访问

Java内存模型

  • Lock 作用于主内存的变量,把一个变量标识为一条线程独占状态
  • unlock :作用于主内存的变量,把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定
  • read 作用于主内存的变量 把一个变量值从主内存传输到线程的主内存中,以便随后的load动作使用
  • load 作用于主内存 的变量 他把read操作从主内存中得到的变量值放入工作内存的变量副本中
  • use 作用于工作内存的变量 把工作内存中的一个变量值传递给执行引擎
  • assign 作用于工作内存的变量 他把一个从执行引擎接收到的值赋给工作内存的变量
  • store 作用于工作内存的变量 把工作内存中的一个变量的值传送到主内存中 以便以后的write操作
  • write 作用于主内存的变量 他把store操作从工作内存中一个变量的值传送到主内存的变量中

死锁 争夺资源,互相等待
四个必要条件

  • 互斥
  • 请求和保持
  • 环路等待
  • 不可剥夺条件

demo:

package cn.learnbyheart.deadLock;

public class DeadLock implements Runnable{
    public int flag = 1;
    private static Object o1 = new Object(),o2 = new Object();
    public static void main(String[] args) {
        DeadLock lock1 = new DeadLock();
        DeadLock lock2 = new DeadLock();
        lock1.flag = 1;
        lock2.flag = 0;
        new Thread(lock1).start();
        new Thread(lock2).start();
    }

    @Override
    public void run() {
        System.out.println(flag);
        if(flag == 1){
            synchronized (o1){
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (o2){
                    System.out.println("1");
                }
            }
        }
        if(flag == 0){
            synchronized (o2){
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (o1){
                    System.out.println("0");
                }
            }
        }
    }
}

多线程并发最佳实践

  • 使用本地变量
  • 使用不可变类
  • 最小化锁的作用域范围
  • 使用Executor而不是new Thread
  • 少用wait和Notify
  • 使用blockingQueue实现生产消费模式
  • 使用并发集合,而不是加了锁的同步集合(Collections.synchornizedXXX)
  • 使用semaphore创建有界的访问
  • 宁可使用同步代码块,也不要用同步方法
  • 避免使用静态变量(最好用final型或者只读集合)