并发与多线程

blog:https://devonmusa.github.io

1 常见概念

1.1 操作系统线程运行状态

    NEW
RUNNABLE
RUNNING
BLOCKED

1.2 Java虚拟机线程实际运行状态

     public enum State {
NEW
尚未开始的线程处于此状态,即刚创建线程对象,未调用start()方法 RUNNABLE
新建线程调用start()方法后,在Java虚拟机中执行的线程处于此状态,但它可能正在等待来自操作系统的其他资源(如处理器)。 BLOCKED
处于阻塞状态的线程正在等待监视器锁定进入同步块或同步方法,或者重入锁进入同步块或同步方法
后调用Object.wait()方法 WAITING
由于调用以下方法之一,线程处于等待状态
Object.wait(); 无超时
Thread.join(); 无超时
LockSupport.park();
例如,a.在对象上调用Object.wait()的线程正在等待另一个线程调用Object.notify()或Object.notifyAll()在该对象。
b.一个名为Thread.join(的线程正在等待指定的线程终止。 TIMED_WAITING
由于调用一个线程,线程处于定时等待状态,使线程处于定时等待状态可调用方法如下:
Thread.sleep(long);
Object.wait(long)
Thread.join(long);
LockSupport.parkNanos(long);
LockSupport.parkUntil(long); TERMINATED
终止线程的线程状态。 即线程已完成当前任务的执行。
}

1.3 重入锁

概念:

    一个线程试图获得它自己持有的锁,那么这个请求就会成功

实现原理:

   1.为每个锁绑定所有者和计数值,每重入一次该计数值自增1,而当线程每退出一次计数值递减,直到计数值为零才释放锁。

常用实现

1.4 sychroinzed

概述:

在JDK1.6之前被称为重量级锁,然后在JDK1.6中为了减少获得和释放锁所带来的性能消耗引入了偏量锁和轻量级锁

实现原理:

    JVM规范中都是给予进入和对出Monitor对象来实现方法同步和代码块同步的,但两者的实现细节不同:
代码块同步是使用monitorenter和monitorexit指令实现的

使用形式:

普通方法+syncronized

锁是当前实例对象

    public  class InstanceObjectSyncronizedTest{
private Integer num = 5; public syncronized Integer getNum(){
num++
return num;
}
}
静态方法+syncronized

锁是当前类的Class对象

    public  class InstanceObjectSyncronizedTest{
private static Integer num = 5; public static synchronized void IntegergetNum() {
++num;
}
}
方法块+syncronized(Object.Class)

锁是当前类的Class对象

 
方法块+syncronized

锁是syncronized括号里配置的对象

 

1.5 lock接口及实现

1.6 volatile

volatile是轻量级的sncronized的实现,只修饰变量,多线程访问不会发生阻塞,但volatile不具备原子性,功能使变量在多线程间可见。
  • 使用方式:

     1)确保自身状态的可见性
    2)确保他们所引用的对象的可见性
  • 应用场景:

     1)标记循环控制状态,检查状态来判断是否退出循环
    2)单线程写,多线程读

1.7 线程池

  • 拒绝策略

    重试策略

    public static class CallerRunsPolicy implements RejectedExecutionHandler {
    /**
    * Creates a {@code CallerRunsPolicy}.
    */
    public CallerRunsPolicy() { } /**
    * Executes task r in the caller's thread, unless the executor
    * has been shut down, in which case the task is discarded.
    *
    * @param r the runnable task requested to be executed
    * @param e the executor attempting to execute this task
    */
    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
    if (!e.isShutdown()) {
    r.run();
    }
    }
    }

    异常抛出策略

    /**
    * A handler for rejected tasks that throws a
    * {@code RejectedExecutionException}.
    */
    public static class AbortPolicy implements RejectedExecutionHandler {
    /**
    * Creates an {@code AbortPolicy}.
    */
    public AbortPolicy() { } /**
    * Always throws RejectedExecutionException.
    *
    * @param r the runnable task requested to be executed
    * @param e the executor attempting to execute this task
    * @throws RejectedExecutionException always
    */
    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
    throw new RejectedExecutionException("Task " + r.toString() +
    " rejected from " +
    e.toString());
    }

    } 旧任务丢弃策略

    /**
    * A handler for rejected tasks that silently discards the
    * rejected task.
    */
    public static class DiscardPolicy implements RejectedExecutionHandler {
    /**
    * Creates a {@code DiscardPolicy}.
    */
    public DiscardPolicy() { } /**
    * Does nothing, which has the effect of discarding task r.
    *
    * @param r the runnable task requested to be executed
    * @param e the executor attempting to execute this task
    */
    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
    }
    }

    当前任务丢弃策略

    /**
    * A handler for rejected tasks that discards the oldest unhandled
    * request and then retries {@code execute}, unless the executor
    * is shut down, in which case the task is discarded.
    */
    public static class DiscardOldestPolicy implements RejectedExecutionHandler {
    /**
    * Creates a {@code DiscardOldestPolicy} for the given executor.
    */
    public DiscardOldestPolicy() { } /**
    * Obtains and ignores the next task that the executor
    * would otherwise execute, if one is immediately available,
    * and then retries execution of task r, unless the executor
    * is shut down, in which case task r is instead discarded.
    *
    * @param r the runnable task requested to be executed
    * @param e the executor attempting to execute this task
    */
    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
    if (!e.isShutdown()) {
    e.getQueue().poll();
    e.execute(r);
    }
    }
    }

2 线程安全模型

2.1 COW(copy on write)

  来源于linux的fork命令,java中的实现类CopyOnWriteArrayList 读操作无需加锁,可能会出现脏数据,遍历时被其他线程修改不会出现并发修改异常(CurrentModificationException)

2.2 CAS(compare and swap)

  即比较与替换被封装为原子操做,ConcurrentHashMap和HashMap都采用数组+链表+红黑数的结构存储数据

2.3 RWS(read write separate)

  即读写分离,LinkedBlockingQueue,用两个可重入锁枷锁head和last

3 应用场景

3.1 多读少写

3.2 多写少读

4 线程安全的类的设计

4.1 判断类是否是线程安全的

  • 找出构成对象状态的所有变量
  • 找出约束状态变量的不变性条件
  • 建立对象状态的并发访问管理策略

原文链接:https://github.com/Devonmusa/demos-parent/blob/master/language/java/doc/并发和多线程.md

Java线程安全详解的更多相关文章

  1. Java线程池详解(二)

    一.前言 在总结了线程池的一些原理及实现细节之后,产出了一篇文章:Java线程池详解(一),后面的(一)是在本文出现之后加上的,而本文就成了(二).因为在写完第一篇关于java线程池的文章之后,越发觉 ...

  2. java 线程状态 详解

    线程被创建后,有一个生命周期,下图是线程的生命周期详解. java api java.lang.Thread.State 这个枚举中给出了六种线程状态,分别是: 线程状态 导致状态发生条件 NEW(新 ...

  3. Java线程池详解

    一.线程池初探 所谓线程池,就是将多个线程放在一个池子里面(所谓池化技术),然后需要线程的时候不是创建一个线程,而是从线程池里面获取一个可用的线程,然后执行我们的任务.线程池的关键在于它为我们管理了多 ...

  4. 【java线程系列】java线程系列之java线程池详解

    一线程池的概念及为何需要线程池: 我们知道当我们自己创建一个线程时如果该线程执行完任务后就进入死亡状态,这样如果我们需要在次使用一个线程时得重新创建一个线程,但是线程的创建是要付出一定的代价的,如果在 ...

  5. Java线程池详解,看这篇就够了!

    构造一个线程池为什么需要几个参数?如果避免线程池出现OOM?Runnable和Callable的区别是什么?本文将对这些问题一一解答,同时还将给出使用线程池的常见场景和代码片段. 基础知识 Execu ...

  6. Java线程池 详解(图解)

    来源:www.jianshu.com/p/098819be088c 拓展: 手动创建 new ThreadPoolExecutor 的使用: https://segmentfault.com/a/11 ...

  7. Java 线程池详解

    Executors创建线程池 Java中创建线程池很简单,只需要调用Executors中相应的便捷方法即可,比如Executors.newFixedThreadPool(int nThreads),但 ...

  8. Java线程池详解(一)

    一.线程池初探 所谓线程池,就是将多个线程放在一个池子里面(所谓池化技术),然后需要线程的时候不是创建一个线程,而是从线程池里面获取一个可用的线程,然后执行我们的任务.线程池的关键在于它为我们管理了多 ...

  9. Java线程学习详解

    线程基础 1. 线程的生命周期 1.1 新建状态: 使用 new 关键字和 Thread 类或其子类建立一个线程对象后,该线程对象就处于新建状态.它保持这个状态直到程序 start() 这个线程. 1 ...

  10. Java线程常用方法详解

    线程的常用方法 1.start() : 线程调用该方法将启动线程,使之从新建状态进入就绪队列排队,一旦轮到它来享用CPU资源时,就可以脱离创建它的线程独立开始自己的生命周期了. 2.run(): Th ...

随机推荐

  1. NoSuchMethodError: Closure call with mismatched arguments:

    原因:某个方法的参数中,回调函数写的有问题,

  2. Seeion相关

    存储会话数据有两种方式: Cookie 1) 存储在浏览器端,通过服务器发送cookie数据 2) 使用cookie存储会话数据,相对不安全(可以cookie查到一些用户安全) 3) 从存储数据类型来 ...

  3. python:修改pdf的书签

    我觉得修改pdf书签总体来说最方便的方式就是: 导出pdf书签为文本文件,修改书签文本文件后再导入到pdf中. 1.直接修改pdf书签 python中比较好用的pdf处理的库是pymupdf: pip ...

  4. 图片转ASCII字符图案的原理(可调整亮度对比度 宽高度)

    来, 先看效果哈哈哈哈! 演示地址: http://ascii-picture.imlht.com/ "\` """ . "\`"" ...

  5. 利用python的PyPDF2和PyMuPDF库玩转PDF的提取、合并、旋转、缩放、加密

    一.安装PyPDF2和PyMuPDF库 pip install PyPDF2 pip install pymupdf # fitz是pymupdf的子模块 二.工具类代码 from PyPDF2 im ...

  6. 如何修改NuGet默认全局包文件夹的位置?

    由于一些历史原因,重装系统成为Windows用户解决疑难杂症的祖传手艺.受此影响,给硬盘分盘几乎成为了一种执念,少则C.D两个盘,夸张一点的5~6个盘的也不是没有. PS:macOS和Linux一直都 ...

  7. TypeScript: 类型别名

    类型别名 在 TYPESCRIPT 中,类型别名可以使用 TYPE 关键字来定义.类型别名可以方便地定义一个类型,并为其起一个易于理解的名称,以便在其他地方引用该类型时使用. 示例 type MySt ...

  8. Unity的IPostprocessBuildWithReport:深入解析与实用案例

    Unity IPostprocessBuildWithReport Unity IPostprocessBuildWithReport是Unity引擎中的一个非常有用的功能,它可以让开发者在构建项目后 ...

  9. python=2.7-not available from current channels

    现象 使用miniconda3创建python2的环境 Collecting package metadata (current_repodata.json): done Solving enviro ...

  10. Docker从入门到部署项目

    Docker概念 Docker是一个开源的应用容器引擎,它是基于Go语言并遵从Apache2.0协议开源.Docker可以让开发者打包他们的应用以及依赖包到一个轻量级.可移植的容器中,然后发布到任何流 ...