【技术笔记-线程1】基本概念、volatile 和 CAS

2020-03-01  本文已影响0人  高大强19

一、线程相关基本概念

区分程序、进程、线程

1.程序是保存在电脑里的一堆代码,静态的。比如,qq安装后,保存在c盘某个文件夹下一些文件。

2.进程是程序运行起来加载在内存中的代码,是程序的载体。比如,双击QQ运行起来后,分配内存以供运行。

3.什么是线程? 是资源的最小单位,一个进程可能一个或多条线程。

三种启动线程的方式:1.Thread 2.Runnable 3.通过线程池来创建:Executors.newCachedThreadPool

不加synchronized 会脏读,业务上允许脏读就不要加,效率会低百千倍。

synchronized 重入锁,必须是, super时,不可重入就死锁了。

程序出现异常,锁释放了。

synchronized 在1.5实现是重量级的。

锁升级概念:

我就是厕所所长(1 2)

markword 记录这个线程ID(偏向锁) 效率高。

如果有线程征用,升级为自旋锁。 用马桶举例,循环转圈玩。默认旋10次。

还没有得到这把锁,升级为重量级锁,去操作系统申请。等待状态,不占cpu了。

锁只能升级,不能降级。

什么情况下,用自旋锁好?

自旋锁,占用cpu但是不占用操作系统,所以是用户态的,不经过内核态。os锁内核态。

执行时间长,线程数多 用系统锁更好些。

执行时间短,线程数少 用自旋锁synchronized 。

synchronized锁的是对象

不加任何,是锁当前this

锁静态是this.xxx.class

锁定方法和非锁定方法可同时执行

锁升级

偏向锁,自旋锁,重量级锁。

synchronized(Object)

锁的Object,不能用String常量,interger,Long

String常量 会导致不同的线程访问同一个对象。

要是实际工程中,直接规定,不能加头,用锁。

二. volatile 和 CAS

保证线程可见性

MESI

缓存一致性协议(cpu)

禁止指令重排序(cpu)

DCL单例

new Thread(t::m, "t1").start();  //

java 的 匿名函数lambda 2种写法:

(Apple a) -> a.getWeight () Apple: :getWeight

() -> Thread. currentThread()。dumpStack() Thread. currentThread() : :dumpStack

(str, i) ->str. subatring(i) String: :subetring

(String s) -> System.out .println(s) System.out :: println

2.1

加了volatile,内存每次都能读到。

单例模式

1. 饿汉式  private + get方法 

初始化后,jvm就帮你初始化

2.调用方法时初始化,懒汉式,带来线程不安全性。

3.线程安全的,在get方法上加synchronized

4.锁细化,先判断线程是否为空再new

5.也是不对的,1 , 2 个线程都判断了,都会初始化。

6.用双重检查,保证线程安全。

超高并发的情况下,如,京东、淘宝的秒杀。会有指令重排序的问题。

加volatile 不允许对这个对象的指令重排序。深追究,jvm的指令重排序,代码级别的,本质是使用cpu的读屏障、写屏障

new 对象过程,分三步,1初始值 2成员变量赋真正初始值 3赋值给栈里变量INSTENCE

volatile不能保证原子性,不能替代synchronized

2.2 锁优化

锁粒度变粗,变细

争论不剧烈的情况下,变细为好。可以使线程争用时间变短,从而提高效率。

当征用锁过多的情况下,锁为粗好。加大锁。

对对象加锁,对象最好加final防止对象变化导致锁无效,异常。

2.2.1 CAS无锁优化,自旋锁

Atomic开头的都是CAS,常见AtomicInteger

例:代码2.2.1

原理:Compare And Set

都是用Unsafe的CompareAndSetxxx,CompareAndExchangexxx等实现的。

cas(v,Expected,NewValue)

先判断是否是我期望的值,不是,说明在我访问中,有人改变了,要重新执行,要不就退出。

思考:假如我判断符合期望,在准备读取的时候有人改变了呢?

cas是cpu原语支持的,指令级别的,不能打断的。

问: 期望值怎么来?比如,列表,往后插,判断索引是否是3. 一般根据业务逻辑判断。

ABA问题,int 、long类型,无所谓,对象就有问题。

解决:加版本号,

A 1.0

B 2.0

A 3.0

AtomicStampedReference 解决(https://www.cnblogs.com/zyrblog/p/9864932.html

了解Unsafe:等同于c c++的指针

典型的单例

8jdk还是 CompareAndSet

11jdk用了弱指针weakCompareAndSetInt,他的好处是垃圾回收的时候效率高。

还有allocateMemory操作指针。。。

分配   释放 分配   释放

c -> molloc free  c++-> new delete

图2.2.1 Unsafe

代码2.2.1

/**

* 解决同样的问题的更高效的方法,使用AtomXXX类

* AtomXXX类本身方法都是原子性的,但不能保证多个方法连续调用是原子性的

* @author mashibing

*/

package com.mashibing.juc.c_018_00_AtomicXXX;

import java.util.ArrayList;

import java.util.List;

import java.util.concurrent.atomic.AtomicInteger;

public class T01_AtomicInteger {

  /*volatile*/ //int count1 = 0;

  AtomicInteger count = new AtomicInteger(0);

  /*synchronized*/ void m() {

      for (int i = 0; i < 10000; i++)

        //if count1.get() < 1000

        count.incrementAndGet(); //count1++

  }

  public static void main(String[] args) {

      T01_AtomicInteger t = new T01_AtomicInteger();

      List<Thread> threads = new ArrayList<Thread>();

      for (int i = 0; i < 10; i++) {

        threads.add(new Thread(t::m, "thread-" + i));

      }

      threads.forEach((o) -> o.start());

      threads.forEach((o) -> {

        try {

            o.join();

        } catch (InterruptedException e) {

            e.printStackTrace();

        }

      });

      System.out.println(t.count);

  }

}

上一篇下一篇

猜你喜欢

热点阅读