多线程

创建线程的两种方式

  1. 从Thread类继承, 重写run方法
    启动方法: 直接调用对象的start方法
  2. 实现Runable接口, 重写run方法
    new Thead(?.class).start
  3. (可能算是第三种) Executors创建线程池

Thread中的关键字

  1. sleep - 单纯的睡眠
  2. yield - 重新回到线程的等待队列, 但如果没有其他线程执行, 则任有可能自己继续执行
  3. join - t1调用了t2.join, 意味着他要等待t2的结束


    image.png

Synchronized 关键字

特性:可见性和原子性、
是把重入锁(指的是子类可以调用父类的加锁方法)
、不公平锁、悲观锁

锁升级概念

JDK早期是重量级锁, 需要向OS申请锁
后来的改进
锁升级的概念:
sync(obj)

  1. 偏向锁 -- 对象的head中 markword 记录这个线程的ID (偏向锁), 如果只有一个线程访问, 则不会加锁
  2. 自旋锁 -- 如果有其他线程争用, 则升级为自旋锁
  3. 重量级锁 -- 自旋10次之后, 还拿不到锁, 则升级为重量级锁, 向OS申请锁

执行时间短(加锁代码), 线程数少, 用自旋锁 -- Automic
执行时间长, 线程数多, 用系统锁 -- synchronized

重点知识

一.synchronized锁定的是对象

可以使用synchronized(this){}锁定代码块
可以在方法的返回类型前加上synchronized(如public synchronized void m() {}),等同于上面
优化,可以锁定在代码块上就不要锁定在方法上,也就是采用细粒度的锁,效率会提高

二. 锁定某对象o,如果o的属性发生改变,不影响锁的使用

但是如果o变成了另外一个对象,则锁定的对象发生改变
应该避免将锁定对象的引用将变成另外的对象
t.o = new Object();//锁对象发生改变,所以t2线程得以执行,如果注释掉这句话,线程2将永远得不到执行的机会

三. 加上static,锁的是class对象

//这里等同于

    synchronized (com.company.highthread.t004.TestSynchronized.class)
    public synchronized static void m() {}

public static void mm() {
        //这里不能用this,因为静态的属性没有this,不需要new出对象访问
        synchronized (TestSynchronized.class) {
            count--;
        }
    }

四. 一个方法有锁和一个方法没锁的问题

在一个类中有一个方法加了synchronized,而另一个方法没加,当有线程锁定了对象,但是其他线程还是能访问不加锁的方法

五. 脏读问题

  • 对业务写方法加锁
  • 对业务读方法不加锁
  • 容易产生脏读问题(dirtyRead)
    解决办法是在读方法上也加synchronized,但是要具体业务具体分析,有的业务可能允许脏读

六. 重入锁

1、一个同步方法可以调用另一个同步方法,一个线程已经拥有某个对象的锁,再次申请的时候仍然会得到该对象的锁
也就是说synchronized是可重入的
2、这里是继承中可能发生的情形,子类调用父类的同步方法

七. 异常导致锁释放问题

  • 程序在执行过程中,如果出现异常,默认情况锁会被释放
  • 所以,在一个web app处理过程中,多个servlet线程共同访问同一个资源,如果异常处理不合适
  • 在第一个异常中抛出异常,其他线程就会进入同步代码区,有可能会访问到异常产生时的数据
  • 因此要非常小心的处理同步业务中的异常

八. 字符串常量加锁问题

  • 不要以字符串常量作为锁定对象, Integer, Long等也不要用
  • 在下面的例子中,m1和m2其实锁定的是同一个对象
public class T {
    String t1 = "Hello";
    String t2 = "Hello";
    void m1() {
        synchronized (t1){ }
    }
    void m2() {
        synchronized (t2){ }
    }
}

volatile关键字

  • 保证线程可见性
    • MESI
    • 缓存一致性协议(硬件实现)
  • 禁止指令重排序
    • 单例模式双重检查锁, instance变量需要加上volatile

volatile保证了可见性但不保证原子性

CAS (无锁优化 自旋)

  • Compare And Swap
  • cas(v, Expected, newValue)
    • if v == e 如果现在的值等于我期望的值
    • 则 v = newValue
    • 否则 try again or fail
  • cpu原语支持
  • ABA问题
    • 加veriosn
    • A 1.0
    • B 2.0
    • C 3.0
    • 如果是基础类型, 无所谓, 如果是引用类型, 你的女朋友找你复合, 她还是她, 但是她经历了什么你不知道...
  • Unsafe.class (单例的, 方法都是native的)
    所有automic类都是调用了Unsafe的compareAndSwap方法

你可能感兴趣的:(多线程)