Java 并发编程 - 1

Volatile 变量

Volatile 关键字的作用:

  • 确保对声明的变量的操作 不会 和其他内存操作一起被重排序
  • Volatile 变量不会缓存到寄存器或者其他对处理器隐藏的地方(保证可见性)
  • 确保 引用类型, long 和 double的读取或者赋值的原子性

Volatile 的主要用法:

  • 确保他们所引用对象状态的可见性
  • 确保读取或者赋值 long 和 double 类型的原子性
  • 确保标志状态变更的可见性

发布和逸出

发布: 一个对象能够被当前范围外的代码所使用. 比如:

  • 作为参数传递到另外一个方法中
  • 作为返回值返回此对象的引用

逸出: 一个对象还没有准备好, 就被发布. 比如:

  • 构造函数中有个集合类型的参数, 然而在构造函数中用 collection.add(this)this 就被逸出了.

Note: 不要在构造函数中 启动 一个线程. 因为当对象在构造函数中创建了一个线程, 无论是显式的还是隐式的, this引用几乎总是被新线程共享. 构造函数中可以创建一个线程, 但是 不要启动 它!

线程封闭

线程需要同步的原因是: 线程之间要共享数据. 所以避免同步的方式就是 不共享数据, 也叫 线程封闭.

应用池的 JDBC 对象是一个线程封闭的例子, 每个 Connection 对象在归还到池中以前, 总是被某个线程独占的.

Ad-hoc 线程限制

特点: 容易出错, 应该用 栈限制 和 ThreadLocal 取代.

栈限制

本地变量被限制在执行线程里, 他们存在于 执行线程栈, 其他线程无法方位这个栈.

Note: 确保本地变量不要被逸出!

ThreadLocal

ThreadLocal 可以将多个对象绑定在执行线程中, ThreadLocal 提供了 get 和 set 方法, 为每个使用它的线程维护一份单独的拷贝.

ThreadLocal 常用在:

  1. 防止在基于可变的单体或全局变量的设计中, 出现不正确的共享. 比如 SimpleDateFormatter 和 Connection
  2. 一个频繁执行的操作既需要 buffer 这样的临时对象, 还需要避免每次都重分配该临时对象.

ThreadLocal: Every thread has a threadLocals map structure to store value related on the thread.

When calling ThreadLocal.get():

  1. ThreadLocal firstly try to get the threadLocals map structure:

    • if the map is null, then create the map and put a (this, initialValue) into the map, then return the value
    • if the map is not null, then try to get value from the map
      • if the value is null, then insert the initialValue into the map (this, initialValue), then return the value
      • if the value is not null, return the value

不可变对象

发布对象依赖于对象的可变性:

  1. 不可变对象可以通过任意机制发布
  2. 高效不可变对象必须要安全发布
  3. 可变对象必须要安全发布, 同时必须要线程安全或者被锁保护

不可变对象:

不可变对象是那些构造后状态无法被改变的对象. 由于它的不可变性, 他不会有 Thread interference 和 Memory inconsistent 等问题.

不可变对象的特征:

  1. 不要设置 setter 方法.
  2. 所有的成员都设置成 final + private
  3. 不允许子类重写方法. 简单的方法是将类声明前 + final
  4. 如果实例变量中有引用类型, 别让他们被更改:
    • 不要提供更改他们的方法
    • 不要共享他们的引用, 包括
      • 不要在构造函数中直接引用参数
      • 不要将实例引用变量返回, 如果不得不, 则, 返回拷贝!

不可变对象永远是线程安全的. 它只有一种状态, 无法被更改. 不可变对象有如下特征:

  1. 被正确创建(创建期间没有发生 this 引用逸出)
  2. 所有域都是 final 类型
  3. 状态不能在创建后再被修改

NOTE: 如果 final域指向可变对象, 那么访问这些对象的状态时仍然需要同步.

如何保证一个 不可变对象 的可见性?

  1. 静态初始化器初始化对象的引用
  2. 将它的引用存储到 volatile 域或者 AtomicReference
  3. 将它的引用存储到正确创建的对象的 final 域中
  4. 它的引用存储在由锁正确保护的域中

有效不可变对象

一个对象在技术上不是不可变的, 但是它的状态不会在发布后被修改, 这样的对象称作有效不可变对象.

任何线程都可以在没有额外的同步下安全地使用一个安全发布的高效不可变对象.

可变对象

安全发布仅仅可以保证"发布当时"状态的可见性. 但是之后状态的改变需要线程同步来保证.

时间: 2024-10-15 00:54:32

Java 并发编程 - 1的相关文章

Java并发编程:Concurrent锁机制解析

.title { text-align: center } .todo { font-family: monospace; color: red } .done { color: green } .tag { background-color: #eee; font-family: monospace; padding: 2px; font-size: 80%; font-weight: normal } .timestamp { color: #bebebe } .timestamp-kwd

Java并发编程:Callable、Future和FutureTask(转)

Java并发编程:Callable.Future和FutureTask 在前面的文章中我们讲述了创建线程的2种方式,一种是直接继承Thread,另外一种就是实现Runnable接口. 这2种方式都有一个缺陷就是:在执行完任务之后无法获取执行结果. 如果需要获取执行结果,就必须通过共享变量或者使用线程通信的方式来达到效果,这样使用起来就比较麻烦. 而自从Java 1.5开始,就提供了Callable和Future,通过它们可以在任务执行完毕之后得到任务执行结果. 今天我们就来讨论一下Callabl

Java并发编程 Volatile关键字解析

volatile关键字的两层语义 一旦一个共享变量(类的成员变量.类的静态成员变量)被volatile修饰之后,那么就具备了两层语义: 1)保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的. 2)禁止进行指令重排序. 根据volatile的语义,我们可以看到,volatile主要针对的是并发三要素(原子性,可见性和有序性)中的后两者有实际优化作用. 可见性: 线程本身并不直接与主内存进行数据的交互,而是通过线程的工作内存来完成相应的操作.

Java并发编程

synchronized是Java中的关键字,在并发编程中被称为内置锁或者监视器锁.当用它来修饰一个方法或者一个代码块的时候能够保证同一时刻最多只有一个线程执行该段代码. Java的内置锁相当于一种互斥锁,最多只有一个线程能够持有这种锁,故而由这个锁保护的同步代码块会以原子方式执行,多个线程在执行该代码时就不会相互干扰. 但由于被锁保护的同步块代码是以串行形式来访问的,即多个线程以独占的方式访问对象,而这也导致如果被锁保护的同步代码块的作用范围过大,会导致并发不良. 这里有必要简单讲一下内置锁的

6、Java并发编程:volatile关键字解析

Java并发编程:volatile关键字解析 volatile这个关键字可能很多朋友都听说过,或许也都用过.在Java 5之前,它是一个备受争议的关键字,因为在程序中使用它往往会导致出人意料的结果.在Java 5之后,volatile关键字才得以重获生机. volatile关键字虽然从字面上理解起来比较简单,但是要用好不是一件容易的事情.由于volatile关键字是与Java的内存模型有关的,因此在讲述volatile关键之前,我们先来了解一下与内存模型相关的概念和知识,然后分析了volatil

7、Java并发编程:深入剖析ThreadLocal

Java并发编程:深入剖析ThreadLocal 想必很多朋友对ThreadLocal并不陌生,今天我们就来一起探讨下ThreadLocal的使用方法和实现原理.首先,本文先谈一下对ThreadLocal的理解,然后根据ThreadLocal类的源码分析了其实现原理和使用需要注意的地方,最后给出了两个应用场景. 以下是本文目录大纲: 一.对ThreadLocal的理解 二.深入解析ThreadLocal类 三.ThreadLocal的应用场景 若有不正之处请多多谅解,并欢迎批评指正. 请尊重作者

java并发编程实战学习(3)--基础构建模块

转自:java并发编程实战 5.3阻塞队列和生产者-消费者模式 BlockingQueue阻塞队列提供可阻塞的put和take方法,以及支持定时的offer和poll方法.如果队列已经满了,那么put方法将阻塞直到空间可用:如果队列为空,那么take方法将阻塞直到有元素可用.队列可以是有界的也可以是无界的. 如果生产者生成工作的速率比消费者处理工作的速率款,那么工作项会在队列中累计起来,最终好紧内存.同样,put方法的阻塞特性也极大地简化了生产者的编码.如果使用有界队列,当队列充满时,生产者将阻

Java 并发编程之任务取消(九)

Jvm关闭 jvm可正常关闭也可强行关闭,正常关闭有多种触发方式: 当最后一个正常(非守护,下面会讲到什么是守护线程)线程结束时 当调用system.exit时,或者通过其他特定于平台的方法关闭时(例如发送了SIGINT信号或键入Ctrl-c) 通过其他特定平台的方法关闭jvm,调用Runtime.halt或者在操作系统当中杀死JVM进程(例如发送sigkill)来强行关闭jvm. 关闭钩子 在正常关闭中,jvm首先调用所有已注册的关闭钩子,关闭钩子是指通过 Runtime.addShutdow

Java并发编程笔记 并发概览

并发概览 >>同步 如何同步多个线程对共享资源的访问是多线程编程中最基本的问题之一.当多个线程并发访问共享数据时会出现数据处于计算中间状态或者不一致的问题,从而影响到程序的正确运行.我们通常把这种情况叫做竞争条件(race condition),把并发访问共享数据的代码叫做关键区域(critical section).同步就是使得多个线程顺序进入关键区域从而避免竞争条件的发生. >>线程安全性 编写线程安全的代码的核心是要对状态访问操作进行管理,尤其是对共享的和可变的状态访问. 线

Java并发编程:锁的释放

.title { text-align: center } .todo { font-family: monospace; color: red } .done { color: green } .tag { background-color: #eee; font-family: monospace; padding: 2px; font-size: 80%; font-weight: normal } .timestamp { color: #bebebe } .timestamp-kwd