From f2ca6e88705f238820defdd9a5b5f9b525737830 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=B6=E5=AD=90?= Date: Wed, 25 Dec 2019 15:42:58 +0800 Subject: [PATCH 01/11] AQS-009 --- week_03/09/AQS-009.md | 257 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 257 insertions(+) create mode 100644 week_03/09/AQS-009.md diff --git a/week_03/09/AQS-009.md b/week_03/09/AQS-009.md new file mode 100644 index 0000000..0fd1e43 --- /dev/null +++ b/week_03/09/AQS-009.md @@ -0,0 +1,257 @@ +###AQS + +1、认识 + + 1)AbstractQueuedSynchronizer即队列同步器在java.util.concurrent.locks包下 + 它提供了一种实现阻塞锁和一系列依赖FIFO等待队列的同步器的框架,ReentrantLock、Semaphore、CountDownLatch、CyclicBarrier等并发类均是基于AQS来实现的。 + 2)主要属性: + 这几个变量都要使用volatile关键字来修饰,因为是在多线程环境下操作,要保证它们的值修改之后对其它线程立即可见 + +```java +//队列尾 +private transient volatile Node tail; +//队列头 +private transient volatile Node head; +//控制锁的状态量 +private volatile int state; + +``` +state属性的操作方法: + +```java + +//返回当前锁状态 +protected final int getState() { + return state; +} + +//set锁状态 +protected final void setState(int newState) { + state = newState; +} + +//依赖unsafe实现原子操作 +protected final boolean compareAndSetState(int expect, int update) { + // See below for intrinsics setup to support this + return unsafe.compareAndSwapInt(this, stateOffset, expect, update); +} + +``` + +2、主要内部类 + +```java + +static final class Node { + /**标识一个节点是共享模式*/ + static final Node SHARED = new Node(); + /**标识一个节点是独占模式*/ + static final Node EXCLUSIVE = null; + + /**等待线程取消*/ + static final int CANCELLED = 1; + /**表示需要唤醒后续线程 */ + static final int SIGNAL = -1; + /**线程等待触发条件*/ + static final int CONDITION = -2; + /** + * 表征下一个acquireShared应无条件传播(共享锁需要连续唤醒读的线程)) + */ + static final int PROPAGATE = -3; + /**表示当前节点线程保存的状态*/ + volatile int waitStatus; + /**前一个节点*/ + volatile Node prev; + /**后一个节点*/ + volatile Node next; + /**当前节点保存的线程*/ + volatile Thread thread; + //下一个等待在条件上的节点(Condition锁时使用) + Node nextWaiter; + + /** + * 如果当前节点在共享模式则返回true + */ + final boolean isShared() { + return nextWaiter == SHARED; + } + + /** + * 获取前一个节点 + */ + final Node predecessor() throws NullPointerException { + Node p = prev; + if (p == null) + throw new NullPointerException(); + else + return p; + } + //SHARED模式下的节点构造方法 + Node() { // Used to establish initial head or SHARED marker + } + //addWaiter下节点构造方法 + Node(Thread thread, Node mode) { // Used by addWaiter + this.nextWaiter = mode; + this.thread = thread; + } + //Condition下的构造方法 + Node(Thread thread, int waitStatus) { // Used by Condition + this.waitStatus = waitStatus; + this.thread = thread; + } + +``` + + +3、 其他方法 + +addWaiter + +```java +//独占模式(Exclusive)下的获取/释放资源 +private Node addWaiter(Node mode) { + Node node = new Node(Thread.currentThread(), mode); + // 尝试将节点快速插入等待队列,若失败则执行常规插入(enq方法) + Node pred = tail; + if (pred != null) { + node.prev = pred; + if (compareAndSetTail(pred, node)) { + pred.next = node; + return node; + } + } + // 常规插入 + enq(node); + return node; +} + +//常规插入是自旋过程(for(;;)),能够保证节点插入成功; +//比快速插入多包含了1种情况,即当前等待队列为空时,需要初始化队列,即将待插入节点设置为头结点,同时为尾节点(因为只有一个嘛) +private Node enq(final Node node) { + for (;;) { + Node t = tail; + if (t == null) { // Must initialize + if (compareAndSetHead(new Node())) + tail = head; + } else { + node.prev = t; + if (compareAndSetTail(t, node)) { + t.next = node; + return t; + } + } + } +} + +``` + +子类需要实现一个同步器的方法: + +```java +//互斥模式下使用:尝试获取锁 +protected boolean tryAcquire(int arg) { + throw new UnsupportedOperationException(); +} +//互斥模式下使用:释放锁 +protected boolean tryRelease(int arg) { + throw new UnsupportedOperationException(); +} +//共享模式下使用:尝试获取锁 +protected int tryAcquireShared(int arg) { + throw new UnsupportedOperationException(); +} +//共享模式下使用:尝试释放锁 +protected boolean tryReleaseShared(int arg) { + throw new UnsupportedOperationException(); +} +// 如果当前线程独占着锁,返回true +protected boolean isHeldExclusively() { + throw new UnsupportedOperationException(); +} +``` + + +自己动手写个基于AQS锁(demo来源微信公众号:彤哥读源码) +```java + +public class AQSTest { + //新建一个同步器 + public static class AQS extends AbstractQueuedSynchronizer{ + //获得锁 + @Override + protected boolean tryAcquire(int arg) { + if(compareAndSetState(0,1)){ + setExclusiveOwnerThread(Thread.currentThread()); + return true; + } + return false; + } + //释放锁 + @Override + protected boolean tryRelease(int arg) { + setExclusiveOwnerThread(null); + setState(0); + return true; + } + } + + //声明一个同步器 + private final AQS aqs = new AQS(); + + //加锁 + public void lock(){ + aqs.acquire(1); + } + + //释放锁 + public void unlock(){ + aqs.release(1); + } + + static int count = 0; + + public static void main(String[] args) throws InterruptedException { + AQSTest lock = new AQSTest(); + //jdk1.5之后使一个线程等待其他线程各自执行完毕后再执行 + CountDownLatch countDownLatch = new CountDownLatch(1000); + + IntStream.range(0,1000).forEach(i -> new Thread(() -> { + lock.lock(); + try{ + + IntStream.range(0,10000).forEach(j -> { + count++; + }); + }catch (Exception e){ + + }finally { + lock.unlock(); + } + System.out.println(Thread.currentThread().getName()); + //将count值减1 + countDownLatch.countDown(); + + },"tt-"+i).start()); + //调用await()方法的线程会被挂起,它会等待直到count值为0才继续执行 + countDownLatch.await(); + System.out.println("count :"+count); + +} + +} +``` + +4、总结 + + 本文主要介绍了AQS在独占和共享两种模式下,如何进行资源的获取和释放(tryAcquire-tryRelease、tryAcquireShared-tryReleaseShared); + (1)AQS是Java中几乎所有锁和同步器的一个基础框架,这里说的是“几乎”,因为有极个别确实没有通过AQS来实现; + (2)AQS中维护了一个队列,这个队列使用双链表实现,用于保存等待锁排队的线程; + (3)AQS中维护了一个状态变量,控制这个状态变量就可以实现加锁解锁操作了; + (4)基于AQS自己动手写一个锁非常简单,只需要实现AQS的几个方法即可。 + + + + + + + -- Gitee From 0f569ac95077ea364b74232cf5ca026b3af52f5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=B6=E5=AD=90?= Date: Thu, 26 Dec 2019 14:34:55 +0800 Subject: [PATCH 02/11] ReentrantLock-009 --- week_03/09/ReentrantLock-009.md | 358 ++++++++++++++++++++++++++++++++ 1 file changed, 358 insertions(+) create mode 100644 week_03/09/ReentrantLock-009.md diff --git a/week_03/09/ReentrantLock-009.md b/week_03/09/ReentrantLock-009.md new file mode 100644 index 0000000..ab1648c --- /dev/null +++ b/week_03/09/ReentrantLock-009.md @@ -0,0 +1,358 @@ +###ReentrantLock + +1、认识 + +ReentrantLock,重入锁,此外synchronized也是重入锁,那什么是重入锁呢? + + 重入锁 :是指一个线程获取锁之后再尝试获取锁时会自动获取锁 + + +ReentrantLock实现了lock 和java.io.Serializable俩类接口 + +```java +/** + *lock 接口 + */ + //加锁 + void lock(); + //获取锁(如果锁是空闲的,立即获得,反之,则被中断) +void lockInterruptibly() throws InterruptedException; +// 尝试获取锁,如果没获取到锁,就返回false +boolean tryLock(); +// 尝试获取锁,如果没获取到锁,就等待一段时间,这段时间内还没获取到锁就返回false +boolean tryLock(long time, TimeUnit unit) throws InterruptedException; +//释放锁 +void unlock(); +//条件锁 +Condition newCondition(); + +``` + +主要属性 + +在初始化的时候给该属性赋值代表公平锁 or 非公平锁 + +```java + private final Sync sync; +``` +构造方法 + +1)默认构造器 (默认非公平锁) +```java + +public ReentrantLock() { + sync = new NonfairSync(); +} +``` + +2)自己可选择使用公平锁还是非公平锁 + +```java +public ReentrantLock(boolean fair) { + sync = fair ? new FairSync() : new NonfairSync(); +} + +``` + +2、主要内部类 + +主要有三个内部类 Sync、NonfairSync、FairSync + +```java +//实现AQS的部分方法 +abstract static class Sync extends AbstractQueuedSynchronizer{} +//非公平锁 +static final class NonfairSync extends Sync{} +//公平锁 +static final class FairSync extends Sync {} + +``` + +3、公平锁 + +假如我们创建一个公平锁,对其进行加锁,源码分析如下: + +```java +//先创建一个公平锁 +ReentrantLock reentrantLock = new ReentrantLock(true) + +reentrantLock.lock(); + +//进入FairSync +final void lock() { + //调用AQS + acquire(1); +} + +public final void acquire(int arg) { + // 尝试获取锁 + // 如果失败了,就排队 + if (!tryAcquire(arg) && + //采用独占模式 调用acquireQueued()再次尝试获取锁,如果成功了,直接返回 + acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) + selfInterrupt(); +} + +//AbstractQueuedSynchronizer.addWaiter分析见AQS--009 + +//继续看ReentrantLock 公平锁实现tryAcquire 获得锁 +protected final boolean tryAcquire(int acquires) { + final Thread current = Thread.currentThread(); + //获取当前状态变量的值 + int c = getState(); + //如果为0,则暂时还没有人占有锁 + if (c == 0) { + if (!hasQueuedPredecessors() && + compareAndSetState(0, acquires)) { + // 当前线程获取了锁,把自己设置到exclusiveOwnerThread变量中 + // exclusiveOwnerThread是AQS的父类AbstractOwnableSynchronizer中提供的变量 + setExclusiveOwnerThread(current); + //返回true说明拿到锁 + return true; + } + } + //如果当前线程的线程本身就占有着锁,现在又尝试获取锁 + // 那么,直接让它获取锁并返回true + else if (current == getExclusiveOwnerThread()) { + // 状态变量state的值加1 + int nextc = c + acquires; + //如果溢出了,则报错 + if (nextc < 0) + throw new Error("Maximum lock count exceeded"); + //设置到state中 + setState(nextc); + // 当线程获取锁成功 + return true; + } + //当前线程尝试获取锁失败 + return false; + } + + +``` + +4、非公平锁 + +```java +final void lock() { + //直接尝试CAS更新状态变量 + if (compareAndSetState(0, 1)) + setExclusiveOwnerThread(Thread.currentThread()); + else + acquire(1); +} + +protected final boolean tryAcquire(int acquires) { + return nonfairTryAcquire(acquires); +} + +final boolean nonfairTryAcquire(int acquires) { + final Thread current = Thread.currentThread(); + int c = getState(); + if (c == 0) { + //相对于公平锁模式少了!hasQueuedPredecessors()条件(直接上去获取锁才不管别人有没有排队) + if (compareAndSetState(0, acquires)) { + setExclusiveOwnerThread(current); + return true; + } + } + else if (current == getExclusiveOwnerThread()) { + int nextc = c + acquires; + if (nextc < 0) // overflow + throw new Error("Maximum lock count exceeded"); + setState(nextc); + return true; + } + return false; +} + + +``` + +so ,相对于公平锁,非公平锁在一开始就多了两次直接尝试获取锁的过程。 + + + +5、条件锁 + +条件锁,是指在获取锁之后发现当前业务场景自己无法处理,而需要等待某个条件的出现才可以继续处理时使用的一种锁 + +lock.newCondition() + +```java +public Condition newCondition() { + return sync.newCondition(); +} + +final ConditionObject newCondition() { + return new ConditionObject(); +} + +``` + +ConditionObject主要属性 + +```java +public class ConditionObject implements Condition, java.io.Serializable { + /**队列头节点*/ + private transient Node firstWaiter; + /**队列尾节点*/ + private transient Node lastWaiter; + + public ConditionObject() { } + + } + +``` + + +condition.await() 表明现在要等待条件的出现。 + +```java + +//await实现过程:先释放锁->等待条件->再次获取锁 +public final void await() throws InterruptedException { + // 如果线程中断了,抛出异常 + if (Thread.interrupted()) + throw new InterruptedException(); + //添加节点到Condition的等待队列中,并返回该节点 + Node node = addConditionWaiter(); + + int savedState = fullyRelease(node); + int interruptMode = 0; + // 是否在同步队列中 + while (!isOnSyncQueue(node)) { + // 阻塞当前线程 + LockSupport.park(this); + ---------------以上是条件没出现前----------- + //条件已经出现,尝试去获取锁 + if ((interruptMode = checkInterruptWhileWaiting(node)) != 0) + break; + } + //尝试获取锁,如果没获取到会再次阻塞 + if (acquireQueued(node, savedState) && interruptMode != THROW_IE) + interruptMode = REINTERRUPT; + //清除取消的节点 + if (node.nextWaiter != null) // clean up if cancelled + unlinkCancelledWaiters(); + //线程中断相关 + if (interruptMode != 0) + reportInterruptAfterWait(interruptMode); +} + + +``` + +condition.signal()方法:通知条件已经出现。 + +```java + +public final void signal() { + // 如果不是当前线程占有着锁,调用这个方法抛出异常 + // 说明signal()也要在获取锁之后执行 + if (!isHeldExclusively()) + throw new IllegalMonitorStateException(); + //条件头队列 + Node first = firstWaiter; + //如果有等待条件的节点,则通知它条件已成立 + if (first != null) + doSignal(first); +} + +private void doSignal(Node first) { + do { + if ( (firstWaiter = first.nextWaiter) == null) + lastWaiter = null; + first.nextWaiter = null; + } while (!transferForSignal(first) && + (first = firstWaiter) != null); +} + +``` + +signal()方法的大致流程为: + +(1)从条件队列的头节点开始寻找一个非取消状态的节点; + +(2)把它从条件队列移到AQS队列; + +(3)且只移动一个节点; + +但是signal真正唤醒一个节点是在,执行了unlock()之后。 + + +6、自己动手写个demo 测试下: + +```java +public class ReentrantLockTest { + + public static void main(String[] args) throws InterruptedException { + //声明一个重入锁 + ReentrantLock reentrantLock = new ReentrantLock(); + //创建一个条件 + Condition condition = reentrantLock.newCondition(); + + new Thread(() -> { + try{ + reentrantLock.lock(); //1 + System.out.println("before wait "); //2 + + try { + condition.await(); //等待条件的出现 //3 + System.out.println("after wait "); //9 + } catch (InterruptedException e) { + e.printStackTrace(); + } + }catch (Exception e){ + + }finally { + reentrantLock.unlock(); //10 + + } + }).start(); + + //休眠会保证上面的执行完成 + Thread.sleep(1000); + + reentrantLock.lock(); //4 + + try{ + Thread.sleep(2000); + + System.out.println("before signal"); //5 + + condition.signal(); //唤醒 通知条件已经出现 //6 + + System.out.println("after signal"); //7 + }catch (Exception e){ + + }finally { + reentrantLock.unlock(); //8 + } + + } +} + + +``` + + +7、总结: + +1)ReentrantLock是重入锁 +2)控制state变化量,进行加锁释放锁;重入一次,这个数量加1,解锁的时候,解一次这个数量减1,所以多少次lock()对应多少次unlock()。 +3)ReentrantLock默认是非公平的,效率更高。 +4)ReentrantLock中的条件锁是通过AQS的ConditionObject内部类实现的; +5)await()和signal()方法都必须在获取锁之后释放锁之前使用。 + + + + + + + + + + + + -- Gitee From d713b3535704e2b24f74a3eb4861e6e113a82fb5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=B6=E5=AD=90?= Date: Thu, 26 Dec 2019 14:39:57 +0800 Subject: [PATCH 03/11] update AQS --- week_03/09/AQS-009.md | 4 +++- week_03/09/ReentrantLock-009.md | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/week_03/09/AQS-009.md b/week_03/09/AQS-009.md index 0fd1e43..c489ad4 100644 --- a/week_03/09/AQS-009.md +++ b/week_03/09/AQS-009.md @@ -1,4 +1,4 @@ -###AQS +AQS 1、认识 @@ -248,6 +248,8 @@ public class AQSTest { (2)AQS中维护了一个队列,这个队列使用双链表实现,用于保存等待锁排队的线程; (3)AQS中维护了一个状态变量,控制这个状态变量就可以实现加锁解锁操作了; (4)基于AQS自己动手写一个锁非常简单,只需要实现AQS的几个方法即可。 + (5)参考链接:https://www.jianshu.com/p/0f876ead2846 || 公众号:彤哥读源码 + diff --git a/week_03/09/ReentrantLock-009.md b/week_03/09/ReentrantLock-009.md index ab1648c..57a1112 100644 --- a/week_03/09/ReentrantLock-009.md +++ b/week_03/09/ReentrantLock-009.md @@ -1,4 +1,4 @@ -###ReentrantLock +ReentrantLock 1、认识 -- Gitee From 8de0791fac9550e03198e1d6e099bdc8297755ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=B6=E5=AD=90?= Date: Thu, 26 Dec 2019 16:25:29 +0800 Subject: [PATCH 04/11] Semaphore-009 --- week_03/09/Semaphore-009.md | 160 ++++++++++++++++++++++++++++++++++++ 1 file changed, 160 insertions(+) create mode 100644 week_03/09/Semaphore-009.md diff --git a/week_03/09/Semaphore-009.md b/week_03/09/Semaphore-009.md new file mode 100644 index 0000000..5985e47 --- /dev/null +++ b/week_03/09/Semaphore-009.md @@ -0,0 +1,160 @@ +Semaphore + +1、认识 + + Semaphore,即信号量。有两个目的,第一个是多个共享资源互斥使用,第二个是并发线程数的控制。 + +2、源码分析 + +与ReentrantLock结构很相似。 + +1)主要内部类 + +```java +abstract static class Sync extends AbstractQueuedSynchronizer { + //传入允许次数 + Sync(int permits) { + //放入state中 + setState(permits); + } + //获取允许次数 + final int getPermits() { + return getState(); + } + //不公平模式尝试获取许可 + final int nonfairTryAcquireShared(int acquires) { + for (;;) { + int available = getState(); + int remaining = available - acquires; + if (remaining < 0 || + compareAndSetState(available, remaining)) + return remaining; + } + } + //释放许可 + protected final boolean tryReleaseShared(int releases) { + for (;;) { + int current = getState(); + int next = current + releases; + if (next < current) // overflow + throw new Error("Maximum permit count exceeded"); + if (compareAndSetState(current, next)) + return true; + } + } + //减少许可 + final void reducePermits(int reductions) { + for (;;) { + int current = getState(); + int next = current - reductions; + if (next > current) // underflow + throw new Error("Permit count underflow"); + if (compareAndSetState(current, next)) + return; + } + } + //销毁许可 + final int drainPermits() { + for (;;) { + int current = getState(); + if (current == 0 || compareAndSetState(current, 0)) + return current; + } + } +} + +``` + +2)非公平模式下 + +```java +static final class NonfairSync extends Sync { + private static final long serialVersionUID = -2694183684443567898L; + + NonfairSync(int permits) { + super(permits); + } + + protected int tryAcquireShared(int acquires) { + return nonfairTryAcquireShared(acquires); + } +} +``` + +非公平模式下,直接调用父类的nonfairTryAcquireShared()尝试获取许可。 + + +3)公平模式下 + +```java + +static final class FairSync extends Sync { + private static final long serialVersionUID = 2014338818796000944L; + + FairSync(int permits) { + super(permits); + } + + protected int tryAcquireShared(int acquires) { + for (;;) { + if (hasQueuedPredecessors()) + return -1; + int available = getState(); + int remaining = available - acquires; + if (remaining < 0 || + compareAndSetState(available, remaining)) + return remaining; + } + } +} + +``` + +公平模式下,先检测前面是否有排队的,如果有排队的则获取许可失败,进入队列排队,否则尝试原子更新state的值。 + + +ps: +如何实现限流? + +答:限流,即在流量突然增大的时候,上层要能够限制住突然的大流量对下游服务的冲击,在分布式系统中限流一般做在网关层,当然在个别功能中也可以自己简单地来限流,比如秒杀场景,假如只有10个商品需要秒杀,那么,服务本身可以限制同时只进来100个请求,其它请求全部作废,这样服务的压力也不会太大。 + +使用Semaphore就可以直接针对这个功能来限流。 + +自己写个demo + +```java + +public class SemaphoreTest { + + public static void main(String[] args) { + //创建信号量 + Semaphore semaphore = new Semaphore(5); + + for (int i = 1; i <= 10; i++){ + new Thread(() -> { + try{ + //获取锁资源 + semaphore.acquire(); + System.out.println(Thread.currentThread().getName() + "\t上厕所"); + + // 模拟人上厕所10秒,然后让出坑位 + TimeUnit.SECONDS.sleep(5); + + System.out.println(Thread.currentThread().getName() + "\t上完厕所,让出坑位"); + + }catch (Exception e){ + + }finally { + // 释放锁资源 + semaphore.release(); + } + + },"" + i + "号帅哥").start(); + } + + } +} +``` + + + \ No newline at end of file -- Gitee From 2c10a47d119ddf84fd5a4bd61cf5e2f46bab3634 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=B6=E5=AD=90?= Date: Thu, 26 Dec 2019 16:53:38 +0800 Subject: [PATCH 05/11] =?UTF-8?q?add=20week=5F03/09/java=E5=86=85=E5=AD=98?= =?UTF-8?q?=E6=A8=A1=E5=9E=8B=E7=90=86=E8=AE=BA.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...\345\255\230\346\250\241\345\236\213\347\220\206\350\256\272" | 1 + 1 file changed, 1 insertion(+) create mode 100644 "week_03/09/java\345\206\205\345\255\230\346\250\241\345\236\213\347\220\206\350\256\272" diff --git "a/week_03/09/java\345\206\205\345\255\230\346\250\241\345\236\213\347\220\206\350\256\272" "b/week_03/09/java\345\206\205\345\255\230\346\250\241\345\236\213\347\220\206\350\256\272" new file mode 100644 index 0000000..d62f427 --- /dev/null +++ "b/week_03/09/java\345\206\205\345\255\230\346\250\241\345\236\213\347\220\206\350\256\272" @@ -0,0 +1 @@ +阅读链接:https://blog.csdn.net/qq_34964197/article/details/80937147 \ No newline at end of file -- Gitee From 5de82fce7eba9aca1d0fcaeeb27f52206043f8ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=B6=E5=AD=90?= Date: Thu, 26 Dec 2019 17:02:29 +0800 Subject: [PATCH 06/11] =?UTF-8?q?update=20java=E5=86=85=E5=AD=98=E6=A8=A1?= =?UTF-8?q?=E5=9E=8B=E7=90=86=E8=AE=BA=20009.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...346\250\241\345\236\213\347\220\206\350\256\272" | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git "a/week_03/09/java\345\206\205\345\255\230\346\250\241\345\236\213\347\220\206\350\256\272" "b/week_03/09/java\345\206\205\345\255\230\346\250\241\345\236\213\347\220\206\350\256\272" index d62f427..05ba3b6 100644 --- "a/week_03/09/java\345\206\205\345\255\230\346\250\241\345\236\213\347\220\206\350\256\272" +++ "b/week_03/09/java\345\206\205\345\255\230\346\250\241\345\236\213\347\220\206\350\256\272" @@ -1 +1,12 @@ -阅读链接:https://blog.csdn.net/qq_34964197/article/details/80937147 \ No newline at end of file +学习阅读链接:https://blog.csdn.net/qq_34964197/article/details/80937147 + +1、什么是java内存模型 + Java程序是需要运行在Java虚拟机上面的,Java内存模型(Java Memory Model ,JMM)就是一种符合内存模型规范的, + 屏蔽了各种硬件和操作系统的访问差异的,保证了Java程序在各种平台下对内存的访问都能保证效果一致的机制及规范。 + +2、实现java内存模型(Java内存模型,除了定义了一套规范,还提供了一系列原语,封装了底层实现后,供开发者直接使用) + 原子性:java中可以使用synchronized来保证方法和代码块内的操作是原子性的 + 可见性:可以使用volatile来保证多线程操作时变量的可见性。除此之外,Java中的synchronized和final两个关键字也可以实现可见性。 + 有序性:在Java中,可以使用synchronized和volatile来保证多线程之间操作的有序性。 + 实现方式有所区别:volatile关键字会禁止指令重排。synchronized关键字保证同一时刻只允许一条线程操作。 + \ No newline at end of file -- Gitee From 490007930efe6939456cadd9d66f6955476477d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=B6=E5=AD=90?= Date: Thu, 26 Dec 2019 17:21:11 +0800 Subject: [PATCH 07/11] =?UTF-8?q?add=20week=5F03/09/volatile=E7=90=86?= =?UTF-8?q?=E8=AE=BA.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- "week_03/09/volatile\347\220\206\350\256\272" | 1 + 1 file changed, 1 insertion(+) create mode 100644 "week_03/09/volatile\347\220\206\350\256\272" diff --git "a/week_03/09/volatile\347\220\206\350\256\272" "b/week_03/09/volatile\347\220\206\350\256\272" new file mode 100644 index 0000000..3b20662 --- /dev/null +++ "b/week_03/09/volatile\347\220\206\350\256\272" @@ -0,0 +1 @@ +学习阅读链接 :https://www.cnblogs.com/zhengbin/p/5654805.html -- Gitee From 9dff14760a575b16735a334b77c1da95935e3432 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=B6=E5=AD=90?= Date: Thu, 26 Dec 2019 17:21:42 +0800 Subject: [PATCH 08/11] =?UTF-8?q?=E5=88=A0=E9=99=A4=E6=96=87=E4=BB=B6=20we?= =?UTF-8?q?ek=5F03/09/README.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- week_03/09/README.md | 1 - 1 file changed, 1 deletion(-) delete mode 100644 week_03/09/README.md diff --git a/week_03/09/README.md b/week_03/09/README.md deleted file mode 100644 index 4287ca8..0000000 --- a/week_03/09/README.md +++ /dev/null @@ -1 +0,0 @@ -# \ No newline at end of file -- Gitee From bdce1063fac5edd49d1f0792405458a34c1c20de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=B6=E5=AD=90?= Date: Fri, 27 Dec 2019 10:07:05 +0800 Subject: [PATCH 09/11] update week_02/09/unsafe-009.md. --- week_02/09/unsafe-009.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/week_02/09/unsafe-009.md b/week_02/09/unsafe-009.md index 7c598c2..f582eaf 100644 --- a/week_02/09/unsafe-009.md +++ b/week_02/09/unsafe-009.md @@ -158,5 +158,7 @@ public native boolean shouldBeInitialized(Class var1); public native void ensureClassInitialized(Class var1); ``` +ps +优质详细文章推荐:https://tech.meituan.com/2019/02/14/talk-about-java-magic-class-unsafe.html -- Gitee From 88d1ab26dfef3575528fcdf36b78a11a3ba24225 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=B6=E5=AD=90?= Date: Fri, 27 Dec 2019 14:38:35 +0800 Subject: [PATCH 10/11] =?UTF-8?q?add=20week=5F03/09/synchronized=E7=90=86?= =?UTF-8?q?=E8=AE=BA.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- "week_03/09/synchronized\347\220\206\350\256\272" | 1 + 1 file changed, 1 insertion(+) create mode 100644 "week_03/09/synchronized\347\220\206\350\256\272" diff --git "a/week_03/09/synchronized\347\220\206\350\256\272" "b/week_03/09/synchronized\347\220\206\350\256\272" new file mode 100644 index 0000000..fdd8ff8 --- /dev/null +++ "b/week_03/09/synchronized\347\220\206\350\256\272" @@ -0,0 +1 @@ +学习阅读链接:https://cloud.tencent.com/developer/article/1465413 \ No newline at end of file -- Gitee From e6fb61cd530015858018517b8ff0202088f3dee9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=B6=E5=AD=90?= Date: Fri, 27 Dec 2019 17:37:57 +0800 Subject: [PATCH 11/11] =?UTF-8?q?add=20week=5F03/09/=E5=88=86=E5=B8=83?= =?UTF-8?q?=E5=BC=8F=E9=94=81=E7=90=86=E8=AE=BA.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...\345\270\203\345\274\217\351\224\201\347\220\206\350\256\272" | 1 + 1 file changed, 1 insertion(+) create mode 100644 "week_03/09/\345\210\206\345\270\203\345\274\217\351\224\201\347\220\206\350\256\272" diff --git "a/week_03/09/\345\210\206\345\270\203\345\274\217\351\224\201\347\220\206\350\256\272" "b/week_03/09/\345\210\206\345\270\203\345\274\217\351\224\201\347\220\206\350\256\272" new file mode 100644 index 0000000..2b11609 --- /dev/null +++ "b/week_03/09/\345\210\206\345\270\203\345\274\217\351\224\201\347\220\206\350\256\272" @@ -0,0 +1 @@ +学习阅读链接;https://blog.csdn.net/wuzhiwei549/article/details/80692278 \ No newline at end of file -- Gitee