1、线程和进程看完狂神的视频 纯属想记录 回顾 狂神哥直链: 遇见狂神说的个人空间_哔哩哔哩_bilibili 没有不会做的事,只有不想做的事。
线程进程是操作系统中的应用程序、是资源分配的基本单位,线程是用来执行具体的任务和功能,是CPU调度和分派的最小单位
一个进程往往可以包含多个线程,至少包含一个
Java默认有几个线程?2个线程! main线程、GC线程
线程的状态
public enum State {
//运行
NEW,
//运行
RUNNABLE,
//阻塞
BLOCKED,
//等待
WAITING,
//超时等待
TIMED_WAITING,
//终止
TERMINATED;
}
线程的生命周期
新建、就绪、运行、阻塞、死亡
进程JAVA真的可以开启线程吗? 开不了的!
java是没有权限去开启线程、操作硬件的,这是一个native的一个本地方法,它可以调用底层的c++代码
public synchronized void start() {
if (threadStatus != 0)
throw new IllegalThreadStateException();
group.add(this);
boolean started = false;
try {
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
}
}
}
//这是一个C++底层,Java是没有权限操作底层硬件的
private native void start0();
并发
多线程操作同一个资源
- CPU只有一核,模拟出来多个线程,天下武功唯快不破,那么就会造成一个CPU调度速度很快假象同时执行的,实则是交替执行,并发编程的本质就是充分的利用CPU资源
并行:多个人行走
-
CPU多核的情况,多个线程同时执行,我们可以使用线程池!
获取CPU的核数
//获取CPU核数 System.out.println(Runtime.getRuntime().availableProcessors()); }
wait 跟sleep的区别
1、父类不同
wait => Object
sleep => Thread
一般企业中使用的休眠都是:
package com.neihan.dome;
import java.util.concurrent.TimeUnit;
public class CpuDome {
public static void main(String[] args) {
TimeUnit.SECONDS.sleep(1); // SEConDS 是休眠的单位
}
}
2、关于锁释放
wait: 会释放锁
sleep:不会释放锁
3、使用的范围是不同的
wait:必须在同步代码块中使用
sleep:可以在任意地方使用
Lock公平锁:十分公平,必须先来后到,先进入的线程先执行
不公平锁:十分不公平,可以插队,当前面的线程需要执行的时间很长的时候,后面的线程可以插队**(默认不公平锁)**
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-umyUkvZy-1636546509443)(C:UsersNeihanAppDataRoamingTyporatypora-user-imagesimage-20211110143059977.png)]
Synchronized与Lock的区别
1、Synchronized 是java关键字,Lock是类
2、Synchronized 无法获取判断锁的状态,Lock可以判断
3、Synchronized 会自动释放锁,Lock必须手动加锁释放锁
4、Synchronized 线程 A (获得锁->阻塞)线程 B(等待) Lock 就不一定会等下去,Lock有一个trylock去尝试获得锁,不会造成长久的等待。
4、Synchronized 是可重入锁,不可以中断,非公平的,Lock 可重入,可以判断锁,可以自己设置公平锁非公平锁
5、 Synchronized 适合锁少量代码同步问题,Lock适合锁大量的同步代码
生产者消费者的关系synchronized版本
if 判断有虚假唤醒,处理方法用 while
结论:就是用if判断的话,唤醒后线程会从wait之后的代码开始运行,但是不会重新判断if条件,直接继续运行if代码块之后的代码,而如果使用while的话,也会从wait之后的代码运行,但是唤醒后会重新判断循环条件,如果不成立再执行while代码块之后的代码块,成立的话继续wait。
这也就是为什么用while而不用if的原因了,因为线程被唤醒后,执行开始的地方是wait之后
package com.neihan.pc;
public class ConsumeAndProduct {
public static void main(String[] args) {
Data data = new Data();
new Thread(() ->{
for (int i = 0; i < 10; i++) {
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"A").start();
new Thread(() ->{
for (int i = 0; i < 10; i++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"B").start();
new Thread(() ->{
for (int i = 0; i < 10; i++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"C").start();
new Thread(() ->{
for (int i = 0; i < 10; i++) {
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"D").start();
}
}
class Data{
Integer number = 0;
public synchronized void increment() throws InterruptedException {
//判断等待
while (0!= number){
this.wait();
}
//操作
number++;
System.out.println(Thread.currentThread().getName()+"==>"+number);
//通知
this.notifyAll();
}
public synchronized void decrement() throws InterruptedException {
//判断
while (0== number){
this.wait();
}
//操作
number--;
System.out.println(Thread.currentThread().getName()+"==>"+number);
//通知
this.notifyAll();
}
}
Lock版本
package com.neihan.pc;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class LockCAP {
public static void main(String[] args) {
Data2 data = new Data2();
new Thread(() ->{
for (int i = 0; i < 10; i++) {
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"A").start();
new Thread(() ->{
for (int i = 0; i < 10; i++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"B").start();
new Thread(() ->{
for (int i = 0; i < 10; i++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"C").start();
new Thread(() ->{
for (int i = 0; i < 10; i++) {
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"D").start();
}
}
class Data2{
Integer number = 0;
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
public void increment() throws InterruptedException {
//加锁
lock.lock();
try {
while (0!= number){
//等待
condition.await();
}
//操作
number++;
System.out.println(Thread.currentThread().getName()+"==>"+number);
//通知
condition.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
//解锁
lock.unlock();
}
}
public void decrement() throws InterruptedException {
//加锁
lock.lock();
try {
while (0== number){
//等待
condition.await();
}
//操作
number--;
System.out.println(Thread.currentThread().getName()+"==>"+number);
//通知
condition.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
//解锁
lock.unlock();
}
}
}
Condition的优势
指定唤醒线程
精准的通知和唤醒的线程!
如果我们要指定通知的下一个进行顺序怎么办呢? 我们可以使用Condition来指定通知进程
package com.neihan.pc;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class JUCPC {
public static void main(String[] args) {
Data3 data3 = new Data3();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
data3.printA();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"A").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
data3.printB();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"B").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
data3.printC();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"C").start();
}
}
class Data3{
private Lock lock = new ReentrantLock();
Condition condition1 = lock.newCondition();
Condition condition2= lock.newCondition();
Condition condition3= lock.newCondition();
Integer number = 1; // 1A 2B 3C
public void printA() throws InterruptedException {
//加锁
lock.lock();
try {
while (number != 1){
//等待
condition1.await();
}
number = 2;
System.out.println(Thread.currentThread().getName()+"=>AAAAAAAAA");
//唤醒 B
condition2.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
//解锁
lock.unlock();
}
}
public void printB() throws InterruptedException {
//加锁
lock.lock();
try {
while (number != 2){
//等待
condition2.await();
}
number =3 ;
//唤醒 C
System.out.println(Thread.currentThread().getName()+"=>BBBBBBBBBBBB");
condition3.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
//解锁
lock.unlock();
}
}
public void printC() throws InterruptedException {
//加锁
lock.lock();
try {
while (number != 3){
//等待
condition3.await();
}
number = 1;
//唤醒 A
System.out.println(Thread.currentThread().getName()+"=>CCCCCCCCCCCCCC");
condition1.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
//解锁
lock.unlock();
}
}
}
8锁现象
synchronized 锁的是当前的对象
package com.neihan.lock8;
import java.util.concurrent.TimeUnit;
public class Test1 {
public static void main(String[] args) throws InterruptedException {
Send send1 = new Send();
Send send2= new Send();
new Thread(()->{
try {
send1.sendSms();
} catch (InterruptedException e) {
e.printStackTrace();
}
},"A").start();
new Thread(()->{
send2.sendEmail();
},"B").start();
}
}
class Send{
public synchronized void sendSms() throws InterruptedException {
//增加延迟 1 s
TimeUnit.SECONDS.sleep(1);
System.out.println("发送短信");
}
public synchronized void sendEmail(){
System.out.println("发送邮箱");
}
}
static synchronized(静态同步函数)
静态同步函数锁的是类的 Class
package com.neihan.lock8;
import java.util.concurrent.TimeUnit;
public class Test2 {
public static void main(String[] args) {
Send2 send = new Send2();
new Thread(()->{
try {
send.sendSms();
} catch (InterruptedException e) {
e.printStackTrace();
}
},"A").start();
new Thread(()->{
send.sendEmail();
},"B").start();
}
}
class Send2{
public static synchronized void sendSms() throws InterruptedException {
//增加延迟 1 s
TimeUnit.SECONDS.sleep(1);
System.out.println("发送短信");
}
public synchronized void sendEmail(){
System.out.println("发送邮箱");
}
}
如果我们使用一个静态同步方法、一个同步方法、一个对象调用顺序是什么?
集合不安全 List(不安全)原因:因为一个锁的是Class类的模板,一个锁的是对象的调用者。所以不存在等待,直接运行。
不安全的 触发 java.util.ConcurrentModificationException 异常 因为底层没有使用 synchronized
解决方案:
1、使用 new Vector<>()
2、Collections.synchronizedList(new ArrayList<>());
3、CopyOnWriteArrayList<>();
CopyonWriteArrayList 比 Vector 好在哪?
Vector 和 CopyonWriteArrayList 的区别
因为 Vector 使用了 synchronized 进行修饰,只要是使用了 synchronized 修饰的效率特别低下。
CopyonWriteArrayList 没有使用 底层是使用的是 Lock锁 写入时复制 效率会更加高效! COW 计算机程序设计领域的一种优化策略
package com.neihan.unsafe;
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
public class TestList {
public static void main(String[] args) {
List list = new CopyOnWriteArrayList<>();
for (int i = 1; i <=10; i++) {
new Thread(()->{
list.add(UUID.randomUUID().toString().substring(0,5));
System.out.println(list);
}).start();
}
}
}
Map(不安全)
默认加载因子是0.75,默认的初始容量是16
new HashMap<>(16,0.75);
package com.neihan.unsafe;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
public class TestMap {
public static void main(String[] args) {
// 默认等价什么?new HashMap<>(16,0.75);
//处理方式 Collections.synchronizedMap()
//处理方式 new ConcurrentHashMap<>();
Map map = new ConcurrentHashMap<>();
map.put("A","A");
map.put("S","B");
map.put("B","c");
System.out.println(map);
}
}
Set(不安全)
HashSet底层是什么?
hashSet底层就是一个HashMap;
public HashSet() {
map = new HashMap<>();
}
package com.neihan.unsafe;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CopyOnWriteArraySet;
public class TestSet {
public static void main(String[] args) {
Set
Callable
1、可以有返回值
2、可以抛出异常
3、方法不同 run() / call()
Callable
泛型就是返回值
启动需要 FutureTask 适配器 使得Callable 与 Runnable 有关联
package com.neihan.callable;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class TestCallable {
public static void main(String[] args) throws ExecutionException, InterruptedException {
MyThread myThread = new MyThread();
//使用 FutureTask 适配器 使得Callable 与 Runnable 有关联
FutureTask futureTask = new FutureTask(myThread);
new Thread(futureTask).start();
//输出返回值 可能出现阻塞 可以把获取返回值放到最后一行,或者异步通信, 不阻塞
System.out.println(futureTask.get());
}
}
class MyThread implements Callable{
@Override
public Integer call() throws Exception {
System.out.println("call()");
return 1024;
}
}
三大工具类
CountDownLatch
new CountDownLatch(5);
创建的时候 指定初始值
package com.neihan.add;
import java.util.concurrent.CountDownLatch;
public class CountDownLatchDemo {
public static void main(String[] args) throws InterruptedException {
CountDownLatch count = new CountDownLatch(5);
for (int i = 1; i <=5; i++) {
new Thread(()->{
//减一
count.countDown();
System.out.println(Thread.currentThread().getName());
},String.valueOf(i)).start();
}
//等待归零向下执行
count.await();
System.out.println("close");
}
}
CyclicBarrier
package com.neihan.add;
import java.util.Hashtable;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
public class TestCyclicBarrier {
public static void main(String[] args) {
CyclicBarrier cyclicBarrier = new CyclicBarrier(7,()->{
System.out.println("集结龙珠完成,召唤神龙");
});
for (int i = 1; i <=7; i++) {
final Integer temp = i;
new Thread(()->{
System.out.println("第"+temp+"龙珠");
try {
//等待次数到达
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
},String.valueOf(i)).start();
}
}
}
Semaphore
package com.neihan.add;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
public class TestSemaphore {
public static void main(String[] args) {
Semaphore semaphore = new Semaphore(3);
for (int i = 1; i <=6; i++) {
new Thread(()->{
try {
semaphore.acquire(); //得到
System.out.println(Thread.currentThread().getName()+"抢到车位");
TimeUnit.SECONDS.sleep(2); //休眠 2s
System.out.println(Thread.currentThread().getName()+"离开到车位");
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
//释放
semaphore.release();
}
},String.valueOf(i)).start();
}
}
}
原理
semaphore.acquire(); 得到了资源,如果资源已经使用完,就等待资释放后再进行使用
semaphore.release(); 释放,会将当前的信号量释放 +1 然后唤醒等待的线程!
作用: 多个共享资源互斥的使用! 并发限流,控制最大的线程数!
读写锁所以如果我们不加锁的情况,多线程的读写会造成数据不可靠的问题。
我们也可以采用synchronized这种重量锁和轻量锁 lock去保证数据的可靠。
但是这次我们采用更细粒度的锁:ReadWriteLock 读写锁来保证
package com.neihan.add;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class TestReadWriteLock {
public static void main(String[] args) {
MyCacheLock cache = new MyCacheLock();
for (int i = 1; i <=10; i++) {
final Integer temp = i;
new Thread(()->{
cache.write(temp+"",temp+"");
},String.valueOf(i)).start();
}
for (int i = 1; i <=10; i++) {
final Integer temp = i;
new Thread(()->{
cache.read(temp+"");
},String.valueOf(i)).start();
}
}
}
class MyCache{
private volatile Map map = new HashMap<>();
public void write(String key,String value){
System.out.println(Thread.currentThread().getName()+"写入"+key);
map.put(key,value);
System.out.println(Thread.currentThread().getName()+"写入完成");
}
public void read(String key){
System.out.println(Thread.currentThread().getName()+"读取"+key);
map.get(key);
System.out.println(Thread.currentThread().getName()+"读取完成");
}
}
class MyCacheLock{
private volatile Map map = new HashMap<>();
private ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
public void write(String key,String value){
//写锁
readWriteLock.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 {
//释放锁
readWriteLock.writeLock().unlock();
}
}
public void read(String key){
//读锁
readWriteLock.readLock().lock();
try {
System.out.println(Thread.currentThread().getName()+"读取"+key);
map.get(key);
System.out.println(Thread.currentThread().getName()+"读取完成");
} catch (Exception e) {
e.printStackTrace();
} finally {
//释放读锁
readWriteLock.readLock().unlock();
}
}
}
阻塞队列
BlockQueue
是 Collection 的一个子类
什么情况下使用阻塞队列
多线程并发、线程池 的情况下使用
BlockQueue 有四组Api
1、抛出异常
2、不抛出异常,有返回值
3、一直阻塞
4、等待 超时阻塞
package com.neihan.queue;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.TimeUnit;
public class TestQueue {
public static void main(String[] args) throws InterruptedException {
test04();
}
public static void test01(){
//队列需要初始化大小
ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
System.out.println(blockingQueue.add("a"));
System.out.println(blockingQueue.add("b"));
System.out.println(blockingQueue.add("c"));
// java.lang.IllegalStateException: Queue full 异常
//System.out.println(blockingQueue.add("c"));
System.out.println(blockingQueue.remove());
System.out.println(blockingQueue.remove());
System.out.println(blockingQueue.remove());
//java.util.NoSuchElementException 异常
//System.out.println(blockingQueue.remove());
}
public static void test02(){
//队列需要初始化大小
ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
System.out.println(blockingQueue.offer("a"));
System.out.println(blockingQueue.offer("b"));
System.out.println(blockingQueue.offer("c"));
System.out.println(blockingQueue.offer("a"));
System.out.println(blockingQueue.poll());
System.out.println(blockingQueue.poll());
System.out.println(blockingQueue.poll());
System.out.println(blockingQueue.poll());
}
public static void test03() throws InterruptedException {
//队列需要初始化大小
ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
//一直阻塞不会返回
blockingQueue.put("a");
blockingQueue.put("b");
blockingQueue.put("c");
//如果队列满了的话此时put不进去值,咋此处一直阻塞,程序不会停止
//blockingQueue.put("d");
System.out.println(blockingQueue.take());
System.out.println(blockingQueue.take());
System.out.println(blockingQueue.take());
//如果队列满了的话此时put不进去值,咋此处一直阻塞,程序不会停止
System.out.println(blockingQueue.take());
}
public static void test04() throws InterruptedException {
//队列需要初始化大小
ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
System.out.println(blockingQueue.offer("a"));
System.out.println(blockingQueue.offer("b"));
System.out.println(blockingQueue.offer("c"));
//等待 两秒 如果队列还是满的话就不再等待
System.out.println(blockingQueue.offer("d",2, TimeUnit.SECONDS));
System.out.println(blockingQueue.poll(2,TimeUnit.SECONDS));
System.out.println(blockingQueue.poll(2,TimeUnit.SECONDS));
System.out.println(blockingQueue.poll(2,TimeUnit.SECONDS));
//取值,两秒没取到就不再等待
System.out.println(blockingQueue.poll(2,TimeUnit.SECONDS));
}
}
同步队列
同步队列 没有 容量 也可以视为 容量为一的队列;
进去一个元素必须等进去的元素出来其他的元素才能进入
put方法 和 take方法;
SynchronousQueue 的take是使用了lock锁保证线程安全的。
package com.neihan.queue;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.TimeUnit;
public class TestSynchronousQueue {
public static void main(String[] args) {
SynchronousQueue synchronousQueue = new SynchronousQueue<>();
new Thread(()->{
try {
System.out.println(Thread.currentThread().getName()+" put 1");
synchronousQueue.put("1");
System.out.println(Thread.currentThread().getName()+" put 2");
synchronousQueue.put("2");
System.out.println(Thread.currentThread().getName()+" put 3");
synchronousQueue.put("3");
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
new Thread(()->{
try {
TimeUnit.SECONDS.sleep(3);
System.out.println(synchronousQueue.take()+"take()");
TimeUnit.SECONDS.sleep(3);
System.out.println(synchronousQueue.take()+"take()");
TimeUnit.SECONDS.sleep(3);
System.out.println(synchronousQueue.take()+"take()");
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
}
线程池
三大方式,七大参数,四种拒绝策略
池化技术
程序的运行,本质:占用系统的资源!我们需要去优化资源的使用 ===> 池化技术
线程池、JDBC的连接池、内存池、对象池 等等。。。。
资源的创建、销毁十分消耗资源
池化技术:事先准备好一些资源,如果有人要用,就来我这里拿,用完之后还给我,以此来提高效率。
线程池的好处1、降低资源的消耗
2、提高响应速度
3、方便管理
实现线程复用、可以控制最大并发数、管理线程
package com.neihan.pool;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Dome01 {
public static void main(String[] args) {
//ExecutorService threadPool = Executors.newSingleThreadExecutor(); //单个线程
//ExecutorService threadPool = Executors.newFixedThreadPool(5); //固定多少个线程 或者说最大多少个线程
ExecutorService threadPool = Executors.newCachedThreadPool(); //遇强则强,遇弱则弱
try {
for (int i = 0; i < 100; i++) {
//使用线程池之后,使用线程池来创建线程
threadPool.execute(()->{
System.out.println(Thread.currentThread().getName()+ " ok");
});
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//使用完线程池一定要关闭
threadPool.shutdown();
}
}
}
线程池的三大方法
- ExecutorService threadPool = Executors.newSingleThreadExecutor(); 单个线程
- ExecutorService threadPool = Executors.newFixedThreadPool(5); //固定多少个线程 或者说最大多少个线程
- ExecutorService threadPool = Executors.newCachedThreadPool(); //遇强则强,遇弱则弱
1、核心线程
2、最大线程
3、阻塞队列
4、线程工厂
5、超时数
6、超时单位
7、拒绝策略
拒绝策略
1. new ThreadPoolExecutor.AbortPolicy(): //该拒绝策略为:银行满了,还有人进来,不处理这个人的,并抛出异常
超出最大承载,就会抛出异常:队列容量大小+maxPoolSize
2. new ThreadPoolExecutor.CallerRunsPolicy(): //该拒绝策略为:哪来的去哪里 main线程进行处理
3. new ThreadPoolExecutor.DiscardPolicy(): //该拒绝策略为:队列满了,丢掉异常,不会抛出异常。
4. new ThreadPoolExecutor.DiscardOldestPolicy(): //该拒绝策略为:队列满了,尝试去和最早的进程竞争,不会抛出异常
如何设置线程池的大小1、CPU密集型:电脑的核数是几核就选择几;选择maximunPoolSize的大小
I/O密集型:
在程序中有15个大型任务,io十分占用资源;I/O密集型就是判断我们程序中十分耗I/O的线程数量,大约是最大I/O数的一倍到两倍之间。
四大函数式接口新时代的程序员:lambda表达式、链式编程、函数式接口、Stream流式计算
Function 函数型接口函数式接口 只有一个方法的接口 并带有 @FunctionalInterface
package com.neihan.function;
import java.util.function.Function;
public class Demo01 {
public static void main(String[] args) {
// lambda 表达式
Function function = (str)->{
return str;
};
System.out.println(function.apply("neihan niu bi lambda"));
}
}
断定型接口Predicate
断定型接口 只能传入一个参数,返回值只能是Boolean
package com.neihan.function;
import java.util.function.Predicate;
public class Dome02 {
public static void main(String[] args) {
Predicate predicate = (str)->{ return str.isEmpty();};
System.out.println(predicate.test(""));
}
}
Supplier供给型接口
没有参数 只有返回值
package com.neihan.function;
import java.util.function.Supplier;
public class Demo03 {
public static void main(String[] args) {
Supplier supplier = ()->{return "Neihan Niu bi ";};
System.out.println(supplier.get());
}
}
消费性接口Consumer
消费性接口 只有参数没有返回值
package com.neihan.function;
import java.util.function.Consumer;
public class Demo04 {
public static void main(String[] args) {
Consumer stringConsumer = new Consumer(){
@Override
public void accept(String o) {
System.out.println(o);
}
};
stringConsumer.accept("neihan ");
}
}
stream流试计算
package com.neihan.function;
import java.util.Arrays;
import java.util.List;
public class testStream {
public static void main(String[] args) {
Student student1 = new Student(16,"a",1);
Student student2 = new Student(26,"b",2);
Student student3 = new Student(25,"c",6);
Student student4 = new Student(23,"d",4);
List students = Arrays.asList(student1, student2, student3, student4);
//链式编程 stream流式计算
students.stream()
.filter(student ->{return student.getId() % 2 ==0;})
.filter(student -> {return student.getAge() > 23;} )
.map(student -> {return student.getName().toUpperCase();}) //转换大写
.sorted((s1,s2)->{return s2.compareTo(s1);}) //排序 倒叙输出
.limit(1) // 分页,显示的页数
.forEach(System.out::printf);
}
}
package com.neihan.function;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class StreamHome01 {
public static void main(String[] args) {
List integers = Arrays.asList(2,3,4,6,60,7);
String collect = integers
.stream()
.map(Object::toString)
.sorted((s1, s2) -> {
return s2.compareTo(s1);
})
.filter((s) -> {
return Integer.parseInt(s) % 2 == 0;
})
.collect(Collectors.joining(""));
String sz [] = new String[collect.length()];
for (int i = 0; i < collect.length(); i++) {
sz[i] = collect.substring(i,i+1);
}
Arrays.stream(sz)
.sorted((i1,i2)->{return i2.compareTo(i1);})
.forEach(System.out::printf);
}
}
ForkJoin
ForkJoin 在JDK1.7,并行执行任务!提高效率~。在大数据量速率会更快!
大数据中:MapReduce 核心思想->把大任务拆分为小任务!
如何使用ForkJoin?- 1、通过ForkJoinPool来执行 - 2、计算任务 execute(ForkJoinTask> task) - 3、计算类要去继承ForkJoinTask; ForkJoin 的计算类
package com.neihan.forkjoin; import lombok.Data; import lombok.NoArgsConstructor; import java.util.concurrent.ForkJoinTask; import java.util.concurrent.RecursiveTask; @Data @NoArgsConstructor public class ForkJoinDemo extends RecursiveTask{ private Long start; private Long end; private Long temp =10000L; //临界值 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 forkJoin1 = new ForkJoinDemo(start,middle); //拆分任务 把任务压入队列 forkJoin1.fork(); ForkJoinDemo forkJoin2 = new ForkJoinDemo(middle+1,end); forkJoin2.fork(); return forkJoin1.join() + forkJoin2.join(); } } }
测试类
package com.neihan.forkjoin;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.stream.LongStream;
public class TestForkjoin {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//test01(); 2928
test02(); //4043
//test03(); //198
}
private static void test01(){
long start = System.currentTimeMillis();
long sum = 0L;
for (Long i = 1L; i <= 10_0000_0000L; i++) {
sum+= i;
}
Long end = System.currentTimeMillis();
System.out.println("sum= "+sum+"时间:"+(end-start));
}
public static void test02() throws ExecutionException, InterruptedException {
Long start = System.currentTimeMillis();
ForkJoinPool forkJoinPool = new ForkJoinPool();
ForkJoinTask task = new ForkJoinDemo(0L, 10_0000_0000L);
ForkJoinTask submit = forkJoinPool.submit(task);//提交任务
Long sum = submit.get();
Long end = System.currentTimeMillis();
System.out.println("sum="+sum+"时间:"+(end-start)/2);
}
public static void test03(){
Long start = System.currentTimeMillis();
Long sum = LongStream.range(0L, 10_0000_0000L).parallel().reduce(0, Long::sum);
Long end = System.currentTimeMillis();
System.out.println("sum="+sum+"时间:"+(end-start));
}
}
异步回调
Future 设计的初衷:对将来的某个事件结果进行建模!
package com.neihan.future;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
public class TestFuture {
public static void main(String[] args) throws ExecutionException, InterruptedException {
test02();
}
public void test01() throws ExecutionException, InterruptedException {
CompletableFuture future = CompletableFuture.runAsync(() -> {
//发起一个异步任务
try {
TimeUnit.SECONDS.sleep(2);
System.out.println("这是一个异步的消息");
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName());
});
System.out.println("Main线程");
System.out.println("future.get() = " + future.get());
}
public static void test02() throws ExecutionException, InterruptedException {
CompletableFuture future = CompletableFuture.supplyAsync(() -> {
//发起一个异步任务
try {
TimeUnit.SECONDS.sleep(2);
System.out.println("这是一个异步的消息");
} catch (InterruptedException e) {
e.printStackTrace();
}
int i = 10 /0;
System.out.println(Thread.currentThread().getName());
return 1024; // 成功的返回值
});
System.out.println(future.whenComplete((t, u) -> {
System.out.println(t); // t 是正常返回的结果
System.out.println(u); // u 是错误的信息
}).exceptionally((e -> {
System.out.println("e.getMessage() = " + e.getMessage());
return 500; //返回error 的信息
})).get());
}
}
但是我们平时都使用CompletableFuture
JMM 什么是JMM?JMM:JAVA内存模型,不存在的东西,是一个概念,也是一个约定!
关于JMM的一些同步的约定:
1、线程解锁前,必须把共享变量立刻刷回主存;
2、线程加锁前,必须读取主存中的最新值到工作内存中;
3、加锁和解锁是同一把锁;
线程中分为 工作内存、主内存
8种操作:
-
Read(读取):作用于主内存变量,它把一个变量的值从主内存传输到线程的工作内存中,以便随后的load动作使用; - load(载入):作用于工作内存的变量,它把read操作从主存中变量放入工作内存中;
-
Use(使用):作用于工作内存中的变量,它把工作内存中的变量传输给执行引擎,每当虚拟机遇到一个需要使用到变量的值,就会使用到这个指令;
-
assign(赋值):作用于工作内存中的变量,它把一个从执行引擎中接受到的值放入工作内存的变量副本中; -
-
store(存储):作用于主内存中的变量,它把一个从工作内存中一个变量的值传送到主内存中,以便后续的write使用; - write(写入):作用于主内存中的变量,它把store操作从工作内存中得到的变量的值放入主内存的变量中; -
-
lock(锁定):作用于主内存的变量,把一个变量标识为线程独占状态;
-
unlock(解锁):作用于主内存的变量,它把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定;
- JMM对这8种操作给了相应的规定*:
- - 不允许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 提供的 轻量级 的同步机制
1、保证可见性
2、不保证原子性
3、禁止指令重排机制
保证可见性
volatile 没加这个关键字的话主存发生了改变但是Thread()线程没收到通知,还是读取到的是没修改过的数据 一直死循环
package com.neihan.jmm;
import java.util.concurrent.TimeUnit;
public class JMMDemo01 {
private static volatile int sum =1; //volatile 没加这个关键字的话主存发生了改变但是Thread()线程没收到通知,还是读取到的是没修改过的数据
public static void main(String[] args) throws InterruptedException {
new Thread(()->{
while (sum==1){
}
}).start();
TimeUnit.SECONDS.sleep(1);
sum=0;
System.out.println("main");
}
}
不保证原子性
如果不加lock和synchronized ,怎么样保证原子性?
使用原子类
这些类的底层都直接和操作系统挂钩!是在内存中修改值。
package com.neihan.jmm;
import java.util.concurrent.atomic.AtomicInteger;
public class VDemo {
private static volatile AtomicInteger count = new AtomicInteger();
public static void main(String[] args) {
for (int i = 0; i < 20; i++) {
new Thread(()->{
for (int j = 0; j < 1000; j++) {
add();
}
}).start();
}
while (Thread.activeCount() > 2 ){
Thread.yield();
}
System.out.println(Thread.currentThread().getName()+""+"count="+count);
}
public static void add(){
count.incrementAndGet();
}
}
禁止指令重排
什么是指令重排?
我们写的程序,计算机并不是按照我们自己写的那样去执行的
源代码–>编译器优化重排–>指令并行也可能会重排–>内存系统也会重排–>执行
int x=1; //1 int y=2; //2 x=x+5; //3 y=x*x; //4 //我们期望的执行顺序是 1_2_3_4 可能执行的顺序会变成2134 1324 //可不可能是 4123? 不可能的 1234567
可能造成的影响结果:前提: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
可能在线程A中会出现,先执行b=1,然后再执行x=a;
在B线程中可能会出现,先执行a=2,然后执行y=b;
那么就有可能结果如下:x=2; y=1.
volatile可以避免指令重排:
volatile中会加一道内存的屏障,这个内存屏障可以保证在这个屏障中的指令顺序。
内存屏障:CPU指令。作用:
1、保证特定的操作的执行顺序;
2、可以保证某些变量的内存可见性(利用这些特性,就可以保证volatile实现的可见性)
总结- volatile可以保证可见性;- 不能保证原子性- 由于内存屏障,可以保证避免指令重排的现象产生
面试官:那么你知道在哪里用这个内存屏障用得最多呢?单例模式
饿汉式、DCL懒汉式
饿汉模式单例模式私有构造
饿汉顾名思义 很饿 什么东西都先创建好
package com.neihan.jmm;
public class Hungry {
//可能会浪费空间
private byte[] data1 = new byte[1024*1024];
private byte[] data2 = new byte[1024*1024];
private byte[] data3 = new byte[1024*1024];
private Hungry(){
}
private final static Hungry hungry = new Hungry();
private static Hungry getInstance(){
return hungry;
}
}
DCL懒汉式
package com.neihan.jmm;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
public class LazyMan {
private static boolean neihan = false; //红绿灯
private LazyMan(){
synchronized (LazyMan.class){
if(neihan == false){
neihan=true;
}else{
throw new RuntimeException("小伙子不要试图使用反射来搞破坏!!!");
}
}
System.out.println(Thread.currentThread().getName()+"ok");
}
private volatile static LazyMan lazyMan;
private static LazyMan getInstance(){
if (lazyMan ==null){
synchronized (LazyMan.class){
if(lazyMan==null){ //单线程下是OK 的 但是多线程是不行的 需要加锁 加锁后java中有反射!!!!
lazyMan = new LazyMan();
}
}
}
return lazyMan;
}
public static void main(String[] args) throws Exception {
//反射
//LazyMan instance1 = LazyMan.getInstance();
Field neihan = LazyMan.class.getDeclaredField("neihan");
Constructor declaredConstructor = LazyMan.class.getDeclaredConstructor(null);
declaredConstructor.setAccessible(true); //这个时候无视了私有的构造器,使用反射创建对象
LazyMan instance2 = declaredConstructor.newInstance();
neihan.setAccessible(true);
neihan.set(instance2,false); //把字段的值改变又可以了
LazyMan instance1 = declaredConstructor.newInstance();
System.out.println(instance1);
System.out.println(instance2);
}
}
静态内部类
package com.neihan.jmm;
public class Holder {
private Holder(){
}
public static Holder getInstance(){
return InnerClass.holder;
}
public static class InnerClass{
private static final Holder holder = new Holder();
}
}
单例不安全, 因为反射
枚举
package com.neihan.jmm;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public enum EnumSingle {
INSTANCE;
public EnumSingle getInstance(){
return INSTANCE;
}
}
class Test{
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
EnumSingle instance1 = EnumSingle.INSTANCE;
Constructor declaredConstructor = EnumSingle.class.getDeclaredConstructor(String.class,int.class);
declaredConstructor.setAccessible(true);
EnumSingle enumSingle = declaredConstructor.newInstance();
System.out.println(instance1);
System.out.println(enumSingle);
}
}
使用枚举,我们就可以防止反射破坏了。
枚举类型的最终反编译源码:
public final class EnumSingle extends Enum
{
public static EnumSingle[] values()
{
return (EnumSingle[])$VALUES.clone();
}
public static EnumSingle valueOf(String name)
{
return (EnumSingle)Enum.valueOf(com/ogj/single/EnumSingle, name);
}
private EnumSingle(String s, int i)
{
super(s, i);
}
public EnumSingle getInstance()
{
return INSTANCE;
}
public static final EnumSingle INSTANCE;
private static final EnumSingle $VALUES[];
static
{
INSTANCE = new EnumSingle("INSTANCE", 0);
$VALUES = (new EnumSingle[] {
INSTANCE
});
}
}
深入理解CAS
什么是CAS?
大厂必须深入研究底层!!!!修内功!操作系统、计算机网络原理、组成原理、数据结构
意思就是比较并交换
package com.neihan.cas;
import java.util.concurrent.atomic.AtomicInteger;
public class CASDemo {
public static void main(String[] args) {
AtomicInteger atomicInteger = new AtomicInteger(1024);
//public final boolean compareAndSet(int expect, int update)
System.out.println(atomicInteger.compareAndSet(1024, 241));
System.out.println(atomicInteger.get());
}
}
Unsafe 类
总结CAS:比较当前工作内存中的值 和 主内存中的值,如果这个值是期望的,那么则执行操作!如果不是就一直循环,使用的是自旋锁。
缺点:
- 循环会耗时;- 一次性只能保证一个共享变量的原子性;- 它会存在ABA问题
CAS : ABA 问题 (狸猫换太子)
线程1:期望值是1,要变成2;
线程2:两个操作:
- 1、期望值是1,变成3- 2、期望是3,变成1
所以对于线程1来说,A的值还是1,所以就出现了问题,骗过了线程1;
public class casDemo {
//CAS : compareAndSet 比较并交换
public static void main(String[] args) {
AtomicInteger atomicInteger = new AtomicInteger(2020);
System.out.println(atomicInteger.compareAndSet(2020, 2021));
System.out.println(atomicInteger.get());
//boolean compareAndSet(int expect, int update)
//期望值、更新值
//如果实际值 和 我的期望值相同,那么就更新
//如果实际值 和 我的期望值不同,那么就不更新
System.out.println(atomicInteger.compareAndSet(2021, 2020));
System.out.println(atomicInteger.get());
//因为期望值是2020 实际值却变成了2021 所以会修改失败
//CAS 是CPU的并发原语
//atomicInteger.getAndIncrement(); //++操作
System.out.println(atomicInteger.compareAndSet(2020, 2021));
System.out.println(atomicInteger.get());
}
}
原子引用
解决ABA问题,对应的思想:就是使用了乐观锁~
带版本号的 原子操作!
使用包装类会有对象引用的问题
Integer 使用了对象缓存机制,默认范围是-128~127,推荐使用静态工厂方法valueOf获取对象实例,而不是new,因为valueOf使用缓存,而new一定会创建新的对象分配新的内存空间。
带版本号的原子操作
package com.neihan.cas;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.atomic.AtomicStampedReference;
public class ABADemo {
public static void main(String[] args) {
//使用包装类会有对象引用的问题
//Integer 使用了对象缓存机制,默认范围是-128~127,推荐使用静态工厂方法valueOf获取对象实例,而不是new,因为valueOf使用缓存,而new一定会创建新的对象分配新的内存空间。
AtomicStampedReference atomicStampedReference = new AtomicStampedReference(1,1);
// CAS 比较交换
new Thread(()->{
int stamp = atomicStampedReference.getStamp(); //获得版本号
System.out.println(Thread.currentThread().getName()+"Stamp=>"+stamp);
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(atomicStampedReference.compareAndSet(
1,
2,
atomicStampedReference.getStamp(),
atomicStampedReference.getStamp() + 1));
System.out.println(Thread.currentThread().getName()+atomicStampedReference.getStamp());
//修改完之后再修改回来,另一条线程并不知情,看看能不能修改成功
System.out.println(atomicStampedReference.compareAndSet(
2,
1,
atomicStampedReference.getStamp(),
atomicStampedReference.getStamp() + 1));
System.out.println(Thread.currentThread().getName()+atomicStampedReference.getStamp());
},"A").start();
new Thread(()->{
int stamp = atomicStampedReference.getStamp();
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"Stamp"+stamp);
System.out.println(Thread.currentThread().getName()+" "+atomicStampedReference.compareAndSet(1, 6, stamp, atomicStampedReference.getStamp() + 1));
System.out.println(Thread.currentThread().getName()+"Stamp"+atomicStampedReference.getStamp());
},"B").start();
}
}
各种锁的理解
公平锁,非公平锁
1、公平锁:非常公平,不能插队,必须先来后到
public ReentrantLock() {
sync = new NonfairSync();
}
2、非公平锁:非常不公平,允许插队,可以改变顺序
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
可重入锁
1、Synchonized 锁
synchronized 版本的可重入锁 自动获得内部锁
package com.neihan.lock;
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版本
Lock 版本的重入锁 Lock 锁一定是配对出现,不然就会出现死锁
package com.neihan.lock;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Demo02 {
public static void main(String[] args) {
Phone2 phone2 = new Phone2();
new Thread(()->{
phone2.sms();
},"A").start();
new Thread(()->{
phone2.sms();
},"B").start();
}
}
class Phone2{
Lock lock = new ReentrantLock();
public void sms(){
try {
lock.lock();
System.out.println(Thread.currentThread().getName()+"==>sms");
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
call(); //这里也是一把锁
}
public void call(){
try {
lock.lock();
System.out.println(Thread.currentThread().getName()+"==>call");
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
自旋锁
1、spinlock
public final int getAndAddInt(Object var1, long var2, int var4) {
int var5;
do {
var5 = this.getIntVolatile(var1, var2);
} while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
return var5;
}
2、自定义自旋锁
基于CAS 实现
package com.neihan.lock;
import java.util.concurrent.atomic.AtomicReference;
public class SpinlockDemo {
AtomicReference 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);
}
}
测试类
package com.neihan.lock;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
public class TestSpinLock {
public static void main(String[] args) {
SpinlockDemo spinlockDemo = new SpinlockDemo();
new Thread(()->{
try {
spinlockDemo.myLock();
TimeUnit.SECONDS.sleep(10);
} catch (Exception e) {
e.printStackTrace();
} finally {
spinlockDemo.myUnlock();
}
},"A").start();
new Thread(()->{
try {
spinlockDemo.myLock();
TimeUnit.SECONDS.sleep(2);
} catch (Exception e) {
e.printStackTrace();
} finally {
spinlockDemo.myUnlock();
}
},"B").start();
}
}
死锁
package com.neihan.lock;
import java.util.concurrent.TimeUnit;
public class Deadlock {
public static void main(String[] args) {
String lockA = "lockA";
String lockB = "lockB";
new Thread(new MyThread(lockA,lockB),"T1").start();
new Thread(new MyThread(lockB,lockA),"T2").start();
}
}
class MyThread implements Runnable{
private String lockA;
private String lockB;
public MyThread(String lockA, String lockB) {
this.lockA = lockA;
this.lockB = lockB;
}
@Override
public void run() {
synchronized (lockA){
System.out.println(Thread.currentThread().getName()+"lock:"+lockA+"=>get "+lockB);
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lockB){
System.out.println(Thread.currentThread().getName()+" lock"+lockB+"===> get"+lockA);
}
}
}
}
如何解开死锁
命令:jps -l
1、使用jps定位进程号,jdk的bin目录下: 有一个jps
2、使用jstack 进程进程号 找到死锁信息
一般情况信息在最后:
while (!atomicReference.compareAndSet(null,thread)){
}
}
public void myUnlock(){
Thread thread = Thread.currentThread();
System.out.println(thread.getName()+"===>myUnlock");
atomicReference.compareAndSet(thread,null);
}
}
**测试类**
```java
package com.neihan.lock;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
public class TestSpinLock {
public static void main(String[] args) {
SpinlockDemo spinlockDemo = new SpinlockDemo();
new Thread(()->{
try {
spinlockDemo.myLock();
TimeUnit.SECONDS.sleep(10);
} catch (Exception e) {
e.printStackTrace();
} finally {
spinlockDemo.myUnlock();
}
},"A").start();
new Thread(()->{
try {
spinlockDemo.myLock();
TimeUnit.SECONDS.sleep(2);
} catch (Exception e) {
e.printStackTrace();
} finally {
spinlockDemo.myUnlock();
}
},"B").start();
}
}
死锁
package com.neihan.lock;
import java.util.concurrent.TimeUnit;
public class Deadlock {
public static void main(String[] args) {
String lockA = "lockA";
String lockB = "lockB";
new Thread(new MyThread(lockA,lockB),"T1").start();
new Thread(new MyThread(lockB,lockA),"T2").start();
}
}
class MyThread implements Runnable{
private String lockA;
private String lockB;
public MyThread(String lockA, String lockB) {
this.lockA = lockA;
this.lockB = lockB;
}
@Override
public void run() {
synchronized (lockA){
System.out.println(Thread.currentThread().getName()+"lock:"+lockA+"=>get "+lockB);
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lockB){
System.out.println(Thread.currentThread().getName()+" lock"+lockB+"===> get"+lockA);
}
}
}
}
如何解开死锁
命令:jps -l
1、使用jps定位进程号,jdk的bin目录下: 有一个jps
2、使用jstack 进程进程号 找到死锁信息
一般情况信息在最后:



