本文是参照b站狂神说java总结的一些知识点
JUC
synchronize八锁问题
- synchronize 的六种锁的情况 详情请看代码里面的注释
第一种
package lock;
import java.util.concurrent.TimeUnit;
/**
* synchronize 锁住两个方法
* 结果先发短信,在打电话
* 原因: 因为synchronize 在方法上,锁住的是对象,你new了几个对象就几个锁
* 那么也就是说现在只有一个锁,而且程序顺序执行,发短信的线程先拿到锁,因为此时不是异常,
* synchronize只有在异常的时候才会释放锁,所以不会释放锁,执行完之后第二个在执行
*/
public class test1 {
public static void main(String[] args) throws InterruptedException {
Phone p = new Phone();
new Thread(() -> {
p.send();
}, "A").start();
TimeUnit.SECONDS.sleep(1);
new Thread(() -> {
p.call();
}, "B").start();
}
}
class Phone {
public synchronized void send() {
try {
TimeUnit.SECONDS.sleep(2);
System.out.println("发短信");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public synchronized void call() {
System.out.println("打电话");
}
}
```
第二种
package lock;
import java.util.concurrent.TimeUnit;
/**
* 还是synchronizie 锁住了两个方法,但是new了两个对象,所以说就有两个锁,那么就会自己管自己的
* 结果是 打电话,发短信
*/
public class test2 {
public static void main(String[] args) throws InterruptedException {
Phone1 p = new Phone1();
Phone1 p2 = new Phone1();
new Thread(() -> {
p.send();
}, "A").start();
TimeUnit.SECONDS.sleep(1);
new Thread(() -> {
p2.call();
}, "B").start();
}
}
class Phone1 {
public synchronized void send() {
try {
TimeUnit.SECONDS.sleep(2);
System.out.println("发短信");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public synchronized void call() {
System.out.println("打电话");
}
}
第三种
package lock;
import java.util.concurrent.TimeUnit;
/**
* 一个被synchronize锁住,一个没锁,所以就是正常执行
* 打电话,发短信
*/
public class test3 {
public static void main(String[] args) throws InterruptedException {
Phone3 p = new Phone3();
new Thread(() -> {
p.send();
}, "A").start();
TimeUnit.SECONDS.sleep(1);
new Thread(() -> {
p.call();
}, "B").start();
}
}
class Phone3 {
public synchronized void send() {
try {
TimeUnit.SECONDS.sleep(2);
System.out.println("发短信");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void call() {
System.out.println("打电话");
}
}
第四种
package lock;
import java.util.concurrent.TimeUnit;
/**
* synchronize锁住静态方法
* 发短信,打电话
* synchronize锁住静态方法的时候其实是锁住的Class模板,锁住的是这个类,那么此时就只有一个锁
*/
public class test4 {
public static void main(String[] args) throws InterruptedException {
Phone4 p = new Phone4();
new Thread(() -> {
p.send();
}, "A").start();
TimeUnit.SECONDS.sleep(1);
new Thread(() -> {
p.call();
}, "B").start();
}
}
class Phone4 {
public static synchronized void send() {
try {
TimeUnit.SECONDS.sleep(2);
System.out.println("发短信");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static synchronized void call() {
System.out.println("打电话");
}
}
第五种
package lock;
import java.util.concurrent.TimeUnit;
/**
* synchronize锁住的还是static 但是现在new了两个对象,但是只有一个Class模板。所以说仍然只有一把锁
* 发短信,打电话
*/
public class test5{
public static void main(String[] args) throws InterruptedException {
Phone5 p = new Phone5();
Phone5 p2 = new Phone5();
new Thread(() -> {
p.send();
}, "A").start();
TimeUnit.SECONDS.sleep(1);
new Thread(() -> {
p2.call();
}, "B").start();
}
}
class Phone5 {
public static synchronized void send() {
try {
TimeUnit.SECONDS.sleep(2);
System.out.println("发短信");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static synchronized void call() {
System.out.println("打电话");
}
}
第六种
package lock;
import java.util.concurrent.TimeUnit;
/**
* 一个锁住了static方法,一个锁住了普通方法, 所以说一个锁在了Class 一个锁住了对象,有两把锁
* 打电话,发短信
*/
public class test6{
public static void main(String[] args) throws InterruptedException {
Phone6 p = new Phone6();
new Thread(() -> {
p.send();
}, "A").start();
TimeUnit.SECONDS.sleep(1);
new Thread(() -> {
p.call();
}, "B").start();
}
}
class Phone6 {
public static synchronized void send() {
try {
TimeUnit.SECONDS.sleep(2);
System.out.println("发短信");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public synchronized void call() {
System.out.println("打电话");
}
}
ArrayList
线程不安全,在添加的时候
package JUCMY;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.Vector;
public class Arrayscon {
public static void main(String[] args) {
List<String> a = new ArrayList<>();
for (int i = 1; i <= 10; i++) {
new Thread(() -> {
a.add(UUID.randomUUID().toString().substring(0, 5));
System.out.println(a);
},String.valueOf(i)).start();
new Thread(() -> {
a.add(UUID.randomUUID().toString().substring(0, 5));
System.out.println(a);
},String.valueOf(i)).start();
}
}
}
这个时候可以用Vector 来解决
Vector是在jdk1.0就提出了,用synchronized 解决的
解决方案
-
用vertor
-
用Colloctions.synchronizedList 底层也是synchronize 类似于 CopyOnWriteArrayList
-
用JUC下的CopyOnWriteArrayList()
-
final transient Object lock = new Object(); /** The array, accessed only via getArray/setArray. */ private transient volatile Object[] array;
-
写入时复制,在写入的时候复制一份,避免造成数据覆盖,多线程操作 jdk 12,之前的可能是有一个lock锁,不是synchronize
public boolean add(E e) { synchronized (lock) { Object[] es = getArray(); int len = es.length; es = Arrays.copyOf(es, len + 1); es[len] = e; setArray(es); return true; } }
-
Set
底层其实就是个hashmap用到了 map的key
hashmap
面试
读写锁(共享锁)
ReadWriteLock 是一种锁 粒度更高 相比于普通的锁 ReentrantLock
package JUCMY;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class ReadWriteLockDemo {
public static void main(String[] args) {
MyLock1 myLock = new MyLock1();
for(int i=1;i<=5;i++){
int finalI = i;
new Thread(()->{
myLock.put(finalI +"",finalI+"");
},String.valueOf(i)).start();
}
for(int i=1;i<=5;i++){
int finalI = i;
new Thread(()->{
myLock.get(finalI +"");
},String.valueOf(i)).start();
}
}
}
class MyLock{
private Map<String,String> m = new HashMap<>();
public void put(String a,String b){
System.out.println(Thread.currentThread().getName()+"写入");
m.put(a,b);
System.out.println(Thread.currentThread().getName()+"写入OK");
}
public void get(String key){
System.out.println(Thread.currentThread().getName()+"读入");
m.get(key);
System.out.println(Thread.currentThread().getName()+"读入OK");
}
}
class MyLock1{
private Map<String,String> m = new HashMap<>();
private ReadWriteLock r = new ReentrantReadWriteLock();
public void put(String a,String b){
r.writeLock().lock();
System.out.println(Thread.currentThread().getName()+"写入");
m.put(a,b);
System.out.println(Thread.currentThread().getName()+"写入OK");
r.writeLock().unlock();
}
public void get(String key){
System.out.println(Thread.currentThread().getName()+"读入");
m.get(key);
System.out.println(Thread.currentThread().getName()+"读入OK");
}
}
阻塞队列
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-F3HyLJCy-1586745559949)(C:\Users\你大爷\AppData\Roaming\Typora\typora-user-images\image-20200408152448236.png)]
四组API
方式 | 抛出异常 | 有返回值不抛出异常 | 阻塞等待 | 超时等待 |
---|---|---|---|---|
取出 | add | offer | put | offer |
添加 | remove | poll | take | poll |
查看队首 | element | peek | ||
package JUCMY;
import java.net.PortUnreachableException;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.TimeUnit;
public class Bq {
public static void main(String[] args) throws InterruptedException {
test4();
}
/**
* 抛出异常
*/
public static void test1(){
ArrayBlockingQueue<Object> objects = new ArrayBlockingQueue<>(3);
System.out.println(objects.add(1));
System.out.println(objects.add(1));
System.out.println(objects.add(1));
System.out.println(objects.add(1));
System.out.println("--------------------");
System.out.println(objects.remove());
System.out.println(objects.remove());
System.out.println(objects.remove());
System.out.println(objects.remove());
}
/**
* 不抛出异常 会有返回值 根据返回值来判断是不是成功
*/
public static void test2(){
ArrayBlockingQueue<Object> objects = new ArrayBlockingQueue<>(3);
System.out.println(objects.offer(1));
System.out.println(objects.offer(1));
System.out.println(objects.offer(1));
System.out.println(objects.offer(1));
System.out.println("--------------------");
System.out.println(objects.poll());
System.out.println(objects.poll());
System.out.println(objects.poll());
System.out.println(objects.poll());
}
/**
* 阻塞等待
*
*/
public static void test3() throws InterruptedException {
ArrayBlockingQueue<Object> objects = new ArrayBlockingQueue<>(3);
objects.put(1);
objects.put(1);
objects.put(1);
System.out.println(objects.take());
System.out.println(objects.take());
System.out.println(objects.take());
System.out.println(objects.take());
}
/**
* 超时等待
*/
public static void test4() throws InterruptedException {
ArrayBlockingQueue<Object> objects = new ArrayBlockingQueue<>(3);
objects.offer(1,2, TimeUnit.SECONDS);
objects.offer(1,2, TimeUnit.SECONDS);
objects.offer(1,2, TimeUnit.SECONDS);
objects.poll(2,TimeUnit.SECONDS);
objects.poll(2,TimeUnit.SECONDS);
objects.poll(2,TimeUnit.SECONDS);
objects.poll(2,TimeUnit.SECONDS);
}
}
同步队列
特点就是 只能放一个 存进去了 就一定要取出来 才能再存第二个(在一个线程中),如果是多个线程会发生错误,并且只要是存进去的一定要取出来
package JUCMY;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.TimeUnit;
public class SynQueue {
public static void main(String[] args) throws InterruptedException {
SynchronousQueue<Integer> integers = new SynchronousQueue<>();
new Thread(()-> {
try {
System.out.println("放入一个 1");
integers.put(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
,"T1").start();
new Thread(()->{
try {
System.out.println("2存");
integers.put(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
},"T2").start();
new Thread(()->{
try {
System.out.println("取出来");
integers.take();
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
integers.take();
}
}
线程池
线程池 : 三大方法 七大参数 四种拒绝策略
线程池就是一个池子,为了提高效率,节省资源,只要我有线程的时候,那么就是从池子里面拿
可以线程复用,便于管理
三大方法
package JUCMY;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class PoolTest {
public static void main(String[] args) {
// ExecutorService executorService = Executors.newSingleThreadExecutor();
ExecutorService executorService = Executors.newFixedThreadPool(5);
// ExecutorService executorService = Executors.newCachedThreadPool();
for (int i = 0; i < 10; i++) {
executorService.execute(()->{
System.out.println(Thread.currentThread().getName()+"k");
});
}
//线程池用完了要关闭
executorService.shutdown();
}
}
七大参数
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
public ThreadPoolExecutor(int corePoolSize, //核心线程数
int maximumPoolSize,//最大线程数
long keepAliveTime,//存活时间 超时了没人调用就释放 在max + queue都满了的时候
TimeUnit unit,//单位
BlockingQueue<Runnable> workQueue,//阻塞队列
ThreadFactory threadFactory,//线程工厂
RejectedExecutionHandler handler)//拒绝策略
{
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
四种
四种拒绝策略
/**
* new ThreadPoolExecutor.AbortPolicy()//如果超过了 max + queue 就抛出异常
* new ThreadPoolExecutor.CallerRunsPolicy()// 超过max + queue 的哪来的回哪去 一般是main线程
* new ThreadPoolExecutor.DiscardPolicy()// 超过max + queue 的满了 丢掉任务 不会抛出异常
* new ThreadPoolExecutor.DiscardOldestPolicy()// 超过max + queue 的 尝试和最早的那个竞争,竞争不到还是会丢掉
*/
最大线程到底如何定义 (用来调优的)
- CPU密集型: 几何就是几
- IO密集型 : 看有多少io的任务 比较耗时 一般设置两倍 >这个数
首先要看你的代码是CPU密集型还是IO密集型
如果是cpu密集型 那么肯定是电脑有几个cpu就开几个线程,但是如果线程数远远大于cpu的核心数量,那么反而会效率下降,因为切换线程也是需要消耗资源的
如果是io密集型,那么 看有多少io的任务 比较耗时 一般设置两倍 >这个数 即可以
新时代程序员必会的四项技术
- 函数式接口
- 链式编程
- lambda表达式
- Stream 计算
四大函数式接口
函数式接口 : 接受参数,返回类型 : 在接口中只有一个函数
@FunctionalInterface
public interface Function<T, R> {
/**
* Applies this function to the given argument.
*
* @param t the function argument
* @return the function result
*/
R apply(T t);
}
有了函数式接口,就可以和lambda表达式一起使用简化代码
package JUCMY;
import java.util.function.Function;
public class FunctionTest {
public static void main(String[] args) {
Function<String, String> function = new Function<>() {
@Override
public String apply(String s) {
return s;
}
};
Function a = (str)->{return str;
};
System.out.println(a.apply("zxc"));
System.out.println(function.apply("asd"));
}
}
断定型接口 : 接受参数,返回boolean类型
package JUCMY.InterfaceTest;
import java.util.function.Predicate;
public class PredictTest {
public static void main(String[] args) {
Predicate<String> predicate = new Predicate<>() {
@Override
public boolean test(String s) {
return false;
}
};
System.out.println(predicate.test("asd"));
}
}
消费型接口 : 只接受参数,无返回值
package JUCMY.InterfaceTest;
import java.util.function.Consumer;
public class ConsummerTest {
public static void main(String[] args) {
//匿名内部类
Consumer<String> consumer = new Consumer<>() {
@Override
public void accept(String s) {
System.out.println(s);
}
};
}
}
供给型接口:不接受参数,返回类型
package JUCMY.InterfaceTest;
import java.util.function.Supplier;
public class SupplyTest {
public static void main(String[] args) {
Supplier<String> supplier = new Supplier<>() {
@Override
public String get() {
return "sad";
}
};
System.out.println(supplier.get());
}
}
Stream 计算 效率很高
package JUCMY.StreamCopu;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class Test {
public static void main(String[] args) {
User a = new User(1, "a", 21);
User b = new User(2, "b", 22);
User c = new User(3, "c", 23);
User d = new User(4, "d", 24);
User e = new User(6, "e", 25);
List<User> users = Arrays.asList(a, b, c, d, e);
users.stream()
.filter(u->{return u.getId()%2==0;})
.filter(u->{return u.getAge()>23;})
.map(u->{return u.getName().toUpperCase();})
.sorted((u1,u2)->{return u2.compareTo(u1);})
.limit(1)
.forEach(u->{
System.out.println(u);
});
}
}
ForkJoin
分支计算,分解任务,把一个大任务拆分成多个小任务,然后在进行计算,提高效率
eg : 计算1+…+10亿
三种方法
- 正常算
- 使用ForkJoin 因为有中间量 temp 可以调优
- 使用Stream 并行计算 速度是最快的
package JUCMY.ForkJoinTest;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.stream.LongStream;
public class ThreeCoders {
public static void main(String[] args) throws ExecutionException, InterruptedException {
// test1();//7991
// test2();//4634
test3();//308
}
public static void test1() {
long l = System.currentTimeMillis();
Long sum = 0L;
for (Long i = 0L; i <= 10_0000_0000; i++) {
sum += i;
}
long l1 = System.currentTimeMillis();
System.out.println(sum + "--------------- " + (l1 - l));
}
public static void test2() throws ExecutionException, InterruptedException {
long l = System.currentTimeMillis();
Long sum = 0L;
ForkJoinPool forkJoinPool = new ForkJoinPool();
ForkJoinTa forkJoinTa = new ForkJoinTa(0L, 10_0000_0000L);
ForkJoinTask<Long> submit = forkJoinPool.submit(forkJoinTa);
Long aLong = submit.get();
sum = System.currentTimeMillis();
long l1 = System.currentTimeMillis();
System.out.println(sum + "--------------- " + (l1 - l));
}
public static void test3() {
long l = System.currentTimeMillis();
Long sum = 0L;
long sum1 = LongStream.range(0L, 10_0000_0000).parallel().reduce(0, Long::sum);
long l1 = System.currentTimeMillis();
System.out.println(sum1 + "--------------- " + (l1 - l));
}
}
ForkJoin使用步骤
- 继承RecursiveTask
- 重写compute 方法
- 类似归并的思想,fork() 拆解任务 join()返回结果
- new ForkJoinTaskPoll submit 异步提交 execute 是执行
package JUCMY.ForkJoinTest;
import java.util.concurrent.RecursiveTask;
public class ForkJoinTa extends RecursiveTask<Long> {
private Long start ;
private Long end;
private Long temp = 10000L;
public ForkJoinTa(Long start, Long end) {
this.start = start;
this.end = end;
}
@Override
protected Long compute() {
if (end - start < temp){
Long sum = 0L;
for (Long i = start; i <= end; i++) {
sum += i;
}
return sum;
}else{
//分支计算
Long middle = (start + end)/2;
ForkJoinTa forkJoinTask = new ForkJoinTa(start, middle);
ForkJoinTa forkJoinTask1 = new ForkJoinTa(middle + 1, end);
//拆解任务
forkJoinTask.fork();
forkJoinTask1.fork();
//并行计算
return forkJoinTask.join() + forkJoinTask1.join();
}
}
}
JMM(java内存模型 是不存在的东西,是一个约定)
volatile关键字 是java虚拟机提供的轻量级虚拟机制
- 保证可见性
- 不保证原子性
- 禁止指令重排
约定~~~ 注意那个图 先store 在write
关于JMM的一些约定
- 加锁前必须要从内存中读取变量到工作内存
- 解锁前必须要把线程内存中的变量写回内存
- 加锁和解锁的是同一把锁
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-i8ko8Dih-1586745559956)(C:\Users\你大爷\AppData\Roaming\Typora\typora-user-images\image-20200411083241583.png)]
这里write 和 load 写反了!!
内存交互操作
内存交互操作有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
package JUCMY;
import java.util.concurrent.TimeUnit;
public class kejianxing {
private static volatile int num = 0;
public static void main(String[] args) throws InterruptedException {
new Thread(()->{
Thread.currentThread().getName();
while (num ==0){
}
}).start();
TimeUnit.SECONDS.sleep(3);
num = 1;
System.out.println(num + " --"+Thread.currentThread().getName());
}
}
验证非原子性和原子类
package JUCMY;
public class notAomic {
private volatile static int num = 0;
public static void add(){
num++;
}
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
new Thread(()->{
for (int j = 0; j < 200; j++) {
add();
}
}).start();
}
//java中默认一直存在的的两个线程是 main 和 gc
while (Thread.activeCount() > 2){
Thread.yield();
}
System.out.println(Thread.currentThread().getName() +" "+ num);
}
}
可以看到加了volatile 之后还是不行 还是不能保证原子性,因为num++就不是
原子性的,并且你也没有加锁,因为只有在加锁和解锁的时候才会进行和内存进行的交互
解决办法
- 加锁
- synchronized
package JUCMY;
public class notAomic {
private volatile static int num = 0;
public synchronized static void add(){
num++;
}
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
new Thread(()->{
for (int j = 0; j < 200; j++) {
add();
}
}).start();
}
//java中默认一直存在的的两个线程是 main 和 gc
while (Thread.activeCount() > 2){
Thread.yield();
}
System.out.println(Thread.currentThread().getName() +" "+ num);
}
}
- 使用原子类(就不需要使用锁了)
package JUCMY;
public class notAomic {
private volatile static int num = 0;
public synchronized static void add(){
num++;
}
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
new Thread(()->{
for (int j = 0; j < 200; j++) {
add();
}
}).start();
}
//java中默认一直存在的的两个线程是 main 和 gc
while (Thread.activeCount() > 2){
Thread.yield();
}
System.out.println(Thread.currentThread().getName() +" "+ num);
}
}
指令重排详解
volatile 可以避免指令重排
内存屏障( 内存屏障(memory barrier)是一个CPU指令 ),CPU指令 作用:
- 保证特定的操作执行顺序
- 可以保证某些变量的内存可见性(利用这些特性 volatile实现了禁止指令重排)
原理: 其实就是在使用volatile的前后都使用了内存屏障,保证了操作的执行顺序
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Eobu6iLi-1586745559958)(C:\Users\你大爷\AppData\Roaming\Typora\typora-user-images\image-20200412094603987.png)]
单例模式
CAS
在面试笔记中有,这里不再赘述
怎么解决ABA问题
加入版本号 java
package JUCMY;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicStampedReference;
public class CAStest {
public static void main(String[] args) {
AtomicStampedReference<Integer> integerAtomicReference = new AtomicStampedReference<>(1,1);
new Thread(()->{
int stamp = integerAtomicReference.getStamp();
System.out.println("a0----"+stamp);
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(integerAtomicReference.compareAndSet(1, 2,
integerAtomicReference.getStamp(), integerAtomicReference.getStamp() + 1));
System.out.println("a1---"+integerAtomicReference.getStamp());
System.out.println(integerAtomicReference.compareAndSet(2, 1, integerAtomicReference.getStamp(),
integerAtomicReference.getStamp() + 1));
System.out.println("a2----"+integerAtomicReference.getStamp());
},"a").start();
new Thread(()->{
int stamp = integerAtomicReference.getStamp();
System.out.println("b----"+stamp);
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(integerAtomicReference.compareAndSet(1, 3, stamp,
integerAtomicReference.getStamp() + 1));
System.out.println("b2---"+integerAtomicReference.getStamp());
},"b").start();
}
}
java手写自旋锁
package JUCMY.PackLock;
import java.util.concurrent.atomic.AtomicReference;
public class Entity {
AtomicReference<Thread> a = new AtomicReference<>();
public void mylock(Thread t){
System.out.println(t.getName()+"---> dedaosuo");
while (!a.compareAndSet(null,t)){
}
}
public void myunlock(Thread t){
while (!a.compareAndSet(t,null)){
}
System.out.println(t.getName()+"----> shifangsuo");
}
}
package JUCMY.PackLock;
import java.util.concurrent.TimeUnit;
public class Test {
public static void main(String[] args) throws InterruptedException {
Entity entity = new Entity();
new Thread(()->{
entity.mylock(Thread.currentThread());
System.out.println(Thread.currentThread().getName());
entity.myunlock(Thread.currentThread());
},"a").start();
TimeUnit.SECONDS.sleep(2);
new Thread(()->{
entity.mylock(Thread.currentThread());
System.out.println(Thread.currentThread().getName());
entity.myunlock(Thread.currentThread());
},"b").start();
}
}
排查死锁
package JUCMY;
public class TestLockDead {
public static void main(String[] args) {
String a= "asd";
String b = "dsa";
new Thread(new lockkk(a,b)).start();
new Thread(new lockkk(b,a)).start();
}
}
class lockkk implements Runnable{
private String s1 ;
private String s2 ;
public lockkk(String s1, String s2) {
this.s1 = s1;
this.s2 = s2;
}
@Override
public void run() {
synchronized (s1){
System.out.println(Thread.currentThread().getName() + "huode "+s1 + "wan "+s2);
synchronized (s2){
}
}
}
}
可以使用 jps -l 来查看进程 找到自己的进程号
在使用jstack + 进程号 来排查死锁