准备工作:导入maven依赖包
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<version>1.18.8version>
dependency>
第二步,将project里的modules的language level改为jdk8
第三步,将javaCompile改为jdk8
java.util.concurrent 并发
java.util.concurrent.atomic 原子性
java.util.concurrent.locks
业务:普通的线程代码 Thread
Runnable:没有返回值,效率相比于Callable相对较低
一个进程可以有多个线程,且至少包含一个。
java默认线程:main ,gc线程,三种开启线程方式:Thread,Runnable,Callable
java是不能真正的开启多线程,源码中是调用了本地native方法,底层C++,java是作用在虚拟机上的,无法直接操作硬件
并发:多线程操作同一个资源,cpu单核模拟多个线程
并行:多个人一起行走,cpu多核,多个线程可以同时执行,线程池
//获取cpu的核数
//cpu密集型,IO密集型
Runtime.getRuntime().availableProcessors();
并发的本质:充分利用cpu的资源
线程状态:
public enum State {
//线程新生
NEW,
//运行
RUNNABLE,
//阻塞
BLOCKED,
//等待,死等
WAITING,
//超时等待
TIMED_WAITING,
//终止
TERMINATED;
}
wait/sleep区别:
1.来自不同的类,wait来自obj类,sleep来自Thread类
2.wait会释放锁,sleep不会释放锁
3.使用范围不同:wait必须在同步代码块中,sleep无限制
Synchronized同步锁,关键字,本质:排队,锁
并发:多线程操作同一个资源类,把资源类丢进线程里,Runnable函数式接口可以new被成为匿名内部类,但繁琐,所以用lambda表达式来代替()->{}
new Thread(()->{},"name").start;
Interface Lock:
实现类:ReentrantLock可重入锁,ReentrantReadWriteLock.ReadLock读锁,eentrantReadWriteLock.WriteLock写锁
公平锁:十分公平,先来后到
非公平锁:十分不公平,可以插队(默认)
Lock:手动加锁,手动释放锁
1.Synchronized是关键字,Lock是java类
2.Synchronized无法判断获取锁的状态,Lock可以判断是否获取锁
3.Synchronized会自动释放锁,Lock手动释放锁
4.Synchronized线程1获得锁,阻塞;线程2等待,Lock锁会trylock()抓取锁
5.Synchronized可重入锁,非公平锁,不可中断的,Lock锁可重入锁,可以中断,公平锁
6.Synchronized锁少量的代码块,Lock锁大量的代码块
步骤:判断等待,业务,通知
面试:单例模式,排序算法,生产者消费者,死锁
线程也可以唤醒,而不会通知,中断或者超时即所谓的虚假唤醒,等待应该总是出现在while循环中
Lock替代synchronized方法和语句的使用,Condition取代了对象
Condition方法:
condition.await();
condition.signalAll();
package com.liu.demo1;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
//生产者消费者问题
public class test {
public static void main(String[] args) {
Data data = new Data();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"A").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
data.decrement();
} 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{
private int number = 0;
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
/*
condition.await();
condition.signalAll();
*/
public void increment() throws InterruptedException {
lock.lock();
try {
//业务代码
while (number!=0){
//等待
condition.await();
}
number++;
System.out.println(Thread.currentThread().getName()+"=>"+number);
//通知其他线程+1完毕
condition.signalAll();
} catch (Exception e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
public void decrement() throws InterruptedException {
lock.lock();
try {
while (number==0){
//等待
condition.await();
}
number--;
System.out.println(Thread.currentThread().getName()+"=>"+number);
//通知其他线程-1完毕
condition.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
package com.liu.demo1;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
//生产者消费者问题
public class test {
public static void main(String[] args) {
Data data = new Data();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
data.printA();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"A").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
data.printB();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"B").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
data.printC();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"C").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
data.printD();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"D").start();
}
}
//等待--业务--通知
class Data{
private int number = 1;
Lock lock = new ReentrantLock();
Condition condition1 = lock.newCondition();
Condition condition2 = lock.newCondition();
Condition condition3 = lock.newCondition();
Condition condition4 = lock.newCondition();
/*
condition.await();
condition.signalAll();
*/
public void printA() throws InterruptedException {
lock.lock();
try {
//业务代码
while (number!=1){
//等待
condition1.await();
}
System.out.println(Thread.currentThread().getName()+"=>"+number);
number=2;
condition2.signal();
} catch (Exception e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
public void printB() throws InterruptedException {
lock.lock();
try {
while (number!=2){
//等待
condition2.await();
}
System.out.println(Thread.currentThread().getName()+"=>"+number);
number=3;
condition3.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void printC() throws InterruptedException {
lock.lock();
try {
while (number!=3){
//等待
condition3.await();
}
System.out.println(Thread.currentThread().getName()+"=>"+number);
number=4;
condition4.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void printD() throws InterruptedException {
lock.lock();
try {
while (number!=4){
//等待
condition4.await();
}
System.out.println(Thread.currentThread().getName()+"=>"+number);
number = 1;
condition1.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
1.执行问题
package com.liu.lock8;
import java.util.concurrent.TimeUnit;
/*
8锁就是8个问题
1.两个线程,总是A先执行,再B执行,是由于锁的原因,synchronized方法锁的对象是方法的调用者,谁先拿到锁谁执行!两个方法用的同一把锁!
2.两个线程,不管方法1睡眠多久,都是方法一先执行,还是方法一先拿到锁!
*/
public class test {
public static void main(String[] args) throws InterruptedException {
phone phone = new phone();
new Thread(()->{
phone.sendMessage();
},"A").start();
//线程睡眠用JUC的TimeUnit枚举类
TimeUnit.SECONDS.sleep(2);
new Thread(()->{
phone.call();
},"B").start();
}
}
class phone{
public synchronized void sendMessage(){
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发信息");
}
public synchronized void call(){
System.out.println("打电话");
}
}
package com.liu.lock8;
import java.util.concurrent.TimeUnit;
/*
3.增加一个普通方法后,如果加了锁的方法和普通方法睡眠时间相同,都是锁的方法先执行,如果锁的方法睡眠时间长,就是普通方法先执行!
4.有两个对象,都执行同步方法,睡眠时间短的就先执行!
*/
public class test2 {
public static void main(String[] args) throws InterruptedException {
phone phone1 = new phone();
phone2 phone2 = new phone2();
new Thread(()->{
phone1.sendMessage();
},"A").start();
//线程睡眠用JUC的TimeUnit枚举类
TimeUnit.SECONDS.sleep(1);
new Thread(()->{
phone2.call();
},"B").start();
}
}
class phone2{
public synchronized void sendMessage(){
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发信息");
}
public synchronized void call(){
System.out.println("打电话");
}
//不是同步方法,不受锁的影响
public void hello(){
System.out.println("hello");
}
}
package com.liu.lock8;
import java.util.concurrent.TimeUnit;
/*
5.静态同步方法,只有一个对象,方法一先执行,phone3.class全局唯一对象,static在类加载的时候就执行了,Class模板,锁的是class
6.两个对象,还是静态模块的先后顺序执行,锁的是class!
*/
public class test3 {
public static void main(String[] args) throws InterruptedException {
phone3 phone2 = new phone3();
phone3 phone3 = new phone3();
new Thread(()->{
phone2.sendMessage();
},"A").start();
//线程睡眠用JUC的TimeUnit枚举类
TimeUnit.SECONDS.sleep(1);
new Thread(()->{
phone3.call();
},"B").start();
}
}
class phone3{
public static synchronized void sendMessage(){
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发信息");
}
public static synchronized void call(){
System.out.println("打电话");
}
}
package com.liu.lock8;
import java.util.concurrent.TimeUnit;
/**
* 7.一个静态同步方法,一个普通同步方法,两者锁的对象不同,一个锁的是class模板,一个锁的是方法调用者,谁睡眠时间短谁先执行!
* 8.两个对象,两个同步方法,还是方法一先执行,锁的对象不同,睡眠时间短的先执行!
*/
public class test4 {
public static void main(String[] args) throws InterruptedException {
phone4 phone4 = new phone4();
phone4 phone5 = new phone4();
new Thread(()->{
phone4.sendMessage();
},"A").start();
//线程睡眠用JUC的TimeUnit枚举类
TimeUnit.SECONDS.sleep(3);
new Thread(()->{
phone5.call();
},"B").start();
}
}
class phone4{
//锁的class模板
public static synchronized void sendMessage(){
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发信息");
}
//锁的是调用者
public synchronized void call(){
System.out.println("打电话");
}
}
并发下的ArrayList不安全,会报错:java.util.ConcurrentModificationException,解决方法:
package com.liu.unsafe;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
import java.util.Vector;
import java.util.concurrent.CopyOnWriteArrayList;
public class test {
public static void main(String[] args) {
/**并发下的ArrayList不安全,会报错:java.util.ConcurrentModificationException,解决方法:
* 1.Vector
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);
},String.valueOf(i)).start();
}
}
}
package com.liu.unsafe;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CopyOnWriteArraySet;
public class SetTest {
//报错:ConcurrentModificationException
/*解决方案:
1.将线程变成安全的,用Collections工具类
2.JUC方法:Set set = new CopyOnWriteArraySet<>();
*/
public static void main(String[] args) {
// Set set = Collections.synchronizedSet(new HashSet<>());
Set<String> set = new CopyOnWriteArraySet<>();
for (int i = 1; i <= 30; i++) {
new Thread(()->{
set.add(UUID.randomUUID().toString().substring(0,5));
System.out.println(set);
},String.valueOf(i)).start();
}
}
}
Set的底层源码:就是hashmap
public HashSet() {
map = new HashMap<>();
}
//add set 本质是map key是无法重复的
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
HashMap:
package com.liu.unsafe;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
public class MapTest {
public static void main(String[] args) {
// Map map = new HashMap<>();
/*
工作中不用HashMap
默认等价于 new HashMap<>(16,0.75);
加载因子 初始化容量 16*0.75=12,超过12就扩容
*/
/**解决线程不安全的方案
*Map map = new ConcurrentHashMap<>();
*/
/*
hashmap和ConcurrentHashMap的区别:
1.HashMap是线程不安全的,ConcurrentHashMap是线程安全的
2.ConcurrentHashMap将整个hash桶进行了分段segment,每个分段上都有锁存在,当一个线程访问其中一个数据片段时,其他的数据也能够被其他线程访问。
3.ConcurrentHashMap让锁的力度更精细,并发性能更好
4.ConcurrentHashMap不能存储null键值对,HashMap可以存储null键值对
*/
Map<String,String> map = new ConcurrentHashMap<>();
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();
}
}
}
hashmap和ConcurrentHashMap的区别:
1.HashMap是线程不安全的,ConcurrentHashMap是线程安全的
2.ConcurrentHashMap将整个hash桶进行了分段segment,每个分段上都有锁存在,当一个线程
访问其中一个数据片段时,其他的数据也能够被其他线程访问。
3.ConcurrentHashMap让锁的力度更精细,并发性能更好
4.ConcurrentHashMap不能存储null键值对,HashMap可以存储null键值对
函数式接口,类似于Runnable,Runnable不返回结果,也不能抛出被检查的异常,而Callable需要返回结果,也要抛出异常
package com.liu.callable;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class CallableTest {
public static void main(String[] args) throws ExecutionException, InterruptedException {
// new Thread(new Runnable()).start();
//new Thread(new FutureTask()).start();和上面一个一样,只是一个适配类
//new Thread(new FutureTask(Callable)).start(); 启动callable
MyThread thread= new MyThread();
FutureTask futureTask = new FutureTask(thread);//适配类
new Thread(futureTask,"A").start();
new Thread(futureTask,"B").start();//结果会被缓存,效率高
Integer o = (Integer) futureTask.get();//获取callable的返回结果,需要等待,可能会产生阻塞
System.out.println(o);
}
}
class MyThread implements Callable<Integer>{
@Override
public Integer call() throws Exception {
System.out.println("call()");
return 1024;
}
}
减法计数器
countDownLatch.countDown();//数量-1
countDownLatch.await();//等到计数器归零,再向下执行
package com.liu.add;
import java.util.concurrent.CountDownLatch;
//计数器
public class CountDownLatchTest {
public static void main(String[] args) throws InterruptedException {
CountDownLatch countDownLatch = new CountDownLatch(6);
for (int i = 1; i <= 6; i++) {
new Thread(()->{
System.out.println(Thread.currentThread().getName()+" go out");
countDownLatch.countDown();//数量-1
},String.valueOf(i)).start();
}
countDownLatch.await();//等到计数器归零,再向下执行
System.out.println("close!");
}
}
cyclicBarrier.await();//等待计数器变成7
加法计数器
package com.liu.add;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
public class CyclicBarrierTest {
public static void main(String[] args) {
CyclicBarrier cyclicBarrier = new CyclicBarrier(7,()->{
System.out.println("成功");
});
for (int i = 1; i <= 7; i++) {
final int temp = i;
//lambda表达式拿不到i,所以定义一个中间变量
new Thread(()->{
System.out.println(Thread.currentThread().getName()+" 收集 "+ temp +" 个");
try {
cyclicBarrier.await();//等待计数器变成7
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}).start();
}
}
}
信号量
semaphore.acquire();获得,如果已经满了,等待,等待被释放完为止
semaphore.release();//释放,会将当前的信号量释放+1,然后唤醒等待的线程
作用:多个共享资源互斥的使用,并发限流,控制最大的线程数。
package com.liu.add;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
public class SemaphoreTest {
public static void main(String[] args) {
//限流
Semaphore semaphore = new Semaphore(3);
for (int i = 1; i <= 6; i++) {
new Thread(()->{
//acquire() 得到
try {
semaphore.acquire();
System.out.println(Thread.currentThread().getName()+" 抢到");
TimeUnit.SECONDS.sleep(2);
System.out.println(Thread.currentThread().getName()+" 离开");
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
semaphore.release();//释放
}
},String.valueOf(i)).start();
}
}
}
package com.liu.rw;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**独占锁(写锁):一次只能被一个线程占用
* 共享锁(读锁):多个线程可以同时占有
* 读写锁
* ReadWriteLock
* 读-读 可以共存
* 读-写 不能共存
* 写-写 不能共存
*/
public class ReadWriteLockDemo {
public static void main(String[] args) {
MyCache2 myCache = new MyCache2();
for (int i = 1; i <= 5; i++) {
final int temp = i;
new Thread(()->{
myCache.put(temp+"",temp+"");
},String.valueOf(i)).start();
}
for (int i = 1; i <= 5; 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);
Object o = map.get(key);
System.out.println(Thread.currentThread().getName()+"读取成功");
}
}
//加锁
class MyCache2{
private volatile Map<String,Object> map = new HashMap<>();
//读写锁,更加细腻度的控制
private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
//存,写入的时候,只希望同时只有一个线程写
public void put(String key,Object value){
readWriteLock.writeLock().lock();//写锁加锁,与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 get(String key){
readWriteLock.readLock().lock();//读锁
try {
System.out.println(Thread.currentThread().getName()+"读取"+key);
Object o = map.get(key);
System.out.println(Thread.currentThread().getName()+"读取成功"+o);
} catch (Exception e) {
e.printStackTrace();
} finally {
readWriteLock.readLock().unlock();
}
}
}
FIFO:
阻塞队列:在多线程并发处理,线程池使用
BlockQueue:
学会使用队列:添加,移除
方式 | 抛出异常 | 有返回值,无异常 | 阻塞 等待 | 超时等待 |
---|---|---|---|---|
添加 | add | offer() | put() | offer()重载 |
移除 | remove | poll() | take() | poll()重载 |
判断队列首 | element | peek() |
public static void test1(){
//不是泛型,是队列的大小
ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
System.out.println(blockingQueue.add("a"));
System.out.println(blockingQueue.add("b"));
System.out.println(blockingQueue.add("c"));
// //IllegalStateException 队列已满,抛异常
// System.out.println(blockingQueue.add("d"));
System.out.println(blockingQueue.remove());
System.out.println(blockingQueue.remove());
System.out.println(blockingQueue.remove());
//取完后也会报错,无元素的异常
}
//不抛出异常,队列满了再添加只会输出false,用.offer()方法
//取值也不会报异常,无值就会输出null值
public static void test2(){
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"));
System.out.println(blockingQueue.element());//查看队首值
System.out.println(blockingQueue.peek());//检查队首元素
System.out.println(blockingQueue.poll());
System.out.println(blockingQueue.poll());
System.out.println(blockingQueue.poll());
System.out.println(blockingQueue.poll());
}
//等待阻塞(一直阻塞)
public static void test3() throws InterruptedException {
ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
blockingQueue.put("a");
blockingQueue.put("b");
blockingQueue.put("c");
//blockingQueue.put("d");// 队列没有位置了,一直阻塞
System.out.println(blockingQueue.take());
System.out.println(blockingQueue.take());
System.out.println(blockingQueue.take());
System.out.println(blockingQueue.take()); //没有此元素,会一直阻塞
}
//等待阻塞(等待超时)
public static void test4() throws InterruptedException {
ArrayBlockingQueue<Object> blockingQueue = new ArrayBlockingQueue<>(3);
//存
blockingQueue.offer("a");
blockingQueue.offer("b");
blockingQueue.offer("c");
blockingQueue.offer("d", 2,TimeUnit.SECONDS);//重载方法,超时等待
//取
blockingQueue.poll();
blockingQueue.poll();
blockingQueue.poll();
blockingQueue.poll(2,TimeUnit.SECONDS);//重载方法,等不到取值就退出
}
SynchronousQueue,没有容量,进去一个元素必须等待取出来之后才能再往里面放一个元素,SynchronousQueue不存储元素,put一个值就会取出一个值,才可以再put一个值
package com.liu.bq;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.TimeUnit;
public class SynchronousQueueDemo {
public static void main(String[] args) {
/*
SynchronousQueue不存储元素,put一个值就会取出一个值,才可以再put一个值
*/
SynchronousQueue<String> 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();
}
},"T1").start();
new Thread(()->{
try {
TimeUnit.SECONDS.sleep(3);
System.out.println(Thread.currentThread().getName()+">"+synchronousQueue.take());
TimeUnit.SECONDS.sleep(3);
System.out.println(Thread.currentThread().getName()+">"+synchronousQueue.take());
TimeUnit.SECONDS.sleep(3);
System.out.println(Thread.currentThread().getName()+">"+synchronousQueue.take());
} catch (InterruptedException e) {
e.printStackTrace();
}
},"T2").start();
}
}
程序的运行,本质:占用系统的资源,优化资源的使用
线程池,连接池,内存池,对象池
池化技术:避免浪费资源,事先准备好资源,最大的并发数
线程池的好处:降低资源消耗,提高效率,方便管理,线程复用,可以控制最大并发数,管理线程
线程池不允许使用Executors去创建,而是通过ThreadPoolExecutor的方式,更加明确线程池的运行规则,避免资源耗尽的风险
FixedThreadPool 和 SingleThreadPool:允许的请求队列太长,可能会堆积大量的请求,会导致OOM
CachedThreadPool会造成太多线程,导致OOM
package com.liu.pool;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Demo01 {
public static void main(String[] args) {
// ExecutorService threadPool = Executors.newSingleThreadExecutor();//单一线程
// ExecutorService threadPool = Executors.newFixedThreadPool(5);//固定线程池的大小,最多有5个线程并发
ExecutorService threadPool = Executors.newCachedThreadPool();//可伸缩的
try {
for (int i = 1; i <= 10; i++) {
//使用线程池后,用线程池来创建线程
threadPool.execute(()->{
System.out.println(Thread.currentThread().getName()+" ok");
});
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//线程池用完,程序结束,关闭线程池
threadPool.shutdown();
}
}
}
int corePoolSize,//核心线程池大小
int maximumPoolSize,//最大核心线程池大小
long keepAliveTime,//超时后不操作就释放
TimeUnit unit,//超时单位
BlockingQueue workQueue,//阻塞队列
ThreadFactory threadFactory,//编程工厂,创建线程的
RejectedExecutionHandler handler) //拒绝策略
最大承装:队列加上最大线程池,超出就会触发拒绝策略
源码分析:
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,//21亿,造成OOM溢出
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
//本质:开启线程就是ThreadPoolExector()
public ThreadPoolExecutor(int corePoolSize,//核心线程池大小
int maximumPoolSize,//最大核心线程池大小
long keepAliveTime,//超时后不操作就释放
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.acc = System.getSecurityManager() == null ?
null :
AccessController.getContext();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
AbortPolicy:超出最大承载数就会抛出异常,拒绝策略
CallerRunsPolicy:会让main线程执行
DiscardPolicy():队列满了,丢弃剩余未执行的任务,不会抛出异常
DiscardOldestPolicy:队列满了,尝试会和最早的竞争,不抛出异常
package com.liu.pool;
import java.util.concurrent.*;
public class Demo01 {
public static void main(String[] args) {
// ExecutorService threadPool = Executors.newSingleThreadExecutor();//单一线程
// ExecutorService threadPool = Executors.newFixedThreadPool(5);//固定线程池的大小,最多有5个线程并发
// ExecutorService threadPool = Executors.newCachedThreadPool();//可伸缩的
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(
2,
5,
3,
TimeUnit.SECONDS,
new LinkedBlockingDeque<>(3),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy() // 人满了,拒绝接待,并抛出异常
);
try {
//最大承装:队列加上最大线程池,超出就会触发拒绝策略
for (int i = 1; i <= 10; i++) {
//使用线程池后,用线程池来创建线程
threadPool.execute(()->{
System.out.println(Thread.currentThread().getName()+" ok");
});
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//线程池用完,程序结束,关闭线程池
threadPool.shutdown();
}
}
}
自定义线程池最大线程到底该如何定义?
调优:
分情况,1.CPU密集型:有多少核,就有多少条线程去执行,通过代码获取cpu核数去定义最大线程池数,
System.out.println(Runtime.getRuntime().availableProcessors());
2.IO密集型:判断程序中十分耗IO的线程,io十分占用资源,至少留15个线程处理
程序员必备知识:lambda表达式,链式编程,函数式接口,Stream流式计算
函数式接口:只有一个方法的接口,简化编程模型
foreach方法的参数也是函数式接口
四大原生的函数式接口:
Consumer:消费型接口
//消费型接口
Consumer<String> consumer = new Consumer<String>() {
@Override
public void accept(String str) {
System.out.println(str);
}
};
Consumer<String> consumer1 = (str) ->{ System.out.println(str);};
BiConsumer:有两个参数,无返回值
Function:函数式接口:有一个输入参数,有一个输出,new一个函数式接口就是一个匿名内部类
public static void main(String[] args) {
// Function function = new Function() {
// @Override
// public String apply(String str) {
// return str;
// }
// };
Function<String,String> function = (str)->{return str;};
}
Predicate:断定型接口:有一个输入参数,返回值是布尔值
//断定型接口:有一个输入参数,返回值是布尔值
Predicate<String> predicate = new Predicate<String>() {
@Override
public boolean test(String str) {
return str.isEmpty();
}
};
Predicate<String> predicate1 = (str) ->{return str.isEmpty();};
}
Supplier:供给型接口
//供给型接口:无参数,有返回值
Supplier<String> supplier = new Supplier<String>() {
@Override
public String get() {
System.out.println("get()");
return null;
}
};
Supplier<Integer> supplier1 = () ->{return 1024;};
package com.liu.stream;
import java.util.Arrays;
import java.util.List;
/**
* 题目要求:一分钟完成此题,只用一行代码
* 5个用户,筛选:
* 1.id必须偶数
* 2.年龄必须大于23岁
* 3.用户名转为大写字母
* 4.用户名字倒着排序
* 5.只输出一个用户
*/
public class Test {
public static void main(String[] args) {
User u1 = new User(1, "a", 21);
User u2 = new User(2, "b", 22);
User u3 = new User(3, "c", 23);
User u4 = new User(4, "d", 24);
User u5 = new User(6, "e", 25);
//存储给list
List<User> list = Arrays.asList(u1, u2, u3, u4, u5);
//计算给stream流,链式编程
list.stream()
.filter(user -> {return user.getId()%2==0;})
.filter(user -> {return user.getAge()>23;})
.map(user -> {return user.getName().toUpperCase();})
.sorted((uu1,uu2) ->{return uu1.compareTo(uu2);})
.limit(1)
.forEach(System.out::println);
}
}
什么是forkJoin:并行执行任务,提高效率,处理大数据量。
forkjoin特点:工作窃取,线程可以抢占其他线程未进行的事务,双端队列
继承RecursiveTask:迭代任务,有返回值
继承RecursiveAction:迭代事件,无返回值
package com.liu.forkjoin;
import java.util.concurrent.RecursiveTask;
/*
大数据计算,用forkJoin,或者用stream并行流
如何使用forkJoin
1.new forkJoinPool 通过它执行
2.计算任务,forkjoinPool.submit(ForkJoinTask task)
3.计算类要继承 ForkJoin Task,RecursiveTask迭代任务
*/
public class ForkJoinTest extends RecursiveTask<Long> {
private Long start;
private Long end;
private Long temp = 10_0000L;
public ForkJoinTest(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;
ForkJoinTest task1 = new ForkJoinTest(start, middle);
task1.fork();//拆分任务,把任务压入线程队列
ForkJoinTest task2 = new ForkJoinTest(middle + 1, end);
task2.fork();//拆分任务,把任务压入线程队列
return task1.join() + task2.join();//结果相加
}
}
}
package com.liu.forkjoin;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.stream.LongStream;
public class Test {
public static void main(String[] args) throws ExecutionException, InterruptedException {
// test1();//耗时:7460
// test2(); //时间7398
test3(); //时间210
}
public static void test1(){
Long sum = 0L;
long start = System.currentTimeMillis();
for (Long i = 1L; i<=10_0000_0000 ; i++){
sum += i;
}
long end = System.currentTimeMillis();
System.out.println("sum="+sum+" 时间"+(end - start));
}
//forkjoin方式
public static void test2() throws ExecutionException, InterruptedException {
long start = System.currentTimeMillis();
ForkJoinPool forkJoinPool = new ForkJoinPool();
ForkJoinTask<Long> task = new ForkJoinTest(0L, 10_0000_0000L);
ForkJoinTask<Long> submit = forkJoinPool.submit(task);//有返回结果
Long sum = submit.get();//在此会阻塞等待
long end = System.currentTimeMillis();
System.out.println("sum="+sum+" 时间"+(end-start));
}
//stream并行流(最好的方式)
public static void test3(){
long start = System.currentTimeMillis();
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接口设计的初衷:对将来某个事件的结果进行建模
用异步回调用CompletableFuture类实现了Future接口,有两个方法,无返回值的runAsync(Runnable接口),有返回值的supplyAsync(),可以获得同步结果
package com.liu.future;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
/*
之前异步调用:ajax:不是编程语言,组合了浏览器内建的XMLHttpRequest对象和JavaScript和HTML DOM(显示或使用数据)
ajax应用程序可以将数据通过纯文本或者json文本传输,json是JavaScript对象标记语法,xml是可标记扩展语言,传输数据,存储数据
JUC下异步回调:CompletableFuture
异步执行
成功回调
失败回调
*/
public class Demo01 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//无返回值的异步回调
// 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("111111111111");
// completableFuture.get();//获取阻塞执行结果
//有返回值的异步回调
CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(() -> {
System.out.println(Thread.currentThread().getName()+"supplyAsync=>Integer");
return 1024;
});
System.out.println(completableFuture.whenComplete((t, u) -> {
System.out.println("t=>" + t); // 正常的返回结果
System.out.println("u=>" + u); // 无错误就是null,有错误就是打印错误的信息
}).exceptionally((e) -> {
System.out.println(e.getMessage());
return 520; //错误的返回结果
}).get());
}
}
Volatile是java虚拟机提供轻量级的同步机制
1.保证可见性
2.不保证原子性
3.禁止指令重排
JMM:Java内存模型,不存在的东西,概念,约定
JMM的一些同步约定:
1.线程解锁前,必须把共享变量立刻刷回主存
2.线程加锁前,必须读取主存中的最新值到工作内存中
3.加锁和解锁是同一把锁
线程,工作内存,主内存
8种操作:
lock:锁定作用于主存的变量,把一个变量标识未线程独占状态
unlock,read,load,use:把工作内存的变量传给执行引擎,assign:赋值,write,store
存在基本问题:线程B修改了主存的值,线程A不能及时可见!
保证可见性:
解决上述的基本问题,能够让线程A实时看到主存发生的变化
不保证原子性:
原子性:不可分割,线程A在执行任务的时候不能被打扰,也不能被分割,要么同时成功,要么同时失败,也就是不保证同时成功或失败
不用lock和synchronized(这个可以保证原子性),如何保证原子性?
使用JUC下的原子类AtomicBoolean,AtomicInteger,AtomicLong,这些类的底层都直接和操作系统挂钩,在内存种修改值,Unsafe类是一个特殊的类
禁止指令重排:
源代码–>编译器优化的重排–>指令并行也可能重排–>内存系统重排–>执行
处理器在进行指令重排的时候考虑数据之间的依赖性
内存屏障,CPU指令,保证内存可见性(避免指令重排)
构造器私有!!!,单例模式不安全
在单例模式中,使用volatile最多!
饿汉式单例模式:一开始就将对象加载了,会浪费内存
package com.liu.single;
//饿汉式单例模式:一开始就将对象加载了,会浪费内存
public class HungrySingleTest {
private byte[] data1 = new byte[1024*1024];
private byte[] data2 = new byte[1024*1024];
private byte[] data3 = new byte[1024*1024];
private byte[] data4 = new byte[1024*1024];
private HungrySingleTest(){
}
private final static HungrySingleTest HUNGRY_SINGLE_TEST = new HungrySingleTest();
public static HungrySingleTest getInstance(){//实例化
return HUNGRY_SINGLE_TEST;
}
}
双重检查锁加volatile原子性操作懒汉式单例,DCL模式
加volatile原因是因为new实例化时不是原子性的操作
package com.liu.single;
//懒汉式模式
public class LazyMan {
private LazyMan(){
System.out.println(Thread.currentThread().getName()+"ok");
}
private volatile static LazyMan lazyMan;
//双重检查锁模式懒汉式单例 DCL懒汉式 加上volatile原子性操作
public static LazyMan getInstance(){//实例
if (lazyMan==null){
synchronized (LazyMan.class){
if (lazyMan==null){
lazyMan = new LazyMan();//不是一个原子性的操作
/*
1.分配内存空间
2.执行构造方法,初始化对象
3.把这个对象指向这个空间
*/
}
}
}
return lazyMan;
}
//多线程并发
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
new Thread(()->{
LazyMan.getInstance();
}).start();
}
}
}
单例模式都不安全
1.通过私有构造器可以获得实例对象,通过反射也可以获得实例对象,不安全,解决方法:通过在构造器加锁可以解决
2.可以通过两个实例对象都是反射获取,依然可以破坏异常,解决方案:设计一个标识位关键字解决
3.依然可以破坏,通过反射的获取字段属性,并设为可获取的和重新设置标识位,又可以通过反射来破坏异常,解决:通过枚举类来解决
package com.liu.single;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
//懒汉式模式
public class LazyMan {
private static boolean liuxiang = false;
private LazyMan(){
synchronized (LazyMan.class){
if (liuxiang == false){
liuxiang = true;
}else {
throw new RuntimeException("不要用反射破坏异常");
}
}
System.out.println(Thread.currentThread().getName()+"ok");
}
private volatile static LazyMan lazyMan; //加volatile原因是因为new实例化时不是原子性的操作
//双重检查锁模式懒汉式单例 DCL懒汉式 加上volatile原子性操作
public static LazyMan getInstance(){//实例
if (lazyMan==null){
synchronized (LazyMan.class){
if (lazyMan==null){
lazyMan = new LazyMan();//不是一个原子性的操作
/*
1.分配内存空间
2.执行构造方法,初始化对象
3.把这个对象指向这个空间
*/
}
}
}
return lazyMan;
}
//反射可以破坏单例
public static void main(String[] args) throws Exception {
// LazyMan instance = LazyMan.getInstance(); 通过构造器获得实例对象
Field liuxiang = LazyMan.class.getDeclaredField("liuxiang");
liuxiang.setAccessible(true);//破坏字段
Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor();
declaredConstructor.setAccessible(true);//无视私有的构造器,破坏构造器
LazyMan instance = declaredConstructor.newInstance();
liuxiang.set(instance,false);//将第一个字改为false,依然可以破坏异常
/*假如两种实例方式都是通过反射的方式获得,依然可以破坏异常,解决方法:通过设置一个标识位,不通过反编译是获取不了这个标识位的关键字的
依然可以通过反射来破坏异常,通过类.class.getDeclaredField()方法,再利用setAccessible(true)方法破坏,
再重新设置为false就可以破坏异常
解决:通过枚举类来解决通过反射破坏异常
*/
LazyMan instance2 = declaredConstructor.newInstance();//用构造器new实例化,可以通过在构造器里加锁来阻止通过反射来破坏异常
System.out.println(instance.hashCode());
System.out.println(instance2.hashCode());
}
}
枚举:有两个参数!!
package com.liu.single;
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, InvocationTargetException, InstantiationException, IllegalAccessException {
EnumSingle instance1 = EnumSingle.INSTANCE;
Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(String.class,int.class);
declaredConstructor.setAccessible(true);
EnumSingle instance2 = declaredConstructor.newInstance();
//NoSuchMethodException异常无参的时候,IllegalArgumentException枚举确实不能破坏单例模式,有参的时候
System.out.println(instance1);
System.out.println(instance2);
}
}
什么是CAS?
cas:compareAndSet:比较并交换,比较工作内存中的值和主内存中的值,如果这个值是期望的,那么就执行操作,如果不是就一直循环,自旋锁
Java无法操作内存,但java可以通过native方法掉头c++,c++可以操作内存,Java的后门。通过Unsafe类操作内存
package com.liu.cas;
import java.util.concurrent.atomic.AtomicInteger;
public class CASDemo01 {
//cas:compareAndSet:比较并交换
public static void main(String[] args) {
AtomicInteger atomicInteger = new AtomicInteger(2022); //底层用的cas
//public final boolean compareAndSet(int expect, int update)
//如果期望的值达到了就更新,否则不更新,CAS是CPU的并发原语!
System.out.println(atomicInteger.compareAndSet(2022, 2023));
System.out.println(atomicInteger.get());
System.out.println(atomicInteger.compareAndSet(2022, 2023));
System.out.println(atomicInteger.get());
}
}
缺点:
1.循环会耗时
2.一次只能保证一个共享变量的原子性
ABA问题
狸猫换太子来解释
package com.liu.cas;
import java.util.concurrent.atomic.AtomicInteger;
public class CASDemo01 {
//cas:compareAndSet:比较并交换
public static void main(String[] args) {
AtomicInteger atomicInteger = new AtomicInteger(2022); //底层用的cas
//public final boolean compareAndSet(int expect, int update)
//如果期望的值达到了就更新,否则不更新,CAS是CPU的并发原语!
//对于写的sql:利用乐观锁解决问题:要知道谁动了线程!
//=======捣乱的线程=========
System.out.println(atomicInteger.compareAndSet(2022, 2023));
System.out.println(atomicInteger.get());
System.out.println(atomicInteger.compareAndSet(2023, 2022));
System.out.println(atomicInteger.get());
//=======捣乱的线程=========
System.out.println(atomicInteger.compareAndSet(2022, 6666));
System.out.println(atomicInteger.get());
}
}
是一种乐观思想,认为读多写少,遇到并发写的可能性低,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但在更新的时候会判断一下在此期间别人有没有去更新这个数据,采取在写时先读出当前版本号,然后加锁操作
当线程拿到资源时,上乐观锁,在提交前,其他的锁也可以操作这个资源,当冲突的时候,并发机制会保留前一个提交,打回后一个提交,让后一个线程重新获取资源后,再操作,再提交。
拿版本号去对比
lock是乐观锁
认为读少写多,遇到并发写的可能性高,每次拿数据的时候都认为别人会修改,所以每次在读写数据的时候都会上锁,别人想拿到这个数据就会block,直到拿到锁。
当线程拿到资源时,就对资源上锁,并在提交后,才释放资源,其他线程才能使用资源
synchronized是悲观锁
在并发量低的时候性能差不多,在并发量高的时候,乐观锁的性能远远优于悲观锁。
利用带版本号的原子引用解决ABA问题:知道谁动了线程,修改了数据,AtomicStampedReference() 带时间戳的,对应的思想是乐观锁
避坑:
Integer使用了对象缓存机制,默认范围是-128-127,推荐使用静态工厂方法valueOf获取对象实例,而不是new,因为valueOf使用缓存,而new一定会创建新的对象分配新的内存空间,超过这个区间的所有数据都会在堆上产生,不会复用对象
package com.liu.cas;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicStampedReference;
public class CASDemo02 {
public static void main(String[] args) {
//注意:如果泛型是包装类,注意对象的引用问题,因为是对象缓存机制,超过一定访问再new的话会新分配空间地址
//正常在业务操作,这里引用的都是一个对象
AtomicStampedReference<Integer> atomicInteger = new AtomicStampedReference<>(1, 1);
new Thread(()->{
int stamp = atomicInteger.getStamp();//获得版本号
System.out.println("a1=>"+stamp);
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
//下面这一段代码跟乐观锁里面的version+1版本号操作一样
System.out.println(atomicInteger.compareAndSet(1, 2,
atomicInteger.getStamp(), atomicInteger.getStamp() + 1));
System.out.println("a2=>"+atomicInteger.getStamp());
System.out.println(atomicInteger.compareAndSet(2, 1,
atomicInteger.getStamp(), atomicInteger.getStamp() + 1));
System.out.println("a3=>"+atomicInteger.getStamp());
},"a").start();
//与乐观锁的原理相同,由于中间修改过了线程,导致正常的线程不能修改,返回false
new Thread(()->{
int stamp = atomicInteger.getStamp();//版本号获取
System.out.println("b1=>"+stamp);
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(atomicInteger.compareAndSet(1, 6, stamp, stamp + 1));
System.out.println("b2=>"+atomicInteger.getStamp());
},"b").start();
}
}
公平锁:非常公平,不能插队,必须先来后到
非公平锁:可以插队,非常不公平(默认都是非公平锁)
Lock lock = new ReentrantLock(); //非公平锁
Lock lock = new ReentrantLock(true); //公平锁
递归锁:拿到外面的锁也就拿到里面的锁了
Lock锁必须配对,加了几把锁就要解几把锁!!!
spinlock
自定义锁测试:
package com.liu.lock;
import java.util.concurrent.atomic.AtomicReference;
//自旋锁
public class SpinLockDemo01 {
//Thread null
AtomicReference<Thread> atomicReference = new AtomicReference<>();
//加锁
public void myLock(){
Thread thread = Thread.currentThread();
System.out.println(Thread.currentThread().getName()+ "==> mylock");
//自旋锁
while (!atomicReference.compareAndSet(null,thread)){//所期望的是空,更新为当前线程
}
}
//解锁
public void myUnLock(){
Thread thread = Thread.currentThread();
System.out.println(Thread.currentThread().getName()+ "==> myUnlock");
atomicReference.compareAndSet(thread,null);//如果是期望的线程,就置为空
}
}
package com.liu.lock;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
/*
测试结果:
t1线程先拿到锁,t2再拿到锁,自旋,等待t1解锁,t2才会解锁
*/
public class Test {
public static void main(String[] args) throws InterruptedException {
// ReentrantLock reentrantLock = new ReentrantLock();
// reentrantLock.lock();
// reentrantLock.unlock();
//底层使用cas实现的自旋锁
SpinLockDemo01 lock = new SpinLockDemo01();
new Thread(()->{
lock.myLock();
try {
TimeUnit.SECONDS.sleep(2);
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.myUnLock();
}
},"t1").start();
TimeUnit.SECONDS.sleep(1);
new Thread(()->{
lock.myLock();
try {
TimeUnit.SECONDS.sleep(2);
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.myUnLock();
}
},"t2").start();
}
}
线程之间想获取其他线程的锁!
package com.liu.lock;
import java.util.concurrent.TimeUnit;
public class DeadLockDemo {
public static void main(String[] args) {
String lockA = "lockA";
String lockB = "lockB";
MyThread myThread = new MyThread(lockA,lockB);
MyThread myThread1 = new MyThread(lockB,lockA);
new Thread(myThread,"t1").start();
new Thread(myThread1,"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+"==>"+lockB);
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lockB){
System.out.println(Thread.currentThread().getName()+"lock:"+lockB+"==>"+lockA);
}
}
}
}
解决死锁问题:
1.使用jps -l
定位进程号,在终端里输入命令查看当前进程号
2.使用jstack 144444
命令查看进程问题
kDemo01();
new Thread(()->{
lock.myLock();
try {
TimeUnit.SECONDS.sleep(2);
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.myUnLock();
}
},"t1").start();
TimeUnit.SECONDS.sleep(1);
new Thread(()->{
lock.myLock();
try {
TimeUnit.SECONDS.sleep(2);
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.myUnLock();
}
},"t2").start();
}
}
## 4、死锁
**线程之间想获取其他线程的锁!**
```java
package com.liu.lock;
import java.util.concurrent.TimeUnit;
public class DeadLockDemo {
public static void main(String[] args) {
String lockA = "lockA";
String lockB = "lockB";
MyThread myThread = new MyThread(lockA,lockB);
MyThread myThread1 = new MyThread(lockB,lockA);
new Thread(myThread,"t1").start();
new Thread(myThread1,"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+"==>"+lockB);
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lockB){
System.out.println(Thread.currentThread().getName()+"lock:"+lockB+"==>"+lockA);
}
}
}
}
解决死锁问题:
1.使用jps -l
定位进程号,在终端里输入命令查看当前进程号