进程:一个程序。一个进程往往可以包含多个线程,至少包含一个
java默认有几个线程?2个:main、GC
线程:开启了一个程序,打字,自动保存(线程负责)
对于java而言:Thread、Runnable、Callable
Java可以开启线程吗?不能
并发:多线程操作同一个资源
CPU一核
并行:多个人一起行走
CPU多核
public static void main(String[] args) {
// 获取cpu的核数
// CPU密集型,IO密集型
System.out.println(Runtime.getRuntime().availableProcessors());
}
线程有几个状态:6个
new新生
runnable运行
blocked阻塞
waiting等待,死死等待
timed_waiting超时等待
terminated终止
wait、sleep的区别
1.来自不同的类
wait是Object类
sleep是Thread类
TimeUnit.Days.sleep(1)
休眠一天
2.关于锁的释放
wait会释放锁,sleep睡觉了,抱着锁睡觉,不会释放
3.使用的范围是不同的
wait必须在同步代码块中
sleep可以在任何地方睡
4.是否需要捕获异常
wait也需要
sleep需要捕获异常
传统Synchronized 本质:队列、锁
可重入锁(常用,ReentrantLock()),读锁,写锁
Lock的使用:
公平锁:十分公平:可以先来后到
非公平锁:十分不公平:可以插队(默认)
Synchronized和lock的区别
锁是什么 ,如何判断锁的什么
if在多线程中会出现虚假唤醒的问题,while判断可以防止虚假唤醒
synchronized (obj){
while(<condition does not hold>)
obj.wait(timeout);
...//Perform action appropriate to condition
}
private Lock lock = new ReentrantLock();
final Condition condition = lock.newCondition();
condition.await();//等待
condition.signal();//唤醒
class Data3{//资源类Lock
private Lock lock = new ReentrantLock();
private Condition condition1 = lock.newCondition();
private Condition condition2 = lock.newCondition();
private Condition condition3 = lock.newCondition();
private int number = 1;//1A 2B 3C
public void printA(){
lock.lock();
try{
//业务, 判断->执行->通知
while(number != 1){
//等待
condition1.await();
}
System.out.println(Thread.currentThread().getName()+"=>AAAAAA");
//唤醒,唤醒指定的人,B
condition2.signal();
}catch(Exception e){
e.printStackTrace();
}finally{
lock.unlock();
}
}
}
public void printB(){...//唤醒,唤醒指定的人,C}
public void printC(){...//唤醒,唤醒指定的人,A}
1.标准情况下,两个线程先打印 发短信
public class test1 {
public static void main(String[] args) {
Phone phone = new Phone();
new Thread(()->{
phone.sendSms();
}, "A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
phone.call();
}, "B").start();
}
}
class Phone{
public synchronized void sendSms(){
System.out.println("发短信");
}
public synchronized void call(){
System.out.println("打电话");
}
}
2.sendSms延迟4秒,两个线程先打印 发短信
对于场景1和场景2,synchronized锁的对象是方法的调用者!两个方法用的是同一个锁,谁先拿到谁执行!
public class test1 {
public static void main(String[] args) {
Phone phone = new Phone();
new Thread(()->{
phone.sendSms();
}, "A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
phone.call();
}, "B").start();
}
}
class Phone{
public synchronized void sendSms(){
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发短信");
}
public synchronized void call(){
System.out.println("打电话");
}
}
3.增加一个普通方法后,先执行普通方法
public class test2 {
public static void main(String[] args) {
Phone2 phone = new Phone2();
new Thread(()->{
phone.sendSms();
}, "A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
phone.hello();
}, "B").start();
}
}
class Phone2{
public synchronized void sendSms(){
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发短信");
}
public synchronized void call(){
System.out.println("打电话");
}
public void hello(){
System.out.println("hello");
}
}
4.两个对象,两个同步方法,两把锁
结果:
打电话
发短信
/**
* 4.两个对象,两个同步方法,两把锁
*/
public class test2 {
public static void main(String[] args) {
//两个对象
Phone2 phone1 = new Phone2();
Phone2 phone2 = new Phone2();
new Thread(()->{
phone1.sendSms();
}, "A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
phone2.call();
}, "B").start();
}
}
class Phone2{
public synchronized void sendSms(){
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发短信");
}
public synchronized void call(){
System.out.println("打电话");
}
public void hello(){
System.out.println("hello");
}
}
5.增加两个静态的同步方法
结果:
发短信
打电话
/**
* 5.增加两个静态的同步方法
*/
public class test3 {
public static void main(String[] args) {
//1个对象
Phone2 phone = new Phone2();
new Thread(()->{
phone.sendSms();
}, "A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
phone.call();
}, "B").start();
}
}
class Phone3{
// synchronized 锁的对象是方法的调用者
//static 静态方法
//类一加载就有了,锁的是Class对象
public static synchronized void sendSms(){
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发短信");
}
public static synchronized void call(){
System.out.println("打电话");
}
}
6.两个对象,增加两个静态同步方法
锁的是Class,两个对象的Class模版只有一个,static
结果:
发短信
打电话
/**
* 6.两个对象,增加两个静态同步方法
*/
public class test3 {
public static void main(String[] args) {
//2个对象
Phone3 phone1 = new Phone3();
Phone3 phone2 = new Phone3();
new Thread(()->{
phone1.sendSms();
}, "A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
phone2.call();
}, "B").start();
}
}
class Phone3{
// synchronized 锁的对象是方法的调用者
//static 静态方法
//类一加载就有了,锁的是Class对象
public static synchronized void sendSms(){
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发短信");
}
public static synchronized void call(){
System.out.println("打电话");
}
}
7.一个静态同步方法,一个普通同步方法
静态方法锁的Class模版,
普通同步方法锁的是对象
结果:
打电话
发短信
/**
* 7.1个静态同步方法,一个普通同步方法
*/
public class test4 {
public static void main(String[] args) {
//1个对象
Phone4 phone = new Phone4();
new Thread(()->{
phone.sendSms();
}, "A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
phone.call();
}, "B").start();
}
}
class Phone4{
// synchronized 锁的对象是方法的调用者
//static 静态方法
//类一加载就有了,锁的是Class对象
public static synchronized void sendSms(){
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发短信");
}
public static void call(){
System.out.println("打电话");
}
}
8.一个静态同步方法,一个普通同步方法,2个对象
结果:打电话 发短信
/**
* 8.1个静态同步方法,一个普通同步方法,2个对象
*/
public class test4 {
public static void main(String[] args) {
//2个对象
Phone4 phone1 = new Phone4();
Phone4 phone2 = new Phone4();
new Thread(()->{
phone1.sendSms();
}, "A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
phone2.call();
}, "B").start();
}
}
class Phone4{
// synchronized 锁的对象是方法的调用者
//static 静态方法
//类一加载就有了,锁的是Class对象
public static synchronized void sendSms(){
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发短信");
}
public static void call(){
System.out.println("打电话");
}
}
总结
new 出来的具体一个对象
static 是Class模版
并发下,ArrayList不安全,Synchronized;
多线程会出现并发修改异常java.util.ConcurrentModificationException
解决方案:
List<String> list = new Vector<>();
List<String> list = Collection.synchronizedList(new ArrayList<>());
多个线程调用 的时候,list,读取的时候,固定,写入的时候避免覆盖,造成数据问题
读写分离
CopyOnWriteArrayList比Vector厉害在哪里?
List<String> list = new CopyOnWriteArrayList<>();
测试代码示例:
public class test5 {
public static void main(String[] args) {
List<String> list = new CopyOnWriteArrayList<>();
for (int i = 0; i < 10; i++) {
new Thread(()->{
list.add(UUID.randomUUID().toString().substring(0,5));
System.out.println(list);
},String.valueOf(i)).start();
}
}
}
解决方案:
Set<String> set = Collections.synchronizedSet(new HashSet<>());
Set<String> set = new CopyOnWriteArraySet<>();
HashSet的底层什么?
Map
ConcurrentHashMap是采用分段锁的技术!线程安全!
public class MapTest {
public static void main(String[] args) {
//默认等价于什么?new HashMap<>(16,0.75);
Map<String, String> map = new ConcurrentHashMap<>();
//加载因子0.75,初始化容量16
for (int i = 0; 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接口类似于Runnable ,因为它们都是为其实例可能由另一个线程执行的类设计的。 然而,A Runnable不返回结果,也不能抛出被检查的异常。
代码测试:
public class CallableTest {
public static void main(String[] args) throws ExecutionException, InterruptedException {
// new Thread(new MyThread()).start();
MyThread myThread=new MyThread();
FutureTask futureTask=new FutureTask(myThread);
new Thread(futureTask,"A").start();//怎么启动Callable,结果有缓存
System.out.println(futureTask.get());//get方法可能会产生阻塞
}
}
//Callable的泛型代表方法的返回值
class MyThread implements Callable<String> {
@Override
public String call() throws Exception {
System.out.println("call()");
return "Callable";
}
}
可能的缺点:
原理:
countDownLatch.countDown();
//数量-1
countDownLatch.await();
//等待计数器归0然后再向下执行
每次有线程调用countDown数量-1,假设计数器归0,才会执行await会的代码
原理
semaphore.acquire();
获取,假设已经满了,等待被释放为止!
semaphore.release();
释放,会将当前的信号量释放+1,然后唤醒等待的线程
作用:多个共享资源互斥的使用!并发限流,控制最大的线程数!
对于码农来说4个很重要的点:lambda表达式、链式编程、函数式接口 、Stream流式计算
函数式接口:只有一个方法的接口
@FunctionalInterface
public interface Runnable{
public abstract void run();
}
//超级多FunctionalInterface
//简化编程模型,在新版本的框架底层大量应用!
//foreach(消费者类的函数式接口)
都可以用lambda表达式简化
/**
* Function 函数式接口,有一个输入参数,有一个输出
* 只要是 函数型接口可以 用lambda表达式简化
*/
public class test1 {
public static void main(String[] args) {
//方式1:
// Function function = new Function() {
// @Override
// public String apply(String str) {
// return str;
// }
// };
//方式2:
Function<String, String> function = (str)->{return str;};
System.out.println(function.apply("你好"));
}
}
/**
* 断定型接口:有一个输入参数,返回值只能是布尔值
*/
public class test2 {
public static void main(String[] args) {
//判断字符串是否为空
//方式1:
// Predicate predicate = new Predicate() {
// @Override
// public boolean test(String s) {
// return s.isEmpty();
// }
// };
//方式2:
Predicate<String> predicate = (str)->{return str.isEmpty();};
System.out.println(predicate.test(""));
}
}
/**
* Consumer 消费型 接口:只有输入,没有返回值
*/
public class test3 {
public static void main(String[] args) {
//方式1:
// Consumer consumer = new Consumer() {
// @Override
// public void accept(String str) {
// System.out.println(str);
// }
// };
//方式2:
Consumer<String> consumer = (str)->{System.out.println(str);};
consumer.accept("打印字符串");
}
}
/**
* Supplier 供给型 接口 没有参数,只有返回值
*/
public class test4 {
public static void main(String[] args) {
//方式1:
// Supplier supplier= new Supplier() {
// @Override
// public Integer get() {
// System.out.println("get()");
// return 1024;
// }
// };
//方式2:
Supplier supplier= ()->{return 1024;};
System.out.println(supplier.get());
}
}
大数据:存储+计算
集合、Mysql本质就是存储东西的
计算都是应该交给流来操作
public class test5 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<String>();
list.add("a");
list.add("a");
list.add("b");
//计算交给Stream流
list.stream()
.filter(u->{return u.equals("a");})
.map(u->{return u.toUpperCase();})
.limit(1)
.forEach(System.out::println);
}
}
另一个例子:
将list转换map
//声明一个List集合
List<Person> list = new ArrayList();
list.add(new Person("1001", "小A"));
list.add(new Person("1002", "小B"));
list.add(new Person("1003", "小C"));
System.out.println(list);
//将list转换map
//1.重复时用后面的value 覆盖前面的value
Map<String, String> map = list.stream().collect(Collectors.toMap(Person::getId, Person::getName,(key1 , key2)-> key2 ));
System.out.println(map);
//2.重复时将前面的value 和后面的value拼接起来;
Map<String, String> map = list.stream().collect(Collectors.toMap(Person::getId, Person::getName,(key1 , key2)-> key1+","+key2 ));
System.out.println(map);
中文:分支合并
特点:工作窃取,提高工作效率,不让线程等待
这个里面维护的都是双端队列
求和计算的任务有两种方法:
import java.util.concurrent.ForkJoinTask;
import java.util.concurrent.RecursiveTask;
/**
* 求和计算的任务
* 3000 6000(ForkJoin) 9000(Stream并行流)
* 如何使用forkJoin
* 1.forkJoinPool通过它来执行
* 2.计算任务forkJoinPool.execute(ForkJoinTask task)
* 3.计算类继承RecursiveTask
* */
public class ForkJoinDemo extends RecursiveTask<Long> {
private Long start;
private Long end;
private Long temp=100000L;
public ForkJoinDemo(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{
//分并计算 forkjoin 递归
long middle = (start + end) / 2;//中间值
ForkJoinDemo task1 = new ForkJoinDemo(start, middle);
task1.fork();//拆分任务,把任务压入线程队列
ForkJoinDemo task2 = new ForkJoinDemo(middle+1, end);
task2.fork();
return task1.join()+ task2.join();
}
}
}
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.stream.LongStream;
/*
* 同一个任务,别人
* */
//3000 6000(ForkJoin) 9000(Stream并行流)
public class Test {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//test1();//18515
//test2();//16583
test3();//1565
}
public static void test1(){
long start=System.currentTimeMillis();
Long sum=0L;
for (Long i = 1L; i <= 10_0000_0000; i++) {
sum+=i;
}
long end=System.currentTimeMillis();
System.out.println("sum="+sum +"时间:"+(end-start));
}
public static void test2() throws ExecutionException, InterruptedException {
long start=System.currentTimeMillis();
ForkJoinPool forkJoinPool = new ForkJoinPool();
ForkJoinTask<Long> forkJoinDemo = new ForkJoinDemo(0L, 10_0000_0000L);
ForkJoinTask<Long> submit = forkJoinPool.submit(forkJoinDemo);
Long sum = submit.get();
long end=System.currentTimeMillis();
System.out.println("sum="+sum+"时间:"+(end-start));
}
public static void test3(){
long start=System.currentTimeMillis();
//Stream并行流
Long sum=LongStream.rangeClosed(0L,10_0000_0000L).parallel().reduce(0,Long::sum);
long end=System.currentTimeMillis();
System.out.println("sum="+sum+"时间:"+(end-start));
}
}
Future设计的初衷,对将来的某个事件的结果进行建模
可以想象成Ajax
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
//异步调用:CompletableFuture
/**
* 异步执行
* 成功回调
* 失败回调
* */
public class Demo01 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//没有返回值的runAsync异步回调
// CompletableFuture completableFuture = CompletableFuture.runAsync(()->{
// try {
// TimeUnit.SECONDS.sleep(2);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
// System.out.println(Thread.currentThread().getName()+"runAsync"+":Void");
// });
// System.out.println("1111111");
// completableFuture.get();//获取阻塞执行结果
//有返回值的异步回调 supplyAsync 异步回调
//ajax:成功和失败的回调
//返回的错误信息;
CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(()->{
System.out.println(Thread.currentThread().getName()+"supplyAsync"+":Integer");
return 1024;
});
System.out.println(completableFuture.whenComplete((t, u) -> {
//t:正常的返回结果
//u:错误信息
System.out.println(t + "\t" + u);
}).exceptionally((e) -> {
e.getMessage();//打印错误信息
return 233;// 可以获取到错误的返回结果
}).get());
}
}
Volatile是Java虚拟机提供轻量级的同步机制
JMM 是java内存模型,不存在的东西,是一种概念或者是约定
关于JMM的一些同步的约定
8种操作
内存交互操作有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操作之前,必须把此变量同步回主内存
如果不可lock和Synchronized,怎么保证原子性呢?
使用原子类作为volatile的补充,解决原子性问题
//volatile 不保证原子性
//原子类的Integer
private volatile static AtomicInteger num=new AtomicInteger(0);
这些类的底层都直接和操作系统挂钩!在内存中修改值!Unsafe类是一个很特殊的存在!
指令重排:写的程序,计算机并不是按照你写的顺序执行的。
源代码–>编译器优化的重排–>指令并行也可能会得排–>内存系统也会重排–>执行
处理器在进行指令重排的时候,考虑:数据之间的依赖性
Volatile是可以保证可见性,不能保证原子性,由于内存屏障,可以保证避免指令重排的现象产生!在单例模式使用最多
点击这里,可以参考笔者的另外一篇设计模式的博客中单例模式的解析
枚举有参构造方法能够解决单例模式的不安全
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
//enum 是一个什么?本身也是一个Class类
public enum EnumSingle {
INSTANCE;
public EnumSingle getInstance(){
return INSTANCE;
}
}
class Test{
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
EnumSingle instance1 = EnumSingle.INSTANCE;
Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(String.class,int.class);
declaredConstructor.setAccessible(true);
EnumSingle enumSingle1 = declaredConstructor.newInstance();
EnumSingle enumSingle2 = declaredConstructor.newInstance();
System.out.println(enumSingle1);
System.out.println(enumSingle2);
}
}
CAS是Compare And Set的一个简称,如下理解:
已知当前内存里面的值current和预期要修改成的值new传入
内存中AtomicInteger对象地址对应的真实值(因为有可能别修改)real与current对比,相等表示real未被修改过,是“安全”的,将new赋给real结束然后返回;不相等说明real已经被修改,结束并重新执行1直到修改成功
优点:
CAS相比Synchronized,避免了锁的使用,总体性能比Synchronized高很多。
CAS:比较当前工作内存中的值和主内存中的值,如果这个值是期望的,那么则执行操作!如果不是就一直循环
缺点:
import java.util.concurrent.atomic.AtomicInteger;
public class CASDemo {
//CAS compareAndSet比较并交换
public static void main(String[] args) {
AtomicInteger atomicInteger=new AtomicInteger(2020);
//期望,更新
// public final boolean compareAndSet(int expect, int update)
//如果我期望的值达到了,那么就更新,否则就不更新,CAS 是CPU的并发原语!
//==============捣乱的线程==============
System.out.println(atomicInteger.compareAndSet(2020, 2021));
System.out.println(atomicInteger.get());
//atomicInteger.getAndIncrement();
System.out.println(atomicInteger.compareAndSet(2021, 2020));
System.out.println(atomicInteger.get());
//==============期望的线程==============
System.out.println(atomicInteger.compareAndSet(2020, 6666));
System.out.println(atomicInteger.get());
}
}
解决ABA问题,引入原子引用!对应的思想:乐观锁!也就是带版本号的原子操作
import java.util.Date;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicStampedReference;
public class CASDemo {
//CAS compareAndSet比较并交换
public static void main(String[] args) {
//AtomicInteger atomicInteger=new AtomicInteger(2020);
//AtomicStampedReference注意,如果泛型是一个包装类,注意对象的引用问题
//正常在业务操作,这里面比较的都是一个人对象
AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(1, 1);
new Thread(()->{
int stamp = atomicStampedReference.getStamp();//获得版本号
System.out.println("A1=>"+stamp);
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
atomicStampedReference.compareAndSet(1,2,atomicStampedReference.getStamp(),atomicStampedReference.getStamp()+1);
System.out.println("A2=>"+atomicStampedReference.getStamp());
atomicStampedReference.compareAndSet(2,1,atomicStampedReference.getStamp(),atomicStampedReference.getStamp()+1);
System.out.println("A3=>"+atomicStampedReference.getStamp());
},"A").start();
//乐观锁的原理相同
new Thread(()-> {
int stamp = atomicStampedReference.getStamp();//获得版本号
System.out.println("B1=>" + stamp);
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
atomicStampedReference.compareAndSet(1, 6, stamp, stamp + 1);
System.out.println("B2=>" + atomicStampedReference.getStamp());
},"B").start();
}
}
//1. 非公平
public ReentrantLock() {
sync = new NonfairSync();
}
//2. 如果想设置公平锁(使用Lock锁的重载方法)
//Lock lock=new ReentrantLock(true);
//公平锁
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
synchronized形式的可重入锁
//synchronized
public class Demo01 {
public static void main(String[] args) {
Phone phone = new Phone();
new Thread(()->{
phone.sms();
},"A").start();
new Thread(()->{
phone.sms();
},"B").start();
}
}
class Phone{
public synchronized void sms(){
System.out.println(Thread.currentThread().getName()+"sms");
call();//这里也有锁
}
public synchronized void call(){
System.out.println(Thread.currentThread().getName()+"call");
}
}
lock形式的可重入锁
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Demo02 {
public static void main(String[] args) {
Phone phone = new Phone();
new Thread(()->{
phone.sms();
},"A").start();
new Thread(()->{
phone.sms();
},"B").start();
}
}
class Phone2{
Lock lock=new ReentrantLock();
public void sms(){
lock.lock();
try {
System.out.println(Thread.currentThread().getName()+"sms");
call();//这里也有锁
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void call(){
lock.lock();//细节问题
//lock锁必须配对一把钥匙,否则就会死在里面
try {
System.out.println(Thread.currentThread().getName()+"call");
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
自定义自旋锁
import java.util.concurrent.atomic.AtomicReference;
/**
* 自旋锁
* */
public class SpinlockDemo {
//Thread null
AtomicReference<Thread> atomicReference=new AtomicReference<>();
//加锁
public void myLock(){
Thread thread=Thread.currentThread();
System.out.println(thread.getName()+"==>"+"myLock");
//自旋锁
while (!atomicReference.compareAndSet(null,thread)){
}
}
//解锁
public void myUnLock(){
Thread thread=Thread.currentThread();
System.out.println(thread.getName()+"==>"+"myUnLock");
atomicReference.compareAndSet(thread,null);
}
}
使用自旋锁
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
public class TestSpinLock {
public static void main(String[] args) {
//底层使用的自旋锁
SpinlockDemo spinlockDemo=new SpinlockDemo();
new Thread(()->{
spinlockDemo.myLock();
try {
TimeUnit.SECONDS.sleep(3);
} catch (Exception e) {
e.printStackTrace();
} finally {
spinlockDemo.myUnLock();
}
},"T1").start();
new Thread(()->{
spinlockDemo.myLock();
try {
TimeUnit.SECONDS.sleep(1);
} catch (Exception e) {
e.printStackTrace();
} finally {
spinlockDemo.myUnLock();
}
},"T2").start();
}
}
jps -l
定位进程号jstack
进程号查看堆栈信息,找到死锁问题