随笔 - 10  文章 - 0  评论 - 0  阅读 - 489

初识JUC

JUC

并发编程

简介

//这三个包里的 就是JUC
import java.util.concurrent.*;
import java.util.concurrent.atomic.*;
import java.util.concurrent.locks.*;

java.util 工具包 包、分类

Runnable 没有返回值、效率相比Callable相对较低

进程和线程

进程:一个运行中的程序 .exe 可执行程序的集合

一个进程往往可以包含多个线程,至少包含一个

java默认有几个线程 2个 main、GC

线程:就是再这个进程中可以分开干别的事 就是一个单独的资源类,没有任何附属操作

开启线程:Thread、Runnable、Callable

并发 :多个线程操作同一个资源

  • CPU一核,模拟多条线程

并行:多个人一起行走

  • CPU多核 ,多个线程可以同时执行,线程池
public static void main(String[] args) {
//获取CPU的核数
//CPU 密集型,IO密集型
System.out.println(Runtime.getRuntime().availableProcessors());
}

并发编程的本质:充分利用CPU的资源

线程有几个状态 Thread.State

public enum State {
//新生
NEW,
//运行
RUNNABLE,
//阻塞
BLOCKED,
//等待,只能等
WAITING,
//超时等待,过时不侯
TIMED_WAITING,
//终止
TERMINATED;
}

wait 、sleep 区别

  • 来自不同的类

    • wiat来自Object类
    • sleep来自Thread 一般使用TimeUnit进行休眠
  • 关于锁的释放

    • wait会释放锁
    • sleep不会释放
  • 使用的范围是不同的

    • wait必须再同步代码块中 使用
    • sleep可以再任何地方都可以使用

都需要捕获一个中断异常 InterruptedException

Lock锁

Synchroized锁

本质:锁(对象)+队列

public class SaleTicketDemo01 {
public static void main(String[] args) {
//并发:多个线程操作通过一个资源类
Ticket ticket = new Ticket();
new Thread(() -> { for (int i = 0; i < 60; i++) ticket.sale(); }, "A").start();
new Thread(() -> { for (int i = 0; i < 60; i++) ticket.sale(); }, "B").start();
new Thread(() -> { for (int i = 0; i < 60; i++)ticket.sale(); }, "C").start();
}
}
//资源类
class Ticket {
//属性、方法
private int number = 50;
//synchronized 本质:锁+队列
public synchronized void sale() {
if (number > 0) {
System.out.println(Thread.currentThread().getName() + "卖出了1张票, 剩余: " + (number--) + " 张票");
}
}
}

Lock锁接口

实现类

  • ReentrantLock(可重入锁)
  • ReentrantReadWriteLock.ReadLock (读锁)
  • ReentrantReadWriteLock.WriteLock (写锁)

公平锁:先来后到 很公平

非公平锁:可以插队

public class SaleTicketDemo02 {
public static void main(String[] args) {
//并发:多个线程操作通过一个资源类
Ticket1 ticket = new Ticket1();
new Thread(() -> { for (int i = 0; i < 40; i++) ticket.sale(); }, "A").start();
new Thread(() -> { for (int i = 0; i < 40; i++) ticket.sale(); }, "B").start();
new Thread(() -> { for (int i = 0; i < 40; i++) ticket.sale(); }, "C").start();
}
}
//Lock 三部曲
//1、 创建锁 new ReentrantLock();
//2、 上锁 lock.lock();
//3、 解锁 finally->lock.unlock();
class Ticket1 {
private int number = 30;
Lock lock = new ReentrantLock();
public void sale() {
//上锁
lock.lock();
try {
//业务逻辑
if (number > 0) {
System.out.println(Thread.currentThread().getName() + "卖出了 1 张票, 剩余: " + (number--) + " 张票");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//解锁
lock.unlock();
}
}
}

区别

  • Synchronized 内置的java关键字; Lock是一个java类
  • Synchronized 无法判断获取锁的状态 隐式;Lock可以判断是否获取到了锁 显式
  • Synchronized 会自动释放锁 ;Lock必须要手动释放锁 不释放锁就会造成死锁
  • Synchronized 线程1 先获得锁之后阻塞了,线程2 就会一直等待;Lock 锁不会一直等待 lock.tryLock(); 尝试获得锁
  • Synchronized 可重入锁,不可中断的,非公平 ; Lock:可重入锁,可以判断锁,非公平(可以自己设置)
  • Synchronized 适合锁少量的代码同步块;Lock 适合锁大量的代码同步块

生产者和消费者

Synchroized

Synchroized版 使用 wait notify

public class A {
public static void main(String[] args) {
Data data = new Data();
new Thread(() -> {
try {
for (int i = 0; i < 10; i++) {
data.increment();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "A").start();
new Thread(() -> {
try {
for (int i = 0; i < 10; i++) {
data.decrement();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "B").start();
}
}
//判断等待、业务、通知
class Data {
private int num = 0;
public synchronized void increment() throws InterruptedException {
if (num != 0) {
//等待
this.wait();
}
//业务
num++;
System.out.println(Thread.currentThread().getName() + "=>" + num);
//通知其他线程
this.notifyAll();
}
public synchronized void decrement() throws InterruptedException {
if (num == 0) {
//等待
this.wait();
}
//业务
num--;
System.out.println(Thread.currentThread().getName() + "=>" + num);
//通知其他线程
this.notifyAll();
}
}

问题存在,只能是两个线程 多个线程出现 虚假唤醒 用while来判断

//判断等待、业务、通知
class Data {
private int num = 0;
public synchronized void increment() throws InterruptedException {
while (num != 0) {
//等待
this.wait();
}
//业务
num++;
System.out.println(Thread.currentThread().getName() + "=>" + num);
//通知其他线程
this.notifyAll();
}
public synchronized void decrement() throws InterruptedException {
while (num == 0) {
//等待
this.wait();
}
//业务
num--;
System.out.println(Thread.currentThread().getName() + "=>" + num);
//通知其他线程
this.notifyAll();
}

Lock

Lock中 有newCondition() 方法 返回一个新Condition绑定到该实例Lock实例

持有当前线程的锁,呼叫Condition.await()将在等待之前将原子释放锁,并在等待返回之前重新获取锁

Condition因素出Object监视器方法( waitnotifynotifyAll )成不同的对象,以得到具有多个等待集的每个对象,通过将它们与使用任意的组合的效果Lock个实现。 Lock替换synchronized方法和语句的使用, Condition取代了对象监视器方法的使用。

唤醒一个等待的线程 signal() 唤醒所有等待线程 signalAll()

//判断等待、业务、通知
class Data {
private int num = 0;
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
public void increment() throws InterruptedException {
lock.lock();
try {
while (num != 0) {
//等待
condition.await();
}
//业务
num++;
System.out.println(Thread.currentThread().getName() + "=>" + num);
//通知其他线程
condition.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void decrement() throws InterruptedException {
lock.lock();
try {
while (num == 0) {
//等待
condition.await();
}
//业务
num--;
System.out.println(Thread.currentThread().getName() + "=>" + num);
//通知其他线程
condition.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}

Condition

精准的通知和唤醒操作

class Data3 {
private Lock lock = new ReentrantLock();
private Condition condition1 = lock.newCondition();
private Condition condition2 = lock.newCondition();
private Condition condition3 = lock.newCondition();
private int num = 1; //1A 2B 3C
public void printA() {
lock.lock();
try {
//业务,判断到执行到通知
while (num != 1) {
condition1.await();
}
System.out.println(Thread.currentThread().getName() + "=>AAAAAAAAAAAAA");
num = 2;
//唤醒condition2
condition2.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void printB() {
lock.lock();
try {
while (num != 2) {
condition2.await();
}
System.out.println(Thread.currentThread().getName() + "=>BBBBBBBBBBBB");
num = 3;
condition3.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void printC() {
lock.lock();
try {
while (num != 3) {
condition3.await();
}
System.out.println(Thread.currentThread().getName() + "=>CCCCCCCCCCCCC");
num = 1;
condition1.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}

锁的多种现象

/**
* 环境:
* 线程:A、B 资源类: 两个同步方法 f1 f2
* 同一个对象调用,A执行f1 B执行f2 f1、f2谁先执行?
* #######谁先拿到这个资源谁先执行 ---->锁的是对象的调用者
*
* 线程:A、B 资源类: 两个同步方法 f1:sleep(1000) f2
* 同一个对象调用,延迟线程A后,A执行f1 B执行f2 f1、f2谁先执行?
* ########先执行f1再执行f2 因为锁的是同一个同一个对象 而sleep不会释放锁
*/
/**
* 环境:
* 线程:A、B 资源类: 同步方法f1:sleep 普通方法f2
* 同一个对象调用 线程A 执行f1, 线程B执行f2 那么先执行f1还是f2?
* ########先执行f2再执行f1 因为f2没有上锁不受锁影响 f1先休眠了
*
* 线程:A、B 资源类: 两个同步方法f1:sleep f2
* 两个分别对象调用 A执行f1 B执行f2 那么哪个方法先执行?
* ########先执行f2 再执行f1 因为是两个对象两把锁 f1也先休眠了
*/
/**
* 环境:
* 线程:A、B 资源类: 两个静态同步方法f1:sleep f2
* 同一个对象调用 线程A 执行f1, 线程B执行f2 那么先执行f1还是f2?
* ########先执行f1再执行f2 是因为static 在类加载的时候就有了 锁的对象是Class对象
* ########同样f1即使休眠了也不会释放锁
*
* 线程:A、B 资源类: 两个静态同步方法f1:sleep f2
* 两个分别对象调用 A执行f1 B执行f2 那么哪个方法先执行?
* ########先执行f1再执行f2 是因为static 在类加载的时候就有了 锁的对象是Class对象
* ########同样f1即使休眠了也不会释放锁
*/
/**
* 环境:
* 线程:A、B 资源类: 静态的同步方法f1:sleep ,普通的同步方法f2
* 同一个对象调用 线程A 执行f1, 线程B执行f2 那么先执行f1还是f2?
* ########先执行f2执行再执行f1 这是因为两个不同的锁
* ########一个锁的是Class 一个锁的是调用者 f1会休眠
*
* 线程:A、B 资源类: 静态同步方法f1:sleep 普通同步方法 f2
* 两个分别对象调用 A执行f1 B执行f2 那么哪个方法先执行?
* ########先执行f2再执行f1 因为这是两把锁
* ########一个锁的是Class 一个锁的是调用者 f1会休眠
*/

new关键字 this 具体的一个对象

static 关键字 Class 对象的唯一模板

集合类不安全

List

//java.util.ConcurrentModificationException 并发修改异常
public class ListTest {
public static void main(String[] args) {
//并发下ArrayList 不安全的
/**
* 解决方案:
* 1、用jdk1.0的 new Vector<>();
* 2、利用工具类转换成安全的 Collections.synchronizedList(new ArrayList<>());
* 3、List<String> list = new CopyOnWriteArrayList<>();
*/
//CopyOnWrite 写入时复制 COW 计算机程序设计领域的一种优化策略
//多个线程调用的时候, list 读取的时候,是固定的,写入的存在覆盖问题
//在写入的时候避免覆盖造成的数据问题
//读写分离 MyCat
//CopyOnWriteArrayList 使用的lock锁和 Vector使用Synchronized
List<String> list = new CopyOnWriteArrayList<>();
for (int i = 1; i <= 10; i++) {
new Thread(() -> {
list.add(UUID.randomUUID().toString().substring(0, 5));
System.out.println(list);
}, "Thread" + i).start();
}
}
}

set

//java.util.ConcurrentModificationException 并发修改异常
public class SetTest {
public static void main(String[] args) {
/**
* 解决方案:
* 1、Set<String> set= Collections.synchronizedSet(new HashSet<>());
* 2、Set<String> set=new CopyOnWriteArraySet<>();
*/
//Set<String> set = new HashSet<>();
//Set<String> set= Collections.synchronizedSet(new HashSet<>());
Set<String> set=new CopyOnWriteArraySet<>();
for (int i = 0; i < 10; i++) {
new Thread(() -> {
set.add(UUID.randomUUID().toString().substring(0, 5));
System.out.println(set);
}, String.valueOf(i)).start();
}
}
}

HashSet原理

/**
* Constructs a new, empty set; the backing <tt>HashMap</tt> instance has
* default initial capacity (16) and load factor (0.75).
*/
public HashSet() {
map = new HashMap<>();
}
//add方法 map key是无法重复的! PRESENT不变的值
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}

Map

//java.util.ConcurrentModificationException
public class MapTest {
public static void main(String[] args) {
/**
* 解决方案
* 1、Map<String,String> map= Collections.synchronizedMap(new HashMap<>());
* 2、Map<String,String> map= new ConcurrentHashMap<>();
*/
Map<String,String> map= new ConcurrentHashMap<>();
//ConcurrentHashMap原理
//默认等价于 new HashMap<>(16,0.75)
//加载因子
for (int i = 1; i <=30; i++) {
new Thread(()->{
map.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0, 5));
System.out.println(map);
},String.valueOf(i)).start();
}
}
}

Callable

可以有返回值、可以抛出异常、方法不同call()

public class CallableTest {
public static void main(String[] args) throws ExecutionException, InterruptedException {
/**
* new Thread(new Runnable()).start();===new Thread(new FutureTask(Callable)).start()
*/
MyThread thread = new MyThread();
//适配类
FutureTask<String> futureTask = new FutureTask<>(thread);
new Thread(futureTask,"A").start();
new Thread(futureTask,"B").start();//结果有缓存
String s = futureTask.get();
System.out.println(s);
}
}
class MyThread implements Callable<String> {
@Override
public String call() throws Exception {
System.out.println("进入call()方法");
//耗时的操作
return "123456";
}
}

细节

  • 结果会有缓存
  • 结果可能需要等待,会阻塞

ReadWriteLock

读写锁

/**
* 独占锁 (写锁) 一次只能被一次线程占有
* 共享锁 (读锁)多个线程同时占有
* ReadWriteLock
* 读 - 读 可以共存
* 读 - 写 不可共存
* 写 - 写 不可共存
*/
public class ReadWriteLockDemo {
public static void main(String[] args) {
MyCacheLock myCache = new MyCacheLock();
for (int i = 1; i <= 10; i++) {
final int temp = i;
new Thread(() -> {
myCache.put(temp + "", UUID.randomUUID().toString().substring(0, 6));
}, String.valueOf(i)).start();
}
for (int i = 1; i <= 10; i++) {
final int temp = i;
new Thread(() -> {
myCache.get(temp + "");
}, String.valueOf(i)).start();
}
}
}
//小型的读写分离
//自定义缓存
class MyCache {
private volatile Map<String, Object> map = new HashMap<>();
//存,写
public void put(String key, Object value) {
System.out.println(Thread.currentThread().getName() + "写入 " + key);
map.put(key, value);
System.out.println(Thread.currentThread().getName() + "写入完毕 " );
}
//取,读
public void get(String key) {
System.out.println(Thread.currentThread().getName() + "读取 " + key);
map.get(key);
System.out.println(Thread.currentThread().getName() + "读取完毕");
}
}
class MyCacheLock {
//读写锁;更加细粒度控制
private ReentrantReadWriteLock rwLock=new ReentrantReadWriteLock();
private volatile Map<String, Object> map = new HashMap<>();
//存,写 ,写入的时候只希望只有一个人线写
public void put(String key, Object value) {
rwLock.writeLock().lock();
try {
System.out.println(Thread.currentThread().getName() + "写入 " + key);
map.put(key, value);
System.out.println(Thread.currentThread().getName() + "写入完毕 " );
} catch (Exception e) {
e.printStackTrace();
} finally {
rwLock.writeLock().unlock();
}
}
//取,读 所有人什么时候都可以读
public void get(String key) {
rwLock.readLock().lock();
try {
System.out.println(Thread.currentThread().getName() + "读取 " + map.get(key));
System.out.println(Thread.currentThread().getName() + "读取完毕");
} catch (Exception e) {
e.printStackTrace();
} finally {
rwLock.readLock().unlock();
}
}
}

异步回调

对将来的某个事件的结果进行建模 异步执行成功返回什么结果失败返回什么 或者没有返回值

/**Ajax
* 异步调用 :CompletableFuture
* 异步执行
* 成功回调
* 失败回调
*/
public class Demo01 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
test2();
}
//没有返回值的runAsync异步回调
public static void test1() throws ExecutionException, InterruptedException {
CompletableFuture<Void> completableFuture = CompletableFuture.runAsync(()->{
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "runAsync=Void");
});
System.out.println("111111111111111");
completableFuture.get();//获取执行结果 可能会阻塞
}
//有返回值的supplyAsync异步回调
//成功和失败回调
public static void test2() throws ExecutionException, InterruptedException {
CompletableFuture<Integer> completableFuture= CompletableFuture.supplyAsync(()->{
System.out.println(Thread.currentThread().getName()+"supplyAsync==>Integer");
int i=1/0;
return 1024;
});
System.out.println(completableFuture.whenComplete((t, u) -> {
System.out.println("t->" + t);// 正常的结果
System.out.println("u->" + u);//错误信息
}).exceptionally((e) -> {
System.out.println(e.getMessage());
return 23333; //获取错误的返回结果
}).get());
}
}

JMM

Java 内存模型 ,概念性

关于JMM的一些同步的约定:

  • 线程解锁前,必须把共享变量立刻刷回主存
  • 线程加锁前,必须读取主存中的最新值到工作内存中
  • 加锁和解锁是同一把锁

线程:工作内存主内存

8种操作:

主存-->lock-->read-->load-->use-->assign-->store-->write--> unlock-->主存

内存交互操作有8种,虚拟机实现必须保证每一个操作都是原子的,不可在分的(对于double和long类型的变量来说,load、store、read和write操作在某些平台上允许例外)

  • lock (锁定):作用于主内存的变量,把一个变量标识为线程独占状态

  • unlock (解锁):作用于主内存的变量,它把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定

  • read (读取):作用于主内存变量,它把一个变量的值从主内存传输到线程的工作内存中,以便随后的load动作使用

  • load (载入):作用于工作内存的变量,它把read操作从主存中变量放入工作内存中

  • use (使用):作用于工作内存中的变量,它把工作内存中的变量传输给执行引擎,每当虚拟机遇到一个需要使用到变量的值,就会使用到这个指令

  • assign (赋值):作用于工作内存中的变量,它把一个从执行引擎中接受到的值放入工作内存的变量副本中

  • store (存储):作用于主内存中的变量,它把一个从工作内存中一个变量的值传送到主内存中,以便后续的write使用

  • write  (写入):作用于主内存中的变量,它把store操作从工作内存中得到的变量的值放入主内存的变量中

JMM对这八种指令的使用,制定了如下规则:

  • 不允许read和load、store和write操作之一单独出现。即使用了read必须load,使用了store必须write

  • 不允许线程丢弃他最近的assign操作,即工作变量的数据改变了之后,必须告知主存

  • 不允许一个线程将没有assign的数据从工作内存同步回主内存

  • 一个新的变量必须在主内存中诞生,不允许工作内存直接使用一个未被初始化的变量。就是怼变量实施use、store操作之前,必须经过assign和load操作

  • 一个变量同一时间只有一个线程能对其进行lock。多次lock后,必须执行相同次数的unlock才能解锁

  • 如果对一个变量进行lock操作,会清空所有工作内存中此变量的值,在执行引擎使用这个变量前,必须重新load或assign操作初始化变量的值

  • 如果一个变量没有被lock,就不能对其进行unlock操作。也不能unlock一个被其他线程锁住的变量

  • 对一个变量进行unlock操作之前,必须把此变量同步回主内存

Volatile

Volatile是Java虚拟机提供轻量级的同步机制

  • 保证可见性
  • 不保证原子性
  • 禁止指令重排

保证可见性

//不加volatile就会死循环!
//加了volatile 保证了可见性
private volatile static int num = 0;
public static void main(String[] args) {
new Thread(() -> { //该线程需要知道主内存的num有没有变化
while (num == 0) {
}
}).start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
num=1;
System.out.println(num);
}

不保证原子性

原子性: 不可分割

线程A在执行任务的时候,不能被打扰的,也不能被分割。要么同时成功,要么同时失败

//不保证原子性
public class Demo02 {
private volatile static int num=0;
private static void add(){
num++;
}
public static void main(String[] args) {
for (int i = 1; i <=20 ; i++) {
new Thread(()->{
for (int j = 0; j <1000 ; j++) {
add();
}
}).start();
}
//判断存活的线程 main gc
while (Thread.activeCount()>2){
Thread.yield();
}
System.out.println(Thread.currentThread().getName()+"=>"+num);
}
}

如果不加lock和synchronized 怎么保证原子性?

使用原子类,解决原子性问题

//不保证原子性
public class Demo02 {
//AtomicInteger原子类
private volatile static AtomicInteger num=new AtomicInteger();
private static void add(){
//num++; //不是原子性操作
num.getAndIncrement();//+1 方法 CAS
}
public static void main(String[] args) {
for (int i = 1; i <=20 ; i++) {
new Thread(()->{
for (int j = 0; j <1000 ; j++) {
add();
}
}).start();
}
//判断存活的线程 main gc
while (Thread.activeCount()>2){
Thread.yield();
}
System.out.println(Thread.currentThread().getName()+"=>"+num);
}
}

这些类的底层都和操作系统挂钩的 native 在内存种修改 Unsafe类是一个很特殊的类

禁止指令重排

指令重排:你写的程序,计算机并不是按照你写的那样取执行的

源代码-->编译器优化的重排-->指令并行也可能重排-->内存系统也会重排-->执行

处理器在进行指令重排的时候,考虑:数据之间的依赖性

int x=1; //1
int y=2; //2
x=x+5; //3
y=x*x; //4
1234 2134 1324 4123

可能造成影响的结果:a b x y默认值都是0;

线程A 线程B
x=a y=b
b=1 a=2

正常的结果 x=0 ; y=0 但可能由于指令重排

线程A 线程B
b=1 a=2
x=a y=b

指令重排导致的结果 x=2 ; y=1

Volatile 可以避免指令重排

内存屏障。CPU指令,作用:

  • 保证特定的操作的执行顺序
  • 可以保证某些变量的内存可见性(利用这些特性volatile实现了可见性 )

理解CAS

CAS: CompareAndSwap 比较并交换

CAS 操作包含三个操作数 —— 内存位置(V)、预期原值(A)和新值(B)。 如果内存位置的值与预期原值相匹配,那么处理器会自动将该位置值更新为新值 。否则,处理器不做任何操作。

//CAS: CompareAndSwap 是CPU的并发原语
public static void main(String[] args) {
AtomicInteger atomicInteger = new AtomicInteger(2020);
/** 期望 更新
* public final boolean compareAndSet(int expect, int update) {
* return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
* }
*/
//如果达到期望值到达了就更新
boolean b = atomicInteger.compareAndSet(2020, 2021);
System.out.println(atomicInteger.get());
atomicInteger.getAndIncrement();
System.out.println(atomicInteger.compareAndSet(2020, 2021));
}

Unsafe类

CAS:比较当前工作内存中的值,如果这个值是期望的,那么执行操作,如果不是就一直循环

缺点:

  • 循环会耗时
  • 一次性只能保证一个共享变量的原子性
  • ABA问题

ABA问题

有个线程它修改了主内存的值之后又改回原来的值,另一个线程过来认为还是原来的那个之后就进行操作

也就是有个线程把主内存的A值该为B后又改为回A了

原子引用

带版本号的CAS操作解决了ABA问题 相似乐观锁

Integer使用了对象缓存机制,默认范围是-128~127,推荐使用静态工厂方法valueof获取对象实例,而不是new因为valueOf使用缓存,而new一定会创建新的对象分配新的内存空间;

public class CASDemo02 {
public static void main(String[] args) {
//参数初始值 和一个版本时间戳
//Integer
AtomicStampedReference<Integer> reference = new AtomicStampedReference<>(20,1);
new Thread(()->{
//获得当前版本号就是时间戳
int stamp=reference.getStamp();
System.out.println("A线程获得当前版本号:"+stamp);
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
//期望的引用值是20 新的引用值为21 获得当前时间戳,CAS操作后,就让时间戳+1也就是新的时间戳
boolean b = reference.compareAndSet(20, 21, reference.getStamp(), reference.getStamp() + 1);
System.out.println("A线程第一次的修改是否成功:"+b);
System.out.println("A线程第一次修改后的版本=>"+reference.getStamp());
boolean b1 = reference.compareAndSet(21, 20, reference.getStamp(), reference.getStamp() + 1);
System.out.println("A线程第二次的修改是否成功:"+b1);
System.out.println("A线程第二次修改后的版本=>"+reference.getStamp());
},"A").start();
new Thread(()->{
//获得当前版本号就是时间戳
int stamp=reference.getStamp();
System.out.println("B线程获得当前版本号:"+stamp);
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
int stamp1=reference.getStamp();
System.out.println("B线程睡觉后获得当前版本号:"+stamp1);
boolean b = reference.compareAndSet(21, 20, stamp, stamp + 1);
System.out.println("B线程的修改是否成功:"+b);
System.out.println("B线程修改后的版本号=> "+reference.getStamp());
},"B").start();
}
}

多种锁含义

公平锁 :

需要排队获取锁 不能插队 先来后到

public ReentrantLock() {
sync = new NonfairSync();
}

非公平锁

不需要排队获取 可以插队 lock默认的方式就是非公平锁

public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}

可重入锁

(递归锁) 锁上加锁 获得外面一层的锁,就自动获得里面的锁,自动获得

//Synchronized
//ReentrantLock
public class Demo01 {
public static void main(String[] args) {
Phone2 phone = new Phone2();
new Thread(() -> {
phone.sms();
}, "A").start();
new Thread(() -> {
phone.sms();
}, "B").start();
}
}
class Phone1 {
//隐式
public synchronized void sms() {
System.out.println(Thread.currentThread().getName() + "发短信");
call();//锁上加锁
}
public synchronized void call() {
System.out.println(Thread.currentThread().getName() + "打电话");
}
}
class Phone2 {
private Lock lock = new ReentrantLock();
public void sms() {
lock.lock(); //显式 需要配对
try {
System.out.println(Thread.currentThread().getName() + "发短信");
call();//锁上加锁
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void call() {
lock.lock();
try {
System.out.println(Thread.currentThread().getName() + "打电话");
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}

自旋锁

(spinLock) 循环

//自旋锁
public class SpinLockDemo04 {
AtomicReference<Thread> atomicReference=new AtomicReference<>();
//加锁
public void lock1(){
Thread thread = Thread.currentThread();
//自旋锁
while (!atomicReference.compareAndSet(null,thread)){
System.out.println(Thread.currentThread().getName()+"====> lock1");
}
}
//解锁
public void unlock1(){
Thread thread = Thread.currentThread();
System.out.println(Thread.currentThread().getName()+"====> unlock1");
atomicReference.compareAndSet(thread,null);
}
}

测试

public class Test {
public static void main(String[] args) {
//自旋锁CAS
SpinLockDemo04 lock = new SpinLockDemo04();
new Thread(() -> {
lock.lock1();
try {
TimeUnit.MILLISECONDS.sleep(1);
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock1();
}
}, "A").start();
new Thread(() -> {
lock.lock1();
try {
TimeUnit.MILLISECONDS.sleep(1);
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock1();
}
}, "B").start();
}
}

死锁

死锁问题

public class DeadLockDemo {
public static void main(String[] args) {
String lockA="lockA";
String lockB="lockB";
new Thread(new MyThead(lockA,lockB),"T1").start();
new Thread(new MyThead(lockB,lockA),"T2").start();
}
}
class MyThead implements Runnable{
private String lockA;
private String lockB;
public MyThead(String lockA, String lockB) {
this.lockA = lockA;
this.lockB = lockB;
}
@Override
public void run() {
synchronized(lockA){
System.out.println(Thread.currentThread().getName()+"_lock: "+lockA+"=>"+lockB);
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized(lockB){
System.out.println(Thread.currentThread().getName()+"_lock: "+lockB+"=>"+lockA);
}
}
}
}

排除死锁

使用 jps-l定位进程号

使用jstack 进程号找到进程号

posted on   万万没想到啊i  阅读(13)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 因为Apifox不支持离线,我果断选择了Apipost!
· 通过 API 将Deepseek响应流式内容输出到前端
< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

点击右上角即可分享
微信分享提示