JAVA内存模型(JMM) 为了保证共享内存的正确性(可见性、有序性、原子性)内存模型中定义了共享内存系统中多线程程序读写操作的行为规范 JMM : 一种符合内存模型结构,屏蔽了各种硬件和操作系统的访问差异,保证了java程序在各种平台下对内存的访问都能保证效果一致的机制规范
限制处理器优化(重排序)使用内存屏障原子性:(处理器优化) 指一个操作中CPU不可以在中途暂停在调度,不被中断操作,要不执行完成,要不就不执行。 可见性: (缓存一致性问题) 多个线程访问同一个变量时,一个线程如果修改了这个变量的值,其他线程能够立即看到修改后的值 有序性:(指令重排序问题) 程序执行的顺序按照代码的先后顺序来执行
jmm中,如果一个操作执行的结果需要对另一个操作可见,这两个操作之间必须要存在happens-before关系,这两个操作既可以是一个线程,也可以是两个线程。
规则:
1.程序顺序规则:对于单个线程中的每个操作,前继操作hanpens-before于该线程中的任意后续操作。 2.监视器锁规则 : 对于一个锁的解锁,happens-before于随后对这个锁的加锁。 3.volatile变量规则:对于一个volatile域的写,happens-before对这个volatile域的读。 4.传递性:如果A happens-before B 且 Bhappens-before C,A happens-before C.
不管怎么重排序,单线程程序执行的结果不能被改变,编译器、处理器、和runtime都必须要遵循as-if-serial语义。编译器和处理器不会对存在数据一览关系的操作重排序,因为会改变执行结果。
double pi = 3.14 ; //A double r = 1.0 ; //B double area = pi * r * r ; //c A happens-before B ; B happens-before C; A happens-before C计算机内存模型和Java内存模型
存在线程安全的问题原因:
1.存在共享数据 2.多线程共同操作共享数据
synchronized :排它锁。
解决多个线程之间访问资源的同步性,被他修饰的方法或代码块在任意一个时间段只能有一个线程执行。 实现原理: java 对象头和 monitor是实现sychronized 的基础。 作用范围: 普通方法 静态方法 同步代码块
1.由JVM提供的最轻量级的同步机制,被volatile修饰的变量保证对所有的线程可见,用volatile修饰的变量,线程在每次使用变量的时候,都会读取被修改后最新的值。 2.禁止指令重排序
和sychronized之间的比较
1.volatile 是线程不同的轻量级实现。性能比sychronized要好 2.volatile只能修饰变量,而synchronized可以修饰方法、代码块 3.多线程访问volatile不会发生阻塞,而synchronized会阻塞 4.volatile保证数据的可见性,但是不能保证数据的原子性 5.volatile解决的是变量在多个线程之间的可见性,而synchronized关键字解决的是多个线程之间访问资源的同步性。
文件 读和写是互斥 写和写互斥 读和读可以同时进行。
Lock 和sychronized比较
synchronized是一个关键字,Lock是一个接口sychronized 不用手动释放锁(1.执行完同步代码 2.抛出异常释放),使用Lock必须手动释放,一般放到finally里面保证一定释放。Lock在等待过程中可以用Interrupt来中断等待,而synchronized 只能改等待释放锁,不能响应中断Lock可以通过tryLock来知道有没有获取到锁,而synchronized不能判断有没有获取到锁。Lock可以提高多个线程的读写操作效率(ReadWriteLock)。从性能上来说,在竞争不激烈的情况下,两者的性能差不多,如果竞争比较激烈的时候Lock比synchronized的性能更好一点。synchronized实现线程的通信使用Object类中的wait/notify/notifyAll调度机制,Lock可以使用Condition进行线程之间的调度。Lock 源码:
lock():用来获取锁,如果锁已被其他线程获取,则进行等待。 lockInterruptibly():当通过这个方法获取到锁的时候,如果线程正在等待获取锁,则这个线程能够响应中断
eg:lock.lockInterruptibly() threadA threadB —>等待 —> threadB.Interruptibly()能够中断threadB等待过程。
tryLock():方法有返回值,表示尝试获取锁,如果获取成功,返回true,如果获取失败,则返回false(在获取不到锁的时候不会一直处在等待的状态) unlock():释放锁。
锁的分类:
公平锁和非公平锁(ReentrantLock/synchronized) 可重入锁和不可重入锁 独享锁和共享锁 乐观锁和悲观锁 分段锁
AQS:
AbstrackQueueSynchronizer ,抽象队列同步器。java.util.concurrent.locks,一个底层同步工具类
AQS底层内容:
state线程标记(哪一个线程加的锁)阻塞队列(用于存放阻塞线程)1.怎么表示锁有没有线程获得? 状态: int state属性 1:占有 0:空闲 2.竞争失败的线程是怎么处理的? 队列:以链表的形式实现。 3.CAS (比较并交换)
AQS源码分析:
public class ReentrantLockTest { static Lock lock = new ReentrantLock(true); //创建一把公平锁 public static void main(String[] args) { for (int i = 0; i < 5; i++) { new Thread(new MyThread(i)).start(); } } static class MyThread implements Runnable { int id; public MyThread(int id) { this.id = id; } @Override public void run() { try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } for (int i = 0; i < 2; i++) { lock.lock(); System.out.println("获取锁的线程:--》" + Thread.currentThread().getName() + "--:" + id); lock.unlock(); } } } }公平锁:
final void lock() { acquire(1); } // 尝试去获取锁 public final void acquire(int arg) { // 获取不到锁,请求进入等待队列。 if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) //线程中断 selfInterrupt(); } // protected final boolean tryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); //锁未占用 if (c == 0) { //先判断是否要在等待队列中排队。然后通过CAS更改锁标记,设置当前线程未锁占有线程 if (!hasQueuedPredecessors() && compareAndSetState(0, acquires)) { setExclusiveOwnerThread(current); return true; } } // 锁被占有。判断当前线程是否未锁获得线程。 else if (current == getExclusiveOwnerThread()) { //锁重入。 int nextc = c + acquires; if (nextc < 0) throw new Error("Maximum lock count exceeded"); setState(nextc); return true; } return false; } }非公平锁:
先尝试获取锁,如果获取不到锁会进入等待队列。
Condition();接口
通过condition 的await() 和 signal()来实现线程间通信。 底层是通过 LockSupport.park(node.thread) 和 LockSupport.unpark(node.thread) 来实现;
package com.weido.test.AQS; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantReadWriteLock; public class ConditionTest { private ReentrantLock lock = new ReentrantLock(); private Condition p = lock.newCondition(); private Condition c = lock.newCondition(); private int num = 0; public void createProduct() { lock.lock(); try { while (true) { if (num < 10) { num++; System.out.println("生产者生产了一个产品,已有产品:" + num); TimeUnit.SECONDS.sleep(1); } else { try { c.signal(); p.await(); } catch (InterruptedException e) { e.printStackTrace(); } } } } catch (Exception e) { } finally { lock.unlock(); } } public void customProduct() { lock.lock(); try { while (true) { if (num == 0) { p.signal(); c.await(); } num--; System.out.println("客户消费了一个产品,还剩下:" + num); TimeUnit.SECONDS.sleep(1); } } catch (Exception e) { } finally { lock.unlock(); } } public static void main(String[] args) { ConditionTest con = new ConditionTest(); new Thread(new Runnable() { @Override public void run() { con.createProduct(); } },"productor").start(); new Thread(new Runnable() { @Override public void run() { con.customProduct(); } },"constomer").start(); } }是一种线程阻塞工具,它可以在线程内任意位置让线程阻塞 底层依赖Unsafe 类 设计思路: 为每一个线程设置一个permit(许可,一个值,类似于AQS state) 两个取值: 0、1; park():阻塞 ----》permit ->0 unpark():解除阻塞 ---->permit ->1
线程安全 先进先出(FIFO) 添加元素: ----》尾部 获取元素:—》头部 head (头节点)+tail (尾节点) Node(节点) = item(元素)+next(指向下一个节点的引用)
public boolean add(E e) { return offer(e); }