实现的模拟多线程实现售票是每个学习多线程的初学者必须要学会掌握的知识点, 既然掌握的它, 我们自然要举一反三
So~, 无锁版出现了
What无锁?
假如两个线程同时修改一个变量的场景下
我们需要三个值, 预期值(线程副本变量中的值), 主存值(从主存变量中的值), 新值(我们要设置的值)
如果 预期值 不等于 主存值 则忽略 新值 写入 =========> 这句话是一个原子操作, 是不可分割的(就是内存屏障), 在执行这个过程中, 是不会失去时间片的
如果 预期值 等于 主存值 则 新值 写入 =========> 这句话是一个原子操作, 是不可分割的(就是内存屏障), 在执行这个过程中, 是不会失去时间片的
直接上代码就能理解了, 这是多线程模拟售票的案例, 里面有无锁和加锁的案例
package com.zhazha.juc; import org.junit.Test; import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * @author zhazha * @version 1.0.0 * @date 2019/10/19 9:39 * @msg **/ public class TestTicketAtomicLock { @Test public void test01() throws Exception { CountDownLatch latch = new CountDownLatch(3); TicketAtomic ticket = new TicketAtomic(latch); for (int i = 0; i < 3; i++) { new Thread(ticket).start(); } latch.await(); } @Test public void test() throws Exception { CountDownLatch latch = new CountDownLatch(3); TicketNonAtomic ticket = new TicketNonAtomic(latch); for (int i = 0; i < 3; i++) { new Thread(ticket).start(); } latch.await(); } } class TicketAtomic implements Runnable { private AtomicInteger ticket = new AtomicInteger(100); private CountDownLatch latch; public TicketAtomic(CountDownLatch latch) { this.latch = latch; } @Override public void run() { while (true) { try { int temp = ticket.get(); if (temp > 0) { // TimeUnit.MILLISECONDS.sleep(100); if (ticket.compareAndSet(temp, ticket.get() - 1)) { System.out.println(Thread.currentThread().getName() + " 成功售票, 余票为: " + ticket.get()); } } else { break; } } catch (Exception e) { e.printStackTrace(); } } latch.countDown(); } } class TicketNonAtomic implements Runnable { private Integer ticket = 100; private Lock lock = new ReentrantLock(); private CountDownLatch latch; public TicketNonAtomic(CountDownLatch latch) { this.latch = latch; } @Override public void run() { while (true) { lock.lock(); try { if (ticket > 0) { // TimeUnit.MILLISECONDS.sleep(100); System.out.println(Thread.currentThread().getName() + " 成功售票, 余票为: " + --ticket); } else { break; } } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } latch.countDown(); } }
上面无锁代码中, 最重要的是这句话
if (ticket.compareAndSet(temp, ticket.get() - 1)) {
内部代码
再进去就是用c++实现的无锁
这步是原子性操作的, 整个过程
如果 预期值 不等于 主存值 则忽略 新值 写入 等于 主存值 则 写入