什么是JUC
java.util工具包
线程和进程
进程:一个程序,程序的集合
一个进程往往可以包含多个线程,至少包含一个;
java默认有几个进程?2个mian和GC
对于java而言:Thread、Runnable、Callable
Java真的可以开启线程吗?开不了,是java调用本地方法开启线程。
并发、并行
并发编程:并发、并行
并发(多个线程操作同一个资源)
- CPU一核,模拟出来多条线程,足够快,快速交替
并行(多个人一起行走)
- CPU多核,多个线程可以同时执行;线程池
并发编程的本质:充分利用CPU资源
所有的公司都很看重
线程状态
创建、运行、阻塞、等待、超时等待、死亡
wait和sleep的区别
1.来自不同的类
wait=>Object
sleep=>Thread
2.关于锁的释放
wait会释放,sleep不会释放锁
3.使用的范围是不同
wait必须在同步代码块,sleep可以在任何地方
4.释放需要捕获异常
sleep需捕获异常
wait不需要捕获异常
Lock锁
传统锁Synchronized
package org.song.Test;
/**
* Synchronized
*
* @author
*
*/
public class Test01 {
public static void main(String[] args) {
ticket ticket = new ticket();
new Thread(() -> {
for (int i = 0; i < 60; i++) {
ticket.sale();
}
}, "A").start();
new Thread(() -> {
for (int i = 0; i < 60; i++) {
ticket.sale();
}
}, "B").start();
new Thread(() -> {
for (int i = 0; i < 60; i++) {
ticket.sale();
}
}, "C").start();
}
}
// 改变实现Runnable接口的方法,让这个类成为一个干净的资源类,有助降低耦合
// 在资源类中,应该只有方法和属性,即是oop编程思想
// 改变为使用lambad表达式实现
class ticket {
private int ticket = 50;
public synchronized void sale() {
if (ticket > 0) {
System.out.println(Thread.currentThread().getName() + "买了票" + ticket-- + "剩余" + ticket);
}
}
}
Lock
常用实现类Renntrantlock
公平锁
十分公平,不可以插队
非公平锁
十分不公平,可以插队(默认)
Renntranlock
- 无参构造器,默认的是非公平锁
- 有一个传入boolean值的构造器,改变为公平锁
package org.song.Test;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* Synchronized
*
* @author
*
*/
public class Test01 {
public static void main(String[] args) {
ticket ticket = new ticket();
new Thread(() -> {
for (int i = 0; i < 60; i++) {
ticket.sale();
}
}, "A").start();
new Thread(() -> {
for (int i = 0; i < 60; i++) {
ticket.sale();
}
}, "B").start();
new Thread(() -> {
for (int i = 0; i < 60; i++) {
ticket.sale();
}
}, "C").start();
}
}
/*
* 1、实现接口类ReentrantLock
* 2、lock.lock(); 加锁
* 3、将要写的代码放入try中
* 4、在finally代码块中解锁: lock.unlock();
*/
class ticket {
private int ticket = 50;
Lock lock=new ReentrantLock();
public void sale() {
lock.lock();
try {
if (ticket > 0) {
System.out.println(Thread.currentThread().getName() + "买了票" + ticket-- + "剩余" + ticket);
}
} finally {
lock.unlock();
}
}
}
Synchronized和Lock的区别
- Synchronized 内置java关键字,Lock是一个java类
- Synchronized 无法获取锁的状态,Lock可以判断是否获取到了锁
- Synchronized 会自动释放锁,lock必须要手动释放锁!如果不释放锁,死锁
- Synchronized 线程1(获得锁,阻塞),线程2(等待,一直等);Lock就不一定会等待下去
- Synchronized 可重入锁,不可以中断,非公平;Lock,可重入锁,非公平(可以自己设置)
- Synchronized 适合锁少量的代码同步,Lock适合锁大量的同步代码
生产者、消费者问题
synchronized版
- 使用if做判断的时候,会产生虚假唤醒,做判断应该采用while
package org.song.Test;
/**
* Synchronized版的生产者消费者问题
* @author
*
*/
public class Test02 {
public static void main(String[] args) {
Pc pc=new Pc();
new Thread(()->{
try {
for (int i = 0; i < 100; i++) {
pc.up();
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
},"A").start();
new Thread(()->{
try {
for (int i = 0; i < 100; i++) {
pc.down();
};
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
},"B").start();
new Thread(()->{
try {
for (int i = 0; i < 100; i++) {
pc.down();
};
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
},"C").start();
new Thread(()->{
try {
for (int i = 0; i < 100; i++) {
pc.up();
};
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
},"D").start();
}
}
class Pc{
private int num;
//+1
public synchronized void up() throws InterruptedException{
if(num!=0){
//等待
this.wait();
}
num++;
System.out.println(Thread.currentThread().getName()+"=>"+num);
this.notifyAll();
}
//-1
public synchronized void down() throws InterruptedException{
if(num==0){
//等待
this.wait();
}
num--;
System.out.println(Thread.currentThread().getName()+"=>"+num);
this.notifyAll();
}
}
采用while后:
package org.song.Test;
/**
* Synchronized版的生产者消费者问题
* @author
*
*/
public class Test02 {
public static void main(String[] args) {
Pc pc=new Pc();
new Thread(()->{
try {
for (int i = 0; i < 100; i++) {
pc.up();
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
},"A").start();
new Thread(()->{
try {
for (int i = 0; i < 100; i++) {
pc.down();
};
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
},"B").start();
new Thread(()->{
try {
for (int i = 0; i < 100; i++) {
pc.down();
};
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
},"C").start();
new Thread(()->{
try {
for (int i = 0; i < 100; i++) {
pc.up();
};
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
},"D").start();
}
}
class Pc{
private int num;
//+1
public synchronized void up() throws InterruptedException{
while(num!=0){
//等待
this.wait();
}
num++;
System.out.println(Thread.currentThread().getName()+"=>"+num);
this.notifyAll();
}
//-1
public synchronized void down() throws InterruptedException{
while(num==0){
//等待
this.wait();
}
num--;
System.out.println(Thread.currentThread().getName()+"=>"+num);
this.notifyAll();
}
}
JUC版
- 使用Lock加锁
- 使用Condtion监视器的await和single替代wait和notifyAll
- 通过创建多个condition监视器来实现精准唤醒,以实现业务流程,例如:购买-》付钱-》发货
- 创建condition的方法:Condition condition1=lock.newCondition();
package org.song.Test;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* JUC版的生产者消费者问题
*
* @author
*
*/
public class Test02 {
public static void main(String[] args) {
Pc pc = new Pc();
new Thread(() -> {
try {
for (int i = 0; i < 100; i++) {
pc.up();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "A").start();
new Thread(() -> {
try {
for (int i = 0; i < 100; i++) {
pc.down();
}
;
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "B").start();
new Thread(() -> {
try {
for (int i = 0; i < 100; i++) {
pc.down();
}
;
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "C").start();
new Thread(() -> {
try {
for (int i = 0; i < 100; i++) {
pc.up();
}
;
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "D").start();
}
}
class Pc {
private int num;
//创建Lock
private Lock lock=new ReentrantLock();
//创建condition
private Condition condition1=lock.newCondition();
// +1
public void up() throws InterruptedException {
lock.lock();
try {
while(num!=0){
condition1.await();
}
num++;
System.out.println(Thread.currentThread().getName() + "=>" + num);
condition1.signalAll();
} catch (Exception e) {
// TODO: handle exception
}finally {
lock.unlock();
}
}
// -1
public void down() throws InterruptedException {
lock.lock();
try {
while(num==0){
condition1.await();
}
num--;
System.out.println(Thread.currentThread().getName() + "=>" + num);
condition1.signalAll();
} catch (Exception e) {
// TODO: handle exception
}finally {
lock.unlock();
}
}
}
与Synchronized的wait和notify不同的是:condition可以通过创建多个监听器来达到精准唤醒
package org.song.Test;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Test03 {
public static void main(String[] args) {
A a = new A();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
a.printA();
}
}, "A").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
a.printB();
}
}, "B").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
a.printC();
}
}, "C").start();
}
}
class A {
// 创建lock锁
private Lock lock = new ReentrantLock();
// 创建condition监视器
private Condition condition1 = lock.newCondition();
private Condition condition2 = lock.newCondition();
private Condition condition3 = lock.newCondition();
// 创建一个标志量,来实现业务的实现顺序
private int num = 1;
public void printA() {
lock.lock();
try {
while (num != 1) {
condition1.await();
}
num = 2;
System.out.println(Thread.currentThread().getName() + "=>A");
condition2.signal();
} catch (Exception e) {
} finally {
lock.unlock();
}
}
public void printB() {
lock.lock();
try {
while (num != 2) {
condition2.await();
}
num = 3;
System.out.println(Thread.currentThread().getName() + "=>B");
condition3.signal();
} catch (Exception e) {
} finally {
lock.unlock();
}
}
public void printC() {
lock.lock();
try {
while (num != 3) {
condition3.await();
}
num = 1;
System.out.println(Thread.currentThread().getName() + "=>C");
condition1.signal();
} catch (Exception e) {
} finally {
lock.unlock();
}
}
}
8锁现象
什么是锁?
如何判断锁的是谁?
深刻理解锁是什么
8锁就是关于锁的八个问题
实例对象产生的锁有很多个(普通同步方法),Class产生的锁只有一个(static同步方法)
package org.song.Test;
import java.util.concurrent.TimeUnit;
/**
* 8锁现象 关于锁的8个问题
* 1、标准情况下,一个对象,是先执行发短信还是打电话?
* 发短信
* 2、SendMsg()延迟四秒,一个对象,是先执行发短信还是打电话?
* 发短信
* 原因:Synchronized锁的对象是方法的调用者,即在只创建一个对象的时候,就只有一把锁
*
* 3、增加了一个普通方法,它不是同步方法,即没有锁,所以不受锁的影响
* 4、两个对象,两个同步方法,发短信还是打电话?
* 打电话
* 5、一个对象,两个静态同步方法,发短信还是打电话
* 6、两个对象,两个静态同步方法,发短信还是打电话
* 原因:static同步方法,在类加载时就已经存在了,所以Synchronized锁的是class对象
* class锁
* 7、一个静态同步方法、一个普通同步方法,一个对象
* 8、一个静态同步方法、一个普通同步方法,两个对象
*
* 实例对象生成的锁有很多个,Class的锁在类加载的时候就产生了,只有一个
* @author
*
*
*/
public class Test04 {
public static void main(String[] args) throws InterruptedException {
Phone phone = new Phone();
//问题一、二
// new Thread(() -> {
// phone.SendMsg();
// }, "A").start();
//
// TimeUnit.SECONDS.sleep(1);
//
// new Thread(() -> {
// phone.Call();;
// }, "B").start();
}
}
class Phone {
public synchronized void SendMsg() {
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("发短信");
}
public synchronized void Call() {
System.out.println("电话");
}
public static synchronized void SendMsg2() {
System.out.println("发短信");
}
public static synchronized void Call2() {
System.out.println("电话");
}
public void Hello() {
System.out.println("hello");
}
}
集合类不安全
List不安全
- 使用Vector
- 使用Collections工具类的synchronizedList(new ArrayList())
- 使用JUC的CopyOnWriteArrayList(); 写入时复制
- Vector多使用Synchronized同步方法,效率比CopyOnWriteArrayList效率低
package org.song.Test;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
import java.util.Vector;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* 集合类不安全
*
* @author
*
*/
public class Test05 {
public static void main(String[] args) {
/**
* 并发下ArrayList不安全
* 1、List<String> list = new Vector<>(); Vector多使用的Synchronized同步方法,效率低
* 2、List<String> list =Collections.synchronizedList(new ArrayList<>());
* 3、List<String> list =new CopyOnWriteArrayList<>(); 多使用lock锁,效率高
* CopyOnWriterArrayList() 写入时复制,即在写入前复制一份出来,等写完后在插入之前的,保证了操作的原子性
* @param 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不安全
package org.song.Test;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CopyOnWriteArraySet;
public class Test06 {
public static void main(String[] args) {
/**
* Set<String> set = new HashSet<>();
* 1、Set<String> set = Collections.synchronizedSet(new HashSet<>());
* 2、Set<String> set =new CopyOnWriteArraySet<String>();
*/
Set<String> set =new CopyOnWriteArraySet<String>();
for (int i = 0; i < 30; i++) {
new Thread(() -> {
set.add(UUID.randomUUID().toString().substring(0, 5));
System.out.println(set);
}, String.valueOf(i)).start();
}
}
}
set底层原理
public HashSet() {
map = new HashMap<>();
}
Map不安全
package org.song.Test;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
public class Test06 {
public static void main(String[] args) {
//concurrent
//ConcurrentModificationException 并发修改异常
/**
* Map<String, String> map=new HashMap<>();
* 1、Map<String, String> map=Collections.synchronizedMap(new HashMap<>());
* 2、Map<String, String> map=new ConcurrentHashMap<>();
*/
Map<String, String> map=new ConcurrentHashMap<>();
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
- 有返回值
- 可以抛出异常
- 方法不同、call方法
FutrueTask是Runnable的一个实现类
package org.song.Test;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class Test07 {
public static void main(String[] args) throws InterruptedException, ExecutionException {
MyThread myThread=new MyThread();
FutureTask<Integer> futureTask=new FutureTask<>(myThread);
new Thread(futureTask,"A").start();
Integer a=futureTask.get();
System.out.println(a);
}
}
class MyThread implements Callable<Integer>{
@Override
public Integer call() throws Exception {
System.out.println("call()");
return 1024;
}
}
常用的辅助类
countDownLatch
- CountDownLatch countDownLatch = new CountDownLatch(6); 传递一个参数
- countDownLatch.countDown(); 参数-1
- countDownLatch.await(); 等待参数归零后,再执行其后的代码
- 可以认为是一个减法计数器
package org.song.Test;
import java.util.concurrent.CountDownLatch;
public class Test08 {
public static void main(String[] args) throws InterruptedException {
CountDownLatch countDownLatch = new CountDownLatch(6);
for (int i = 0; i < 6; i++) {
new Thread(() -> {
countDownLatch.countDown();
System.out.println(Thread.currentThread().getName());
}).start();
}
countDownLatch.await();
System.out.println("close door");
}
}
CyclicBarrier
可以认为是一个加法计数器
package org.song.Test;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
public class Test09 {
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;
new Thread(() -> {
System.out.println(Thread.currentThread().getName()+"收集了"+temp+"龙珠");
try {
cyclicBarrier.await();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (BrokenBarrierException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
},String.valueOf(i)).start();
}
}
}
Semaphore
参照操作系统的信号量
package org.song.Test;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
public class Test10 {
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);
System.out.println(Thread.currentThread().getName()+"离开车位");
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
semaphore.release();
}
},String.valueOf(i)).start();
}
}
}
读写锁ReadWriteLock
读可以被多线程同时读,写只能一个线程来写:提高效率
package org.song.Test;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class Test11 {
public static void main(String[] args) {
MyMap myMap = new MyMap();
for (int i = 0; i < 5; i++) {
final int temp=i;
new Thread(() -> {
myMap.put(String.valueOf(temp), String.valueOf(temp));
myMap.get(String.valueOf(temp));
},String.valueOf(i)).start();
}
for (int i = 0; i < 5; i++) {
final int temp=i;
new Thread(() -> {
myMap.get(String.valueOf(temp));
},String.valueOf(i)).start();
}
}
}
class MyMap {
private volatile Map<String, Object> map = new HashMap<String, Object>();
private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
public void put(String key, Object value) {
readWriteLock.writeLock().lock();
try {
System.out.println(Thread.currentThread().getName() + "写入");
map.put(key, value);
System.out.println(Thread.currentThread().getName() + "写入完成");
} catch (Exception e) {
// TODO: handle exception
} finally {
readWriteLock.writeLock().unlock();
}
}
public void get(String key) {
readWriteLock.readLock().lock();
try {
System.out.println(Thread.currentThread().getName() + "读");
Object o = map.get(key);
System.out.println(Thread.currentThread().getName() + "读完成");
} catch (Exception e) {
} finally {
readWriteLock.readLock().unlock();
}
}
}
阻塞队列
BolckingQueue:阻塞队列:一端取数据,当队列满的时候,会发生写入的阻塞,当队列为空的时候,会发生取的阻塞
BolckingDeque:阻塞双端队列:可以从两端取数据
AbstractQueue:非阻塞队列
四组API
抛出异常 | 不抛出异常,有返回值(true&false) | 阻塞 | 不阻塞 | |
---|---|---|---|---|
添加 | add() | offer() | put() | put(…) |
移除 | remove() | poll() | take() | take(.) |
检测队首元素 | element() | peek() | - | - |
package org.song.Test;
import java.util.concurrent.ArrayBlockingQueue;
public class Test12 {
public static void main(String[] args) {
BlockingQueue1();
}
public static void BlockingQueue1(){
ArrayBlockingQueue<String> arrayBlockingQueue = new ArrayBlockingQueue<>(3);
//ArrayBlockingQueue的参数为队列的大小
//抛出异常
arrayBlockingQueue.add("a");
arrayBlockingQueue.add("b");
arrayBlockingQueue.add("c");
System.out.println(arrayBlockingQueue.element());
//超过队列容量,抛出异常
arrayBlockingQueue.add("d");
System.out.println(arrayBlockingQueue.remove());
System.out.println(arrayBlockingQueue.remove());
System.out.println(arrayBlockingQueue.remove());
//队列已为空,抛出异常
System.out.println(arrayBlockingQueue.remove());
}
}
package org.song.Test;
import java.util.concurrent.ArrayBlockingQueue;
public class Test12 {
public static void main(String[] args) {
BlockingQueue1();
}
public static void BlockingQueue1(){
ArrayBlockingQueue<String> arrayBlockingQueue = new ArrayBlockingQueue<>(3);
//ArrayBlockingQueue的参数为队列的大小
//不抛出异常,返回true或false
arrayBlockingQueue.offer("a");
arrayBlockingQueue.offer("b");
arrayBlockingQueue.offer("c");
System.out.println(arrayBlockingQueue.peek());
//超过队列容量,返回false
System.out.println(arrayBlockingQueue.offer("d"));
System.out.println(arrayBlockingQueue.poll());
System.out.println(arrayBlockingQueue.poll());
System.out.println(arrayBlockingQueue.poll());
//队列已为空,放回null
System.out.println(arrayBlockingQueue.poll());
}
}
package org.song.Test;
import java.util.concurrent.ArrayBlockingQueue;
public class Test12 {
public static void main(String[] args) throws InterruptedException {
BlockingQueue1();
}
public static void BlockingQueue1() throws InterruptedException{
ArrayBlockingQueue<String> arrayBlockingQueue = new ArrayBlockingQueue<>(3);
//ArrayBlockingQueue的参数为队列的大小
//阻塞时,一直阻塞
arrayBlockingQueue.put("a");
arrayBlockingQueue.put("b");
arrayBlockingQueue.put("c");
//System.out.println(arrayBlockingQueue.peek());
//超过队列容量,一直等待队列有空间,一直阻塞
arrayBlockingQueue.put("d");
System.out.println(arrayBlockingQueue.take());
System.out.println(arrayBlockingQueue.take());
System.out.println(arrayBlockingQueue.take());
//队列已为空,队列为空,一直等待队列有元素
System.out.println(arrayBlockingQueue.take());
}
}
package org.song.Test;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.TimeUnit;
public class Test12 {
public static void main(String[] args) throws InterruptedException {
BlockingQueue1();
}
public static void BlockingQueue1() throws InterruptedException{
ArrayBlockingQueue<String> arrayBlockingQueue = new ArrayBlockingQueue<>(3);
//ArrayBlockingQueue的参数为队列的大小
//阻塞时,一直阻塞
arrayBlockingQueue.offer("a");
arrayBlockingQueue.offer("b");
arrayBlockingQueue.offer("c");
//System.out.println(arrayBlockingQueue.peek());
//超过队列容量,设置时间后,直接结束程序
arrayBlockingQueue.offer("d",2,TimeUnit.SECONDS);
System.out.println(arrayBlockingQueue.poll());
System.out.println(arrayBlockingQueue.poll());
System.out.println(arrayBlockingQueue.poll());
//队列已为空,设置时间后,直接结束程序
System.out.println(arrayBlockingQueue.poll(2,TimeUnit.SECONDS));
}
}
SynchronousQueue
同步队列
没有容量
即最多只能存一个,存进一个 后,等待它取出来,才能存
package org.song.Test;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.SynchronousQueue;
public class Test13 {
public static void main(String[] args) {
BlockingQueue<String> synchronousQueue = new SynchronousQueue<>();
new Thread(() -> {
try {
System.out.println(Thread.currentThread().getName()+"put1");
synchronousQueue.put("a");
System.out.println(Thread.currentThread().getName()+"put1");
synchronousQueue.put("b");
System.out.println(Thread.currentThread().getName()+"put1");
synchronousQueue.put("c");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
},"A").start();
new Thread(() -> {
try {
System.out.println(Thread.currentThread().getName()+"take1");
synchronousQueue.take();
System.out.println(Thread.currentThread().getName()+"put1");
synchronousQueue.take();
System.out.println(Thread.currentThread().getName()+"put1");
synchronousQueue.take();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
},"B").start();
}
}
线程池(重点)
池化技术
程序的运行、本质:占用系统资源!优化资源的使用-》池化技术
线程池、连接池、内存池、对象池
池化技术:事先准备好一些资源,有人要用,就来我这里拿,用完后还给我
线程池的好处:
- 降低资源的消耗
- 提高的响应的速度
- 方便管理
线程复用、可以控制最大并发数、管理线程
线程池:三大方法
- ExecutorService threadPool = Executors.newSingleThreadExecutor(); 单线程,固定只有一个线程
- ExecutorService threadPool = Executors.newFixedThreadPool(5); 通过传递的参数,生成一个固定大小的线程池
- ExecutorService threadPool = Executors.newCachedThreadPool(); 遇强则强
package org.song.Test;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Test14 {
public static void main(String[] args) {
// 线程池的三大方法
// ExecutorService threadPool = Executors.newSingleThreadExecutor();
// ExecutorService threadPool = Executors.newFixedThreadPool(5);
ExecutorService threadPool = Executors.newCachedThreadPool();
for (int i = 0; i < 100; i++) {
threadPool.execute(() -> {
System.out.println(Thread.currentThread().getName()+" ok");
});
}
}
}
线程池:七大参数
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iqjPnChC-1630498026446)(C:\Users\宋杰的电脑\Downloads\未命名文件.jpg)]
//底层代码
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.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
ExecutorService threadPool=new ThreadPoolExecutor(
corePoolSize, //核心线程
maximumPoolSize, //最大核心线程
keepAliveTime, //超时释放时间
unit, //TimeUnit 的单位
workQueue, //阻塞队列,相当于银行的候客区
threadFactory, //创建线程的工厂类,一般不改动
handler)//当线程数量超过最大核心线程+阻塞队列数,则会拒绝线程,有四种拒绝策略
- AbortPolicy() 抛出异常
- CallerRunPolicy() 哪来回哪里去
- DiscardPolicy() 丢弃任务,不抛出异常
- DiscardOldestPolicy() 与最早的沟通,成功则替换,反之丢弃
package org.song.Test;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class Test15 {
public static void main(String[] args) {
ExecutorService threadPool = new ThreadPoolExecutor(
2,
5,
3,
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(3),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.DiscardOldestPolicy());
/*
* new ThreadPoolExecutor.AbortPolicy() 队列满了,抛出异常
* new ThreadPoolExecutor.CallerRunsPolicy() 队列满了,哪里来回哪里去,例如main
* new ThreadPoolExecutor.DiscardPolicy() 队列满了,不抛出异常
* new ThreadPoolExecuto.DiscardOldestPolicy() 队列满了,尝试与最早的线程沟通,其执行结束则替换,反之抛弃任务
*/
for (int i = 0; i < 9; i++) {
threadPool.execute(() -> {
System.out.println(Thread.currentThread().getName()+" ok");
});
}
}
}
线程池执行定时任务
- https://segmentfault.com/a/1190000008038848
public class ScheduledExecutorServiceTest {
public static void main(String[] args) throws Exception {
ScheduledExecutorService timer = Executors.newSingleThreadScheduledExecutor();
TimerTask timerTask = new TimerTask(2000); // 任务需要 2000 ms 才能执行完毕
System.out.printf("起始时间:%s\n\n", new SimpleDateFormat("HH:mm:ss").format(new Date()));
// 延时 1 秒后,按 3 秒的周期执行任务
timer.scheduleAtFixedRate(timerTask, 1000, 3000, TimeUnit.MILLISECONDS);
}
private static class TimerTask implements Runnable {
private final int sleepTime;
private final SimpleDateFormat dateFormat;
public TimerTask(int sleepTime) {
this.sleepTime = sleepTime;
dateFormat = new SimpleDateFormat("HH:mm:ss");
}
@Override
public void run() {
System.out.println("任务开始,当前时间:" + dateFormat.format(new Date()));
try {
System.out.println("模拟任务运行...");
Thread.sleep(sleepTime);
} catch (InterruptedException ex) {
ex.printStackTrace(System.err);
}
System.out.println("任务结束,当前时间:" + dateFormat.format(new Date()));
System.out.println();
}
}
设置最大线程池(调优)
-
CPU 密集型
通过查看你的电脑有几个cpu,来设置最大核心线程
package org.song.Test; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; public class Test15 { public static void main(String[] args) { System.out.println(Runtime.getRuntime().availableProcessors()); ExecutorService threadPool = new ThreadPoolExecutor( 2, Runtime.getRuntime().availableProcessors(), 3, TimeUnit.SECONDS, new LinkedBlockingQueue<>(3), Executors.defaultThreadFactory(), new ThreadPoolExecutor.DiscardOldestPolicy()); for (int i = 0; i < 9; i++) { threadPool.execute(() -> { System.out.println(Thread.currentThread().getName()+" ok"); }); } } }
-
IO 密集型
通过设置你的系统最消耗io的任务个数的两倍
四大函数式接口(重要)
新时代的程序员:lambda表达式、链式编程、函数式接口、Stream流计算
函数式接口:只有一个方法的接口;简化编程模型
-
Fuction:有一个输入参数,一个输出,只要是函数式接口,可以lambda表达式
简化:Fuction<String,String> fuction=(str)->{return str;};
-
Predicate:断定型接口,有一个输入参数,返回值为boolean值
Predicate predicate2=(sss)->{return sss.isEmpty();};
-
Consumer:消费型接口,只有输入,没有返回值
Consumer cuConsumer2=(str)->{return;};
-
Supplier:供给型接口,没有参数,只有返回值
Supplier supplier2=()->{return 1024;};
public static void main(String[] args) {
Function<String, String> function = new Function<String, String>() {
@Override
public String apply(String t) {
return t;
}
};
//简化
Function function2 =(str)->{return str;};
System.out.println(function.apply("hhh"));
}
public static void main(String[] args) {
Predicate predicate=new Predicate<String>() {
@Override
public boolean test(String t) {
return t.isEmpty();
}
};
//简化
Predicate<String> predicate2=(sss)->{return sss.isEmpty();};
System.out.println(predicate.test("sss"));
}
public static void main(String[] args) {
Consumer<String> consumer=new Consumer<String>() {
@Override
public void accept(String t) {
System.out.println(t);
}
};
Consumer<String> cuConsumer2=(str)->{return;};
consumer.accept("你好");
}
public static void main(String[] args) {
Supplier<Integer> supplier=new Supplier<Integer>() {
@Override
public Integer get() {
// TODO Auto-generated method stub
return 1024;
}
};
Supplier<Integer> supplier2=()->{return 1024;};
System.out.println(supplier.get());
}
Stream流式计算
什么是Stream流式计算?
存储+计算
ForkJoin
ForkJoin 在JDK1.7后, 并发执行任务,提高效率,大数据量
分支合并
ForkJoin的特点:工作窃取
在a分了多个任务,b分了多个任务时,当a完成的时候,但b还为完成,此时a会窃取b的任务执行,以提高效率 (双端队列)
package org.song.Test;
import java.util.concurrent.RecursiveTask;
public class Test17 extends RecursiveTask<Long> {
private Long start;
private Long end;
//使用forkjoin的临界值
private Long temp=10000L;
public Test17(Long start,Long end) {
this.start=start;
this.end=end;
}
@Override
protected Long compute() {
if((end-start)<temp){
Long sum=0L;
for (Long i = start; i < end; i++) {
sum+=i;
}
return sum;
}else{
//将一个任务拆分成多个任务
long middle=(end+start)/2;
Test17 task1=new Test17(start, middle);
task1.fork();
Test17 task2=new Test17(middle+1, end);
task2.fork();
//forkjoinTask.fork() 将拆分后的任务加入队列中
//forkjoinTask.join() 获取子任务的返回值
return task1.join()+task2.join();
}
}
}
package org.song.Test;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.stream.LongStream;
public class Test18 {
public static void main(String[] args) throws InterruptedException, ExecutionException {
test1();
}
//使用传统的求和
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("时间:"+(end-start));
}
//使用forkjoin
public static void test2() throws InterruptedException, ExecutionException{
Long start=System.currentTimeMillis();
ForkJoinPool forkJoinPool = new ForkJoinPool();
ForkJoinTask<Long> task=new Test17(0L, 10_0000_0000L);
//forkjoinpool的两个执行方法 executor(无返回)和submit(返回一个forkjoinTask)
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("时间:"+(end-start));
}
}
使用步骤:
- 让一个计算类继承RecursiveTask ,它是ForkjoinTask的子类,有一个返回结果,重写他的方法compute(),可将多个任务分为几个小任务,fork()和join() fork分 join合
- 使用forkjoinPool的executor(无返回)合submit(有返回)
- 使用submit.get()获取返回结果
异步回凋
类似于Ajax
CompletableFutrue
-
无返回runAsysc
-
有返回supplyAsysc
获取返回值:whencomplete exceptionally
package org.song.Test;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
public class Test19 {
@SuppressWarnings("static-access")
public static void main(String[] args) throws InterruptedException, ExecutionException {
/*
* Futrue 其实现类为Completablefutrue 1、异步执行 2、成功回调 3、失败回调
*
*/
// 无返回的回调 runAsysc
CompletableFuture<Void> completableFuture = new CompletableFuture<String>().runAsync(() -> {
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "runAsysc");
});
System.out.println("hello");
completableFuture.get();
// 有返回的回调 whenComplete exceptionally错误信息
CompletableFuture<Integer> completableFuture2 = new CompletableFuture<Integer>().supplyAsync(() -> {
int a=10/0;
return 1024;
});
System.out.println(completableFuture2.whenComplete((t, u) -> {
System.out.println(t); //成功回调返回 return 1024
System.out.println(u); //失败回调返回错误信息
}).exceptionally((e) -> {
System.out.println(e.getMessage());
return 233;
}).get());
}
}
JMM
请你谈谈Volatile的理解
Volatile时java虚拟机提供轻量级的同步机制
- 保证可见性
- 不保证原子性
- 禁止指令重排
什么是JMM
JMM:java内存模型,不存在的东西,概念!
关于JMM的一些同步的约定
- 线程解锁前,必须把共享变量立刻刷回主存
- 线程加锁前,必须读取主存中的最新值到工厂内存中
- 加锁合解锁是同一把锁
线程:工作内存、主内存
可见性:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xDw60g3m-1630498026449)(C:\Users\宋杰的电脑\Downloads\未命名文件 (1)].jpg)
lock(锁定):作用于主内存的变量,把一个变量标识为一条线程独占的状态。
unclock(解锁):作用于主内存的变量,把一个处于锁定的状态释放出来。
read(读取):作用于主内存的变量,把一个变量的值从主内存传输到线程的工作内存中
load(载入):作用于工作内存的变量,把read操作从主内存 得到的变量值放入工作内存的变量副本中。
use(使用):作用于工作内存的变量,把工作内存中一个变量的值传递给执行引擎,每当虚拟机遇到一个需要使用到变量的 值的字节码指令时将会执行这个操作。
assign(赋值):作用于工作内存的变量,把一个从执行引擎接收到的值 赋值给工作内存的变量,每当虚拟机遇到一个给变 量赋值的字节码指令时执行这个操作。
store(存储):作用于工作内存的变量,把工作内存中的一个变量的值传递到主内存,以便write操作使用。
write(写入):作用于主内存的变量,把store操作从工作内存中得到的变量的值放入主内存的变量中。
Volatile
- 保证可见性
- 不保证原子性
- 禁止指令重排
package org.song.Test;
import java.util.concurrent.TimeUnit;
//volatile
//保证可见性
public class Test20 {
private static int num;
private volatile static int num;
public static void main(String[] args) throws InterruptedException {
new Thread(() -> {
while(num==0){
}
}).start();
TimeUnit.SECONDS.sleep(2);
num=1;
System.out.println("main");
}
}
package org.song.Test;
/*
* Volatile
* 不保证原子性
*/
public class Test21 {
private volatile static int num;
public static void add() {
num++;
}
public static void main(String[] args) {
for (int i = 0; i < 20; i++) {
new Thread(() -> {
for (int j = 0; j < 1000; j++) {
add();
}
}).start();
}
System.out.println(num);
//加了Volatile结果依旧小于20000
}
}
package org.song.Test;
/*
* Volatile
* 不保证原子性
*/
public class Test21 {
private volatile static int num;
public static void add() {
num++;
}
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(num);
}
}
解决方法:
- 加锁,使方法成为同步方法
- 使用AtomicIntegerg类定义:java.util.concurrent.Atomic
private static AtomicInteger num=new AtomicInteger();
public static void add() {
num.getAndIncrement();
}
指令重排
什么是指令重排:你写的程序,计算机并不是按照你写的那样去执行
源代码–》编译器优化的重排–》指令并行也可能重排–》内存系统也会重排–》执行
处理器在进行指令重排的时候,考虑:数据之间的依赖性
int x=1;//1
int y=2;//2
x=x+5;//3
y=x*x;//
//你希望的执行顺序是1234,但cpu指令重排不一定是1234,但发生错误的概率比较小
volatile可以避免指令重排:
内存屏障,cpu指令,作用:
- 保证特定操作的执行顺序
- 可以保证某些变量的内存可见性(实现了可见性)
- 在volatile修饰的属性上下均加上了内存屏障,禁止了指令重排
彻底玩转单例模式
饿汉式单例、DCL懒汉式单例
反射中的setAccessible(true)
- 通知jdk关闭安全检查
- 提高了反射创建对象的速度,但有安全性的问题
- 可以破环单例模式
饿汉式单例
package org.song.Man;
public class HungryMan {
//缺点:浪费内存资源
//构造器私有
private HungryMan(){
}
//一开始就创建对象
private final static HungryMan HUNGRY_MAN=new HungryMan();
//使用getInstace返回这个对象
public static HungryMan getInstance(){
return HUNGRY_MAN;
}
}
懒汉式单例
01:普通懒汉式
package org.song.Man;
public class LazyMan01 {
//单线程ok,多线程不安全
private LazyMan01(){
}
private static LazyMan01 lazyMan01;
public static LazyMan01 getInstance(){
if(lazyMan01==null){
lazyMan01=new LazyMan01();
}
return lazyMan01;
}
}
02:双重监测懒汉式-DCL懒汉式
package org.song.Man;
public class LazyMan01 {
private LazyMan01() {
System.out.println(Thread.currentThread().getName()+" ok");
}
private volatile static LazyMan01 lazyMan01;
//双重监测
public static LazyMan01 getInstance() {
if(lazyMan01==null){
synchronized(LazyMan01.class){
if (lazyMan01 == null) {
lazyMan01 = new LazyMan01();
/*
* 实例化这个对象,它不是一个原子操作
* 1、分配内存空间
* 2、初始化对象
* 3、将对象指向内存空间
* 可能在执行顺序是132,这样的话就没有初始化对像,存在问题
* 所以需要在上面加上volatile关键字,禁止指令重排
*/
}
}
}
return lazyMan01;
}
//多线程不安全
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
new Thread(() -> {
getInstance();
}).start();
}
}
}
03:静态内部类
package org.song.Man;
public class Holder {
private Holder(){
}
public static Holder getInstance(){
return MyHolder.HOLDER;
}
public static class MyHolder{
private final static Holder HOLDER=new Holder();
}
}
04:通过反射破环单例模式
package org.song.Man;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class LazyMan01 {
private LazyMan01() {
System.out.println(Thread.currentThread().getName()+" ok");
}
private static LazyMan01 lazyMan01;
//双重监测
public static LazyMan01 getInstance() {
if(lazyMan01==null){
synchronized(LazyMan01.class){
if (lazyMan01 == null) {
lazyMan01 = new LazyMan01();
/*
* DCL懒汉式缺点:实例化这个对象,它不是一个原子操作
* 1、分配内存空间
* 2、初始化对象
* 3、将对象指向内存空间
* 可能在执行顺序是132,这样的话就没有初始化对像,存在问题
*/
}
}
}
return lazyMan01;
}
public static void main(String[] args) throws NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
//通过反射破环单例模式
Constructor<LazyMan01> declaredConstructor = LazyMan01.class.getDeclaredConstructor(null);
declaredConstructor.setAccessible(true);
LazyMan01 newInstance = declaredConstructor.newInstance(null);
LazyMan01 lazyMan01=LazyMan01.getInstance();
System.out.println(newInstance);
System.out.println(lazyMan01);
}
}
解决方法:它通过反射调取无参构造器,那么在无参构造器中再添加一个synchronized(LazyMan01.class),来锁住
package org.song.Man;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class LazyMan01 {
private LazyMan01() {
synchronized(LazyMan01.class){
throw new RuntimeException("不要试图通过反射破环单例模式");
}
//System.out.println(Thread.currentThread().getName()+" ok");
}
private static LazyMan01 lazyMan01;
//双重监测
public static LazyMan01 getInstance() {
if(lazyMan01==null){
synchronized(LazyMan01.class){
if (lazyMan01 == null) {
lazyMan01 = new LazyMan01();
/*
* DCL懒汉式缺点:实例化这个对象,它不是一个原子操作
* 1、分配内存空间
* 2、初始化对象
* 3、将对象指向内存空间
* 可能在执行顺序是132,这样的话就没有初始化对像,存在问题
*/
}
}
}
return lazyMan01;
}
public static void main(String[] args) throws NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
//通过反射破环单例模式
Constructor<LazyMan01> declaredConstructor = LazyMan01.class.getDeclaredConstructor(null);
declaredConstructor.setAccessible(true);
LazyMan01 newInstance = declaredConstructor.newInstance(null);
LazyMan01 lazyMan01=LazyMan01.getInstance();
System.out.println(newInstance);
System.out.println(lazyMan01);
}
}
05:如果是之前没有使用构造器构造类,直接使用两个反射构造?
解决方法:通过一个标志位判断
package org.song.Man;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class LazyMan01 {
private static boolean flag=false;
private LazyMan01() {
if(flag==false){
flag=true;
}else{
synchronized(LazyMan01.class){
throw new RuntimeException("不要试图通过反射破环单例模式");
}
}
//System.out.println(Thread.currentThread().getName()+" ok");
}
private static LazyMan01 lazyMan01;
//双重监测
public static LazyMan01 getInstance() {
if(lazyMan01==null){
synchronized(LazyMan01.class){
if (lazyMan01 == null) {
lazyMan01 = new LazyMan01();
/*
* DCL懒汉式缺点:实例化这个对象,它不是一个原子操作
* 1、分配内存空间
* 2、初始化对象
* 3、将对象指向内存空间
* 可能在执行顺序是132,这样的话就没有初始化对像,存在问题
*/
}
}
}
return lazyMan01;
}
public static void main(String[] args) throws NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
//通过反射破环单例模式
Constructor<LazyMan01> declaredConstructor = LazyMan01.class.getDeclaredConstructor(null);
declaredConstructor.setAccessible(true);
LazyMan01 newInstance = declaredConstructor.newInstance(null);
//LazyMan01 lazyMan01=LazyMan01.getInstance();
LazyMan01 newInstance2 = declaredConstructor.newInstance(null);
System.out.println(newInstance);
System.out.println(newInstance2);
}
}
06:通过是反射获取属性,一样可以破环单例模式
07:反射不能破环枚举的单例模式
package org.song.Man;
public enum Emun {
//反射不能破环枚举的单例模式
INSTANCE;
public Emun getInstance(){
return INSTANCE;
}
}
枚举的最终反编译构造器有两个参数(构造器)string.class和int.class
深入理解CAS
AtomicInteger:int类型的原子类
CAS:compareAndSet
package org.song.atomic;
import java.util.concurrent.atomic.AtomicInteger;
public class CAS {
public static void main(String[] args) {
AtomicInteger num=new AtomicInteger(11);
//期望并更新
//public final boolean compareAndSet(int expect, int update)
//unsafe类是java通过c++操作内存的后面
num.compareAndSet(11, 20);
System.out.println(num.get());
//原子底层并发原语,效率很高
num.compareAndSet(20, 21);
System.out.println(num.get());
}
}
比较当前工作内存的值和主内存的值,如果这个值是期望的,那么执行操作,如果不是就一直循环(自旋锁)
缺点:
- 循环会耗时
- 一次只能保证一个共享变量的原子性
- ABA问题
CAS:ABA问题(狸猫换太子)
一个线程操作一个主内存的数据后,再将这个数据修改回去,但另一个线程不知道这个数据被操作过:这就是ABA问题;
package org.song.atomic;
import java.util.concurrent.atomic.AtomicInteger;
public class CAS {
public static void main(String[] args) {
AtomicInteger num=new AtomicInteger(11);
//捣蛋线程
num.compareAndSet(11, 20);
System.out.println(num.get());
num.compareAndSet(20, 11);
System.out.println(num.get());
//另一个线程
num.compareAndSet(11, 21);
System.out.println(num.get());
}
}
解决方法:原子引用
原子引用
AtomicStampedReference
AtomicStampedReference atomicStampedReference = new AtomicStampedReference<>(10, 1);
atomicStampedReference.getStamp()
- 线程等待,是为了让获取stemp一致
- Integer的引用的坑,它有一个对象缓存机制,其范围再-128-127,在这个区间外,将会生成新的应用对象,比较时建议使用equals
package org.song.atomic;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicStampedReference;
public class CAS {
public static void main(String[] args) {
AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(10, 1);
new Thread(() -> {
try {
TimeUnit.SECONDS.sleep(2);
System.out.println(atomicStampedReference.getStamp());
atomicStampedReference.compareAndSet(10, 20, atomicStampedReference.getStamp(), atomicStampedReference.getStamp()+1);
System.out.println(atomicStampedReference.getStamp());
atomicStampedReference.compareAndSet(20, 10, atomicStampedReference.getStamp(), atomicStampedReference.getStamp()+1);
System.out.println(atomicStampedReference.getStamp());
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}).start();
new Thread(() -> {
try {
TimeUnit.SECONDS.sleep(2);
int stemp=atomicStampedReference.getStamp();
System.out.println(stemp);
System.out.println(atomicStampedReference.compareAndSet(10, 20, stemp, stemp+1));
System.out.println(atomicStampedReference.getStamp());
System.out.println(atomicStampedReference.compareAndSet(20, 10, stemp, stemp+1));
System.out.println(atomicStampedReference.getStamp());
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}).start();
}
}
可重入锁(递归锁)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xY02ZYP4-1630498026450)(C:\Users\宋杰的电脑\AppData\Roaming\Typora\typora-user-images\1630481757807.png)]
- 拿到最外面的锁,自然就拿到里面的锁
- 锁必须配对
- synchronized和lock都是可重入锁
package org.song.atomic;
public class MyLock01 {
public static void main(String[] args) {
Phone phone = new Phone();
new Thread(()->{
phone.sms();
}).start();
new Thread(()->{
phone.phone();
}).start();
}
}
class Phone{
public synchronized void sms(){
System.out.println(Thread.currentThread().getName()+"sms");
phone();
}
public synchronized void phone(){
System.out.println(Thread.currentThread().getName()+"phone");
}
}
package org.song.atomic;
import java.util.concurrent.locks.ReentrantLock;
public class MyLock01 {
public static void main(String[] args) {
Phone phone = new Phone();
new Thread(() -> {
phone.sms();
}).start();
new Thread(() -> {
phone.phone();
}).start();
}
}
class Phone {
ReentrantLock reentrantLock = new ReentrantLock();
public void sms() {
try {
reentrantLock.lock();
reentrantLock.lock();
System.out.println(Thread.currentThread().getName() + "sms");
phone();
} catch (Exception e) {
// TODO: handle exception
} finally {
reentrantLock.unlock();
reentrantLock.unlock();
}
}
public void phone() {
try {
reentrantLock.lock();
System.out.println(Thread.currentThread().getName() + "phone");
} catch (Exception e) {
// TODO: handle exception
} finally {
reentrantLock.unlock();
}
}
}
自旋锁
- 基于CAS实现的自旋锁
- 会一直等待
package org.song.atomic;
import java.util.concurrent.atomic.AtomicReference;
public class SpinLock {
//原子引用的类型时对象时,默认为null
AtomicReference<Thread> atomicReference=new AtomicReference<Thread>();
public void MyLock(){
System.out.println(Thread.currentThread().getName()+" lock");
while(!atomicReference.compareAndSet(null, Thread.currentThread())){
}
}
public void MyUnLock(){
System.out.println(Thread.currentThread().getName()+" unlock");
atomicReference.compareAndSet(Thread.currentThread(),null);
}
}
package org.song.atomic;
import java.util.concurrent.TimeUnit;
public class Test01 {
public static void main(String[] args) {
SpinLock lock=new SpinLock();
new Thread(()->{
lock.MyLock();
try {
TimeUnit.SECONDS.sleep(5);
} catch (Exception e) {
// TODO: handle exception
}finally {
lock.MyUnLock();
}
},"T1").start();
new Thread(()->{
lock.MyLock();
try {
} catch (Exception e) {
// TODO: handle exception
}finally {
lock.MyUnLock();
}
},"T2").start();
}
}
死锁
package org.song.atomic;
import java.util.concurrent.TimeUnit;
public class MyDeadLock {
public static void main(String[] args) {
String lockA="xxx";
String lockB="yyy";
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()+"lockA "+lockA+" get "+lockB);
try {
TimeUnit.SECONDS.sleep(6);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
synchronized (lockB) {
System.out.println(Thread.currentThread().getName()+"lockB "+lockB+" get "+lockA);
}
}
}
}
排查问题
- 看日志
- 查看堆栈信息
- jps -l 查看到想看的进程号
- jstack 进程号