players
1. What is JUC?
JUC: Refers to the three concurrent programming toolkits of java.util
- java.util.concurrent
- java.util.concurrent.atomic
- java.util.concurrent.locks
Four ways to achieve multithreading:
- Inherit the Thread class
- Implement the Runnable interface
- Implement the Callable interface
- Thread Pool
Business: common thread code Thread
Runnable has no return value, and its efficiency is relatively low compared to Callable!
Difference between Runnable interface and Callable interface:
- Whether there is a return value: Runnable has no return value, and Callable has a return value
- Whether to throw an exception: the call method calculates a result, and if it cannot do so, an exception is thrown
- The implementation method names are different, the Runnable interface is the run method, and the Callable interface is the call method
2. Threads and processes
Process: refers to an application program that is running in the system; once the program is running, it is a process; process - the smallest unit of resource allocation.
Thread: The basic unit that the system allocates processor time resources, or a unit execution flow that is independently executed within a process. Thread - The smallest unit of program execution.
Process: It is a program, a process contains multiple threads, and contains at least one thread.
Java has two threads by default: main and GC.
Can Java open threads? The start method starts the thread
public synchronized void start() {
/**
* This method is not invoked for the main method thread or "system"
* group threads created/set up by the VM. Any new functionality added
* to this method in the future may have to also be added to the VM.
*
* A zero status value corresponds to state "NEW".
*/
if (threadStatus != 0)
throw new IllegalThreadStateException();
/* Notify the group that this thread is about to be started
* so that it can be added to the group's list of threads
* and the group's unstarted count can be decremented. */
group.add(this);
boolean started = false;
try {
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
/* do nothing. If start0 threw a Throwable then
it will be passed up the call stack */
}
}
}
//本地方法,底层C++,Java无法操作硬件,由操作系统决定是否创建线程,是否立即创建线程
private native void start0();
Java cannot start threads . The underlying method is to call start0(), which is a native method written by the underlying C++ method. Java cannot directly manipulate hardware.
Several states of the thread
Thread.State
public enum State {
NEW,//新建
RUNNABLE,//准备就绪
BLOCKED, //阻塞
WAITING,//一直等待
TIMED_WAITING,//超时等待,过时不候
TERMINATED;//终止
}
wait/sleep difference
1. From a different class
wait => Object, any object instance can call
sleep => Thread, the static method of Thread
2. About the release of the lock
wait will release the lock; sleep will not release the lock, and it does not need to occupy the lock
3. The scope of use and exception capture are different
wait: must be used in a synchronous code block, no exceptions need to be caught
sleep: can be used anywhere , exceptions must be caught
concurrent, parallel
Concurrent programming: concurrent, parallel
Concurrency: Multiple threads access the same resource at the same time (multi-threaded shared resources)
For example: grabbing tickets for Spring Festival travel, e-commerce flash sales
Parallelism: Multiple tasks are performed together and aggregated later
For example: make instant noodles, while the electric kettle is boiling water, disassemble the instant noodles seasoning and pour it into the bucket
System.out.println(Runtime.getRuntime().availableProcessors());//获取cpu的核数
The essence of concurrent programming: make full use of CPU resources
Monitor
Monitor monitor (that is, the usual lock)
It is a synchronization mechanism that ensures that only one thread accesses the protected data or code at the same time
Jvm synchronization is based on entry and exit, implemented using monitor objects
User threads and daemon threads
User thread: custom thread (new Thread())
daemon thread: a special thread in the background, such as garbage collection
The main thread is over, the user thread is still running, and the jvm survives
There are no user threads anymore, they are all daemon threads, and jvm ends
3. Lock lock (emphasis)
Multithreaded programming steps:
- Create a resource class, create attributes and operation methods in the resource class
- Create multiple threads and call the operation method of the resource class
Case: Three conductors sell 30 tickets at the same time.
traditional synchronized
public class SaleTicketDemo1 {
public static void main(String[] args) {
//并发:多个线程操作同一个资源类,把资源类丢入线程
Ticket ticket = new Ticket();
//Runnable接口 -》 函数式接口,lambda表达式:函数式接口的实例
new Thread(() -> {
for (int i = 0; i < 30; i++) {
ticket.sale();
}
},"A").start();
new Thread(() -> {
for (int i = 0; i < 30; i++) {
ticket.sale();
}
},"B").start();
new Thread(() -> {
for (int i = 0; i < 30; i++) {
ticket.sale();
}
},"C").start();
}
}
/**
* 资源类
*/
class Ticket {
//属性
private int num = 30;
//synchronized 本质:线程串行化,排队,锁
public synchronized void sale() {
if (num > 0) {
System.out.println(Thread.currentThread().getName() + "卖出了第" + (num--) + "张票,剩余" + num + "张");
}
}
}
Lock interface
Implementation class
Fair lock: very fair, first come, first served (sunshine, relatively low efficiency)
Unfair lock: very unfair, you can jump in the queue (by default, it may cause thread starvation, but the efficiency is high)
public class SaleTicketDemo2 {
public static void main(String[] args) {
//并发:多个线程操作同一个资源类,把资源类丢入线程
Ticket2 ticket = new Ticket2();
new Thread(() -> {
for (int i = 0; i < 40; i++) ticket.sale();},"A").start();
new Thread(() -> {
for (int i = 0; i < 40; i++) ticket.sale();},"B").start();
new Thread(() -> {
for (int i = 0; i < 40; i++) ticket.sale();},"C").start();
}
}
/**
* 资源类
*
* Lock三部曲
* 1.new ReentrantLock();
* 2.lock.lock(); //加锁
* 3.finally -》 lock.unlock();//解锁
*
*/
class Ticket2 {
//属性
private int num = 30;
// 创建可重入锁
Lock lock = new ReentrantLock();
public void sale() {
lock.lock(); //加锁
try {
//业务代码
if (num > 0) {
System.out.println(Thread.currentThread().getName() + "卖出了第" + (num--) + "张票,剩余" + num + "张");
}
} finally {
lock.unlock();//解锁
}
}
}
The difference between synchronized and Lock
1. Synchronized is a built-in Java keyword, and Lock is an interface of Java
2. Synchronized cannot determine the status of the lock acquisition, but Lock can determine whether the lock has been acquired
3. Synchronized will automatically release the lock, Lock must release the lock manually! If the lock is not released, it will cause a deadlock
4. Once the synchronized thread is blocked while acquiring the lock, the second thread can only wait foolishly; the Lock will not necessarily wait
5. Synchronized reentrant lock, non-interruptible, unfair; Lock, reentrant lock, can judge lock, unfair/fair (can be set by yourself, default unfair lock)
6. synchronized is suitable for locking a small amount of synchronization code; Lock is suitable for locking a large amount of synchronization code!
7. Lock can improve the efficiency of multiple threads for read operations.
在性能上来说,如果竞争资源不激烈,两者性能是差不多的,而当竞争资源非常激烈时(即大量线程同时竞争),此时Lock的性能要远远优于synchronized
4. Communication between threads
Producer and Consumer Problems
Interview: singleton mode, sorting algorithm, producer consumer problem, deadlock
Resource class operation steps: Judgment Waiting-"Executing Business-"Wake Up Notification
Producer and consumer problem synchronized version
Object类中方法
/**
* 线程之间通信问题:生产者和消费者问题! 等待唤醒,通知唤醒
* 线程之间交替执行 A B 操作同一个变量 num = 0
* A num+1
* B num-1
*/
public class ProducerAndConsumer {
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();
}
}
/**
* 资源类
*
* 判断等待,业务,通知
*/
class Data {
private int num = 0;
//+1
public synchronized void increment() throws InterruptedException {
if (num != 0) {
//等待
this.wait();
}
num++;
System.out.println(Thread.currentThread().getName() + "=>" + num);
//通知其他线程我+1 完毕了
this.notifyAll();
}
//-1
public synchronized void decrement() throws InterruptedException {
if (num == 0) {
//等待
this.wait();
}
num--;
System.out.println(Thread.currentThread().getName() + "=>" + num);
//通知其他线程我-1 完毕了
this.notifyAll();
}
}
The problem exists, ABCD four threads spurious wake-up
public class ProducerAndConsumer {
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();
}
}
/**
* 资源类
* <p>
* 判断等待,业务,通知
*/
class Data {
private int num = 0;
/**
* +1
*/
public synchronized void increment() throws InterruptedException {
if (num != 0) {
//等待
this.wait();
}
num++;
System.out.println(Thread.currentThread().getName() + "=>" + num);
//通知其他线程我+1 完毕了
this.notifyAll();
}
/**
* -1
*/
public synchronized void decrement() throws InterruptedException {
if (num == 0) {
//等待
this.wait();
}
num--;
System.out.println(Thread.currentThread().getName() + "=>" + num);
//通知其他线程我-1 完毕了
this.notifyAll();
}
}
为什么会出现虚假唤醒问题呢?
Mainly because the wait method wakes up from where it waits (and wait will not hold the lock, other threads can get the lock directly), if will only judge once, as long as the condition is met for the first time, wait for wakeup The following +1/-1 operations will be executed directly, so there will be >1 or <0 numbers or even deadlocks, which are caused by false wakeups.
Prevent false wake-up problems: change if to while
/**
* 线程之间通信问题:生产者和消费者问题! 等待唤醒,通知唤醒
* 线程之间交替执行 A B 操作同一个变量 num = 0
* A num+1
* B num-1
*/
public class ProducerAndConsumer {
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 num = 0;
//+1
public synchronized void increment() throws InterruptedException {
while (num != 0) {
//等待
this.wait();
}
num++;
System.out.println(Thread.currentThread().getName() + "=>" + num);
//通知其他线程我+1 完毕了
this.notifyAll();
}
//-1
public synchronized void decrement() throws InterruptedException {
while (num == 0) {
//等待
this.wait();
}
num--;
System.out.println(Thread.currentThread().getName() + "=>" + num);
//通知其他线程我-1 完毕了
this.notifyAll();
}
}
The JUC version of the producer and consumer problem
Find Condition by Lock (official document)
Code
public class ProducerAndConsumer2 {
public static void main(String[] args) {
Data2 data = new Data2();
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 Data2 {
private int num = 0;
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
//condition.await(); // 等待
//condition.signalAll(); // 唤醒全部
//+1
public void increment() throws InterruptedException {
lock.lock();
try {
//业务代码
while (num != 0) {
//等待
condition.await();
}
num++;
System.out.println(Thread.currentThread().getName() + "=>" + num);
//通知其他线程我+1 完毕了
condition.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
//-1
public void decrement() throws InterruptedException {
lock.lock();
try {
while (num == 0) {
//等待
condition.await();
}
num--;
System.out.println(Thread.currentThread().getName() + "=>" + num);
//通知其他线程我-1 完毕了
condition.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
Complete steps of multithreaded programming
Step 1: Create a resource class, create attributes and operation methods in the resource class
Step 2: Resource class operation method
(1) Judgment waiting
(2) Business logic
(3) Notification wake-up
Step 3: Create multiple threads and call the operation method of the resource class
Step Four: Prevent False Wakeups
线程间定制通信
Threads execute in agreed order
Any new technology definitely does not just cover the original technology, it has its advantages and supplements!
Condition precise notification and wake-up thread
Code implementation: three threads execute sequentially
/**
* A B C三个线程顺序执行
*/
public class C {
public static void main(String[] args) {
Resource resource = new Resource();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
resource.printA();
}
}, "A").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
resource.printB();
}
}, "B").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
resource.printC();
}
}, "C").start();
}
}
/**
* 资源类 Lock
*/
class Resource {
private final Lock lock = new ReentrantLock();
private final Condition condition1 = lock.newCondition();
private final Condition condition2 = lock.newCondition();
private final Condition condition3 = lock.newCondition();
private int num = 1; // 1A 2B 3C
public void printA() {
lock.lock();
try {
//业务 判断 -》 执行 -》 通知
while (num != 1) {
//等待
condition1.await();
}
num = 2;
System.out.println(Thread.currentThread().getName() + "=>AAA");
//唤醒,唤醒指定线程B
condition2.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void printB() {
lock.lock();
try {
//业务 判断 -》 执行 -》 通知
while (num != 2) {
condition2.await();
}
System.out.println(Thread.currentThread().getName() + "=>BBB");
num = 3;
//唤醒线程C
condition3.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void printC() {
lock.lock();
try {
//业务 判断 -》 执行 -》 通知
while (num != 3) {
condition3.await();
}
System.out.println(Thread.currentThread().getName() + "=>CCC");
num = 1;
//唤醒线程A
condition1.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
5. Eight-lock phenomenon
What is a lock? How to tell who the lock is?
object of any class, class object
Deep understanding of locks
* 八锁,就是关于锁的八个问题
* 1、标准情况下,两个线程先打印 发短信 还是 打电话? 1/发短信 2/打电话
* 2、sendSms()延迟4s情况下,两个线程先打印 发短信 还是 打电话? 1/发短信 2/打电话
*/
public class Test1 {
public static void main(String[] args) {
Phone phone = new Phone();
//锁的存在
new Thread(() -> {
phone.sendSms();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
},"A").start();
new Thread(() -> {
phone.call();
},"B").start();
}
}
class Phone{
//synchronized 锁的对象是方法的调用者!
//两个方法用的是同一把锁,谁先拿到谁执行!
public synchronized void sendSms(){
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发短信");
}
public synchronized void call(){
System.out.println("打电话");
}
}
/**
* 3、增加了一个普通方法后,先打印发短信还是hello? hello
* 4、两个对象,两个同步方法 两个线程先打印 发短信 还是 打电话? 1/打电话 2/发短信
*/
public class Test2{
public static void main(String[] args) {
//两个对象,两个不同的对象,两把锁!
Phone2 phone1 = new Phone2();
Phone2 phone2 = new Phone2();
//锁的存在
new Thread(() -> {
phone1.sendSms();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
},"A").start();
new Thread(() -> {
phone2.call();
},"B").start();
}
}
class Phone2{
//synchronized 锁的对象是方法的调用者!
//两个方法用的是同一把锁,谁先拿到谁执行!
public synchronized void sendSms(){
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发短信");
}
public synchronized void call(){
System.out.println("打电话");
}
//没有锁,不是同步方法,不受锁的影响
public void hello(){
System.out.println("hello");
}
}
/**
* 5.增加两个静态的同步方法 一个对象 两个线程先打印 发短信 还是 打电话? 1/发短信 2/打电话
* 6.两个对象 两个静态的同步方法 两个线程先打印 发短信 还是 打电话? 1/发短信 2/打电话
*/
public class Test3 {
public static void main(String[] args) {
//两个不同的对象 但是类只会加载一次 共用一把锁
Phone3 phone1 = new Phone3();
Phone3 phone2 = new Phone3();
//锁的存在
new Thread(() -> {
phone1.sendSms();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
},"A").start();
new Thread(() -> {
phone2.call();
},"B").start();
}
}
class Phone3{
//static静态方法 锁的是类对象 Phone3.class
public static synchronized void sendSms(){
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发短信");
}
public static synchronized void call(){
System.out.println("打电话");
}
}
/**
* 7.一个静态同步方法一个普通同步方法 一个对象 两个线程先打印 发短信 还是 打电话? 打电话 发短信
* 8.一个静态同步方法一个普通同步方法 两个对象 两个线程先打印 发短信 还是 打电话? 打电话 发短信
*/
public class Test4 {
public static void main(String[] args) {
Phone4 phone1 = new Phone4();
Phone4 phone2 = new Phone4();
//锁的存在
new Thread(() -> {
phone1.sendSms();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
},"A").start();
new Thread(() -> {
phone2.call();
},"B").start();
}
}
class Phone4{
//static静态同步方法 锁的是类对象 Phone3.class
public static synchronized void sendSms(){
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发短信");
}
//普通同步方法 锁的是方法调用者
public synchronized void call(){
System.out.println("打电话");
}
}
summary:
- In the synchronization method, whoever gets the lock first executes first, and the same lock is executed sequentially.
- The ordinary synchronization method locks the current object of this (that is, the caller of the method).
- The static synchronization method locks the class object, and the class will only be loaded once, so the static synchronization method always locks the class object (XXX.class).
The basis of synchronized implementation of synchronization: every object in Java can be used as a lock.
It is manifested in the following three forms.
- For normal synchronized methods, the lock is the current instance object.
- For static synchronized methods, the lock is the Class object of the current class.
- For a synchronized method block, the lock is the object configured in the synchronized brackets.
6. The collection class is not safe
List is not safe
//java.util.ConcurrentModificationException 并发修改异常!
public class ListTest {
public static void main(String[] args) {
//并发下 ArrayList不安全
/*
解决方案
1、List<String> list = new Vector<>();
2、List<String> list = Collections.synchronizedList(new ArrayList<>());
3、List<String> list = new CopyOnWriteArrayList<>();
*/
//CopyOnWrite 写入时复制 COW 计算机程序设计领域的一种优化策略;
//多个线程操作的时候 list 读取的时候 固定的 写入(覆盖)
//再写入的时候避免覆盖,造成数据问题!
//读写分离
//CopyOnWriteArrayList 比 Vector 好在哪里?
//Vector 底层是synchronized实现效率较低 ; CopyOnWriteArrayList 底层是ReentrantLock实现 效率更高 灵活性也更高
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 is not safe
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CopyOnWriteArraySet;
//java.util.ConcurrentModificationException
public class SetTest {
public static void main(String[] args) {
//HashSet<String> set = new HashSet<>();
//解决方案一:
//Set<String> set = Collections.synchronizedSet(new HashSet<>());
//解决方案二:
Set<String> set = new CopyOnWriteArraySet<>();
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();
}
}
}
What is the bottom layer of HashSet?
The bottom layer of HashSet is HashMap
public HashSet() {
map = new HashMap<>();
}
// add() set 本质就是map 中的key, key是不可重复的!
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
// PRESENT 不变的值!
private static final Object PRESENT = new Object();
Map is not safe
Review the bottom layer of HashMap
public class MapTest {
public static void main(String[] args) {
/*
并发下 HashMap线程不安全
解决方案:
1.Map<String, Object> map = Collections.synchronizedMap(new HashMap<>());
2.Map<String, Object> map = new ConcurrentHashMap<>();
*/
//HashMap<String, Object> map = new HashMap<>();
//Map<String, Object> map = Collections.synchronizedMap(new HashMap<>());
Map<String, Object> 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();
}
}
}
7、Callable
1. Can have a return value
2. Can throw an exception
3. Different methods, run() -> call()
code testing
public class CallableTest {
public static void main(String[] args) throws ExecutionException, InterruptedException {
new Thread(new MyThread()).start();
/*
本身Callable接口和Runnable接口毫无关系
通过一个Runnable接口的实现类FutureTask,Callable接口与Runnable接口构建了关系,便可以启动线程
*/
//适配类 FutureTask 是 Runnable接口的实现类 构造器 FutureTask(Callable<V> callable)
MyThread1 t1 = new MyThread1();
FutureTask<Integer> futureTask = new FutureTask<>(t1); //泛型是线程返回值类型
/*
启动两个线程,只会打印一个`call()...`
*/
new Thread(futureTask,"A").start(); //怎么启动Callable
new Thread(futureTask,"B").start(); //结果会被缓存,效率高
Integer i = futureTask.get(); //获取线程返回值 get()可能会产生阻塞!把他放到最后 或者 使用异步通信来处理!
System.out.println(i);
}
}
class MyThread implements Runnable{
@Override
public void run() {
}
}
/**
* 泛型是返回值类型
*/
class MyThread1 implements Callable<Integer>{
@Override
public Integer call(){
System.out.println("call()...");
//耗时的操作
return 1024;
}
}
Notice:
1. There is a cache
2. You may need to wait to get the result, it will be blocked!
8. Commonly used auxiliary classes (must)
8.1 CountDownLatch (subtraction counter)
/**
* 计数器
*/
public class CountDownLatchDemo {
public static void main(String[] args) throws InterruptedException {
//倒计时 起始为6 必须要执行任务的时候,再使用!
CountDownLatch countDownLatch = new CountDownLatch(6);
for (int i = 0; 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 Door!");
}
}
principle:
countDownLatch.countDown () ; // count - 1
countDownLatch.await () ; // wait for the counter to return to 0, and then execute downward
Every time a thread calls countDown() the number -1, when the counter becomes 0, countDownLatch.await() will be woken up and continue to execute!
8.2 CyclicBarrier (adding counter)
public class CyclicBarrierDemo {
public static void main(String[] args) {
/*
集齐7颗龙珠召唤神龙
*/
//召唤神龙的线程
CyclicBarrier barrier = 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 {
barrier.await(); //等待
} catch (Exception e) {
e.printStackTrace();
}
}).start();
}
}
}
8.3 Semaphore (semaphore)
Grab a parking spot!
6 cars - 3 parking spaces
public class SemaphoreDemo {
public static void main(String[] args) {
//线程数量:停车位! 限流!
Semaphore semaphore = new Semaphore(3);
for (int i = 1; i <= 6; i++) {
new Thread(() -> {
try {
//acquire() 获得
semaphore.acquire();
System.out.println(Thread.currentThread().getName() + "抢到车位");
TimeUnit.SECONDS.sleep(3);
System.out.println(Thread.currentThread().getName() + "离开车位");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
semaphore.release(); //release() 释放
}
},String.valueOf(i)).start();
}
}
}
principle:
semaphore.acquire () ; //Acquire, if it is full, wait until it is released!
semaphore.release () ; // release, will release the current semaphore +1, and then wake up the waiting thread!
Role: Mutually exclusive use of multiple shared resources! Concurrent current limiting, control the maximum number of threads!
9. Read-write lock
Write lock: exclusive lock
Read lock: shared lock
Read-write lock: A resource can be accessed by multiple read threads, or by a write thread, but read and write threads cannot exist at the same time, read and write are mutually exclusive, and read and read are shared
ReadWriteLock
public class ReadWriteLockDemo {
public static void main(String[] args) {
MyCache myCache = new MyCache();
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(() -> {
System.out.println("获取" + temp + "缓存数据-> " + 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);
try {
TimeUnit.MILLISECONDS.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
map.put(key, value);
System.out.println(Thread.currentThread().getName() + "写入OK");
}
/**
* 取 读
*/
public Object get(String key) {
System.out.println(Thread.currentThread().getName() + "读取" + key);
Object o = map.get(key);
System.out.println(Thread.currentThread().getName() + "读取OK");
return o;
}
}
operation result:
Result analysis: Since it is a multi-threaded operation, multiple threads are started at the same time. It may take some time for the writing thread to write data. At this time, the data is not written yet, and the reading thread starts to read the data, resulting in no data being read. This situation requires locking control.
Add read-write lock:
/**
* 独占锁(写锁) 一次只能被一个线程占有
* 共享锁(读锁) 多个线程可以同时占有
*
* ReadWriteLock
* 读-读 可以共存!
* 读-写 不可共存!
* 写-写 不可共存!
*/
public class ReadWriteLockDemo {
public static void main(String[] args) {
MyCache myCache = new MyCache();
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(() -> {
System.out.println("获取" + temp + "缓存数据-> " + myCache.get(temp + ""));
}, String.valueOf(i)).start();
}
}
}
/**
* 自定义缓存
*/
class MyCache {
private volatile Map<String, Object> map = new HashMap<>();
private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
/**
* 存 写
*/
public void put(String key, Object value) {
readWriteLock.writeLock().lock();
System.out.println(Thread.currentThread().getName() + "写入" + key);
try {
TimeUnit.MILLISECONDS.sleep(200);
map.put(key, value);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
readWriteLock.writeLock().unlock();
}
System.out.println(Thread.currentThread().getName() + "写入OK");
}
/**
* 取 读
*/
public Object get(String key) {
readWriteLock.readLock().lock();
Object o = null;
try {
System.out.println(Thread.currentThread().getName() + "读取" + key);
o = map.get(key);
System.out.println(Thread.currentThread().getName() + "读取OK");
} catch (Exception e) {
e.printStackTrace();
} finally {
readWriteLock.readLock().unlock();
}
return o;
}
}
At this point the result is correct
Result analysis: Due to the addition of a read-write lock, the write lock is an exclusive lock, and the read lock is a shared lock. Therefore, no operation is allowed during the writing process. After the writing operation is completed, multiple threads can share the reading.
Lock downgrade: Write locks will be downgraded to read locks, and read locks cannot be upgraded to write locks.
10. Blocking queue
BlockingQueue |
---|
![]() |
Under what circumstances will we use blocking queues: multi-threaded concurrent processing, thread pool!
Learn to use queues
add, remove
Four APIs
Way | Throw an exception | does not throw an exception | blocking wait | timeout wait |
---|---|---|---|---|
Add to | add() | offer() | put() | offer(E e, long timeout, TimeUnit unit) |
remove | remove() | poll() | take() | poll(long timeout, TimeUnit unit) |
Get the first element of the queue | element() | peek() | - | - |
/**
* 抛出异常
*/
public static void test1() {
//队列的大小
ArrayBlockingQueue<Object> arrayBlockingQueue = new ArrayBlockingQueue<>(3);
System.out.println(arrayBlockingQueue.add("a"));
System.out.println(arrayBlockingQueue.add("b"));
System.out.println(arrayBlockingQueue.add("c"));
//java.lang.IllegalStateException: Queue full 抛出异常
//System.out.println(arrayBlockingQueue.add("d"));
System.out.println("**************");
System.out.println(arrayBlockingQueue.remove());
System.out.println(arrayBlockingQueue.remove());
System.out.println(arrayBlockingQueue.remove());
//java.util.NoSuchElementException 抛出异常
//System.out.println(arrayBlockingQueue.remove());
}
/**
*有返回值,不抛出异常
*/
public static void test2(){
ArrayBlockingQueue<Object> arrayBlockingQueue = new ArrayBlockingQueue<>(3);
System.out.println(arrayBlockingQueue.offer("a"));
System.out.println(arrayBlockingQueue.offer("b"));
System.out.println(arrayBlockingQueue.offer("c"));
//System.out.println(arrayBlockingQueue.offer("d")); // false 不抛出异常!
System.out.println("*****");
System.out.println(arrayBlockingQueue.poll());
System.out.println(arrayBlockingQueue.poll());
System.out.println(arrayBlockingQueue.poll());
System.out.println(arrayBlockingQueue.poll()); // null 不抛出异常!
}
/**
*等待,阻塞(一直阻塞)
*/
public static void test3() throws InterruptedException {
ArrayBlockingQueue<Object> arrayBlockingQueue = new ArrayBlockingQueue<>(3);
//一直阻塞
arrayBlockingQueue.put("a");
arrayBlockingQueue.put("b");
arrayBlockingQueue.put("c");
//arrayBlockingQueue.put("d"); //队列没有位置了,一直阻塞
System.out.println(arrayBlockingQueue.take());
System.out.println(arrayBlockingQueue.take());
System.out.println(arrayBlockingQueue.take());
System.out.println(arrayBlockingQueue.take()); //没有数据了,一直阻塞
}
/**
*等待,阻塞(等待超时)
*/
public static void test4() throws InterruptedException {
ArrayBlockingQueue<Object> arrayBlockingQueue = new ArrayBlockingQueue<>(3);
arrayBlockingQueue.offer("a");
arrayBlockingQueue.offer("b");
arrayBlockingQueue.offer("c");
arrayBlockingQueue.offer("d",2, TimeUnit.SECONDS); //等待两秒,超时退出
System.out.println("******");
System.out.println(arrayBlockingQueue.poll());
System.out.println(arrayBlockingQueue.poll());
System.out.println(arrayBlockingQueue.poll());
arrayBlockingQueue.poll(2, TimeUnit.SECONDS); //等待两秒,超时退出
}
SynchronousQueue synchronous queue
After entering an element, you must wait to take it out before you can put another element in it!
put()、take()
/**
* 同步队列
* SynchronousQueue 和 BlockingQueue 不一样,SynchronousQueue 不存储元素
* put一个元素,必须从里面take取出来,否则不能再put进去值!(存一个,取一个,循环)
*/
public class SynchronousQueueDemo {
public static void main(String[] args) {
//存一个元素,取一个元素 循环
SynchronousQueue<Object> 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() + "->take " + synchronousQueue.take());
TimeUnit.SECONDS.sleep(3);
System.out.println(Thread.currentThread().getName() + "->take " + synchronousQueue.take());
TimeUnit.SECONDS.sleep(3);
System.out.println(Thread.currentThread().getName() + "->take " + synchronousQueue.take());
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "T2").start();
}
}
11. Thread pool (emphasis)
Thread pool: three methods, seven parameters, and four rejection strategies
pooling technology
The program runs, the essence: occupying system resources! Optimize the use of resources! => pooling technology
Thread pool, connection pool, memory pool, object pool...
Creating and destroying is a waste of resources.
Pooling technology : Prepare some resources in advance. If someone wants to use them, they can come to me to get them, and return them to me after use, so as to improve efficiency.
Benefits of thread pool
1. Reduce resource consumption;
2. Improve the speed of response;
3. Convenient thread management.
Thread multiplexing, can control the maximum number of concurrency, manage threads
Thread pool: three methods
/**
* Executors 工具类:创建线程池 3大方法
*/
public class Demo1 {
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 < 10; i++) {
//使用了线程池之后,用线程池来创建线程
threadPool.execute(() -> {
System.out.println(Thread.currentThread().getName() + " OK");
});
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//线程池用完,程序结束,关闭线程池
threadPool.shutdown();
}
}
}
Source code analysis: seven parameters
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,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
//本质:ThreadPoolExecutor()
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;
}
Manually create a thread pool
/**
* Executors 工具类:创建线程池 3大方法
*
* 4大拒绝策略:
* new ThreadPoolExecutor.AbortPolicy() //默认拒绝策略 银行满了,还有人进来,不处理这个人,抛出异常
* new ThreadPoolExecutor.CallerRunsPolicy() //哪来的去哪里!
* new ThreadPoolExecutor.DiscardPolicy() //队列满了,丢掉任务,不会抛出异常
* new ThreadPoolExecutor.DiscardOldestPolicy() //队列满了,尝试和最早的竞争,也不会抛出异常!
*
*/
public class Demo2 {
public static void main(String[] args) {
//工具类创建
//ExecutorService threadPool = Executors.newSingleThreadExecutor();//创建单个线程的线程池
//ExecutorService threadPool = Executors.newFixedThreadPool(5);//创建固定线程的线程池
//ExecutorService threadPool = Executors.newCachedThreadPool();//创建可伸缩线程池
//手动创建线程池 ThreadPoolExecutor
ExecutorService threadPool = new ThreadPoolExecutor(
2,
5,
3,
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(3),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.DiscardOldestPolicy()); //队列满了,尝试和最早的竞争,也不会抛出异常!
try {
//最大承载:Queue + max
//超出最大承载抛出RejectedExecutionException 异常 (默认拒绝策略)
for (int i = 0; i < 9; i++) {
//使用了线程池之后,用线程池来创建线程
threadPool.execute(() -> {
System.out.println(Thread.currentThread().getName() + " OK");
});
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//线程池用完,程序结束,关闭线程池
threadPool.shutdown();
}
}
}
Four Rejection Strategies
* 4大拒绝策略:
* new ThreadPoolExecutor.AbortPolicy() //默认拒绝策略 银行满了,还有人进来,不处理这个人,抛出异常
* new ThreadPoolExecutor.CallerRunsPolicy() //哪来的去哪里!
* new ThreadPoolExecutor.DiscardPolicy() //队列满了,丢掉任务,不会抛出异常
* new ThreadPoolExecutor.DiscardOldestPolicy() //队列满了,尝试和最早的竞争,也不会抛出异常!
Summary and Extension
How to set the maximum number of threads in the thread pool?
Understand: IO-intensive, CPU-intensive (tuning)
public class Demo1 {
public static void main(String[] args) {
//手动创建线程池 ThreadPoolExecutor
//最大线程到底如何定义?
//1、CPU 密集型 电脑处理器数是几,就是几,可以保证CPU的效率最高!
//2、IO 密集型 大于 程序中十分耗IO的线程数 ---> 程序中 15个大型任务 io十分占用资源! =》 30
//获取CPU核数 电脑处理器数
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()); //队列满了,尝试和最早的竞争,也不会抛出异常!
try {
//最大承载:Queue + max
//超出最大承载 RejectedExecutionException (默认拒绝策略)
for (int i = 0; i < 9; i++) {
//使用了线程池之后,用线程池来创建线程
threadPool.execute(() -> {
System.out.println(Thread.currentThread().getName() + " OK");
});
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//线程池用完,程序结束,关闭线程池
threadPool.shutdown();
}
}
}
12. Four major functional interfaces (must be mastered)
Programmers in the new era: lambda expression (essentially an instance of functional interface), chain programming, functional interface, Stream streaming computing
Functional interface: an interface with only one abstract method
//此注解用来判断该接口是否是函数式接口
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
//简化编程模型,在新版本的框架底层大量应用!
Function functional interface |
---|
![]() |
code testing
/**
* Function 函数型接口 有一个输入,有一个输出
* 只要是 函数式接口 可以用 lambda表达式简化
*/
public class Demo01 {
public static void main(String[] args) {
//输出输入的值
// Function<String ,String > fun = new Function<String ,String >() {
// @Override
// public String apply(String str) {
// return str;
// }
// };
Function<String ,String > fun = (str) -> str; //lambda表达式
System.out.println(fun.apply("123"));
}
}
Predicate assertion interface |
---|
![]() |
code testing
/**
* Predicate 断定型接口 有一个输入值 返回值是布尔值!
*/
public class Demo02 {
public static void main(String[] args) {
//判断字符串是否为空
// Predicate<String> predicate = new Predicate<String>() {
// @Override
// public boolean test(String str) {
// return "".equals(str);
// }
// };
Predicate<String> predicate = str -> "".equals(str);
System.out.println(predicate.test(""));
System.out.println(predicate.test("123"));
}
}
Consumer consumer interface |
---|
![]() |
code testing
/**
* Consumer 消费型接口 只接收参数,不返回值
*/
public class Demo03 {
public static void main(String[] args) {
//接收参数,将其打印出来
// Consumer<String> consumer = new Consumer<String>() {
// @Override
// public void accept(String str) {
// System.out.println(str);
// }
// };
Consumer<String> consumer = str -> System.out.println(str);
consumer.accept("hello");
}
}
Supplier supply interface |
---|
![]() |
code testing
/**
* Supplier 供给型接口 不需参数,有返回值
*/
public class Demo04 {
public static void main(String[] args) {
// Supplier<String> supplier = new Supplier<String>() {
// @Override
// public String get() {
// return "world";
// }
// };
Supplier<String> supplier = () -> "world";
System.out.println(supplier.get());
}
}
13. Stream computing
What is Stream computing
Big Data: Storage + Compute
Storage: Collections, MySQL
Calculation: streaming computing~
public class Test {
public static void main(String[] args) {
User user1 = new User(1, "a", 21);
User user2 = new User(2, "b", 22);
User user3 = new User(3, "c", 23);
User user4 = new User(4, "d", 24);
User user5 = new User(5, "e", 25);
User user6 = new User(6, "f", 26);
//存储交给集合
List<User> list = Arrays.asList(user1, user2, user3, user4, user5, user6);
//计算交给Stream流
//lambda表达式、链式编程、函数式接口、Stream流式计算
list.stream()
.filter(user -> user.getId() % 2 == 0) //找出id为偶数的用户
.filter(user -> user.getAge() > 23) //年龄大于23岁
.map(user -> user.getName().toUpperCase()) //用户名 转为大写
.sorted((u1, u2) -> -u1.compareTo(u2)) //用户名字母到着排序
.limit(1) //只输出一个用户
.forEach(System.out::println);
}
}
14、ForkJoin
What is ForkJoin
ForkJoin in JDK1.7, execute tasks in parallel! Improve efficiency ~. In the large amount of data rate will be faster!
In big data: the core idea of MapReduce -> split big tasks into small tasks!
ForkJoin Features: Job Stealing!
The realization principle is: double-ended queue ! From above and below, you can go to get the task and execute it! (It maintains double-ended queues)
Use of Class ForkJoinPool
ForkJoinTask
Calculation class of ForkJoin:
/**
* 求和计算的任务
*
* 如何使用 ForkJoin?
* 1.ForkJoinPool 通过它来执行
* 2.计算任务 forkJoinPool.execute(ForkJoinTask<?> task)
* 3.计算类要继承ForkJoinTask
*/
public class ForkJoinDemo extends RecursiveTask<Long> {
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 {
//分支合并计算
Long middle = (start + end) / 2;//中间值
ForkJoinDemo task1 = new ForkJoinDemo(start, middle);
task1.fork(); //拆分任务,把线程任务压入线程队列
ForkJoinDemo task2 = new ForkJoinDemo(middle, end);
task2.fork(); //拆分任务,把线程任务压入线程队列
//结果汇总
return task1.join() + task2.join();
}
}
}
Test class:
/**
* 三六九等 三 六(ForkJoin) 九(Stream并行流)
*/
public class Test {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//test1(); // 419
//test2();
test3();//234
}
//普通程序员
public static void test1() {
long sum = 0L;
long start = System.currentTimeMillis();
for (long i = 0L; 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 ForkJoinDemo(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));
}
public static void test3() {
long start = System.currentTimeMillis();
//Stream并行流计算 []
long sum = LongStream.rangeClosed(0L, 10_0000_0000L).parallel().reduce(0, Long::sum);
long end = System.currentTimeMillis();
System.out.println("sum = " + sum + " 时间:" + (end - start));
}
}
.parallel().reduce(0, Long::sum) Use a parallel stream to calculate, improving efficiency. (Parallel Computing Reduction Sum)
15. Asynchronous callback
The original intention of Future design: to model the result of an event in the future!
Thread asynchronous calls usually use the CompletableFuture class
(1) runAsync asynchronous callback with no return value
//没有返回值的 runAsync 异步回调
CompletableFuture<Void> completableFuture = CompletableFuture.runAsync(()->{
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " runAsync => Void");
});
System.out.println("11111");
completableFuture.get(); //阻塞,获取执行结果
(2) supplyAsync asynchronous callback with return value
/**
* 类似异步调用:Ajax
*
* 异步调用:CompletableFuture
* 成功回调
* 失败回调
*/
public class Demo02 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//有返回值的 supplyAsync 异步回调
//成功和失败回调
//返回的是错误信息
CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(() -> {
System.out.println(Thread.currentThread().getName() + " supplyAsync => Integer");
int i = 10 / 0;
return 1024;
});
//成功回调
System.out.println(completableFuture.whenComplete((t, u) -> {
System.out.println("t=>" + t); //正常的返回结果
System.out.println("u=>" + u); //错误信息 java.util.concurrent.CompletionException: java.lang.ArithmeticException: / by zero
}).exceptionally((e -> {
//失败回调
System.out.println(e.getMessage());
return 233; // 可以获取到错误的返回结果
})).get());
}
}
16、JMM
Please talk about your understanding of volatile
volatile is a lightweight synchronization mechanism provided by the Java virtual machine
1. Ensure visibility
2. Atomicity is not guaranteed
3. Prohibition of command rearrangement
What is JMM
JMM: Java memory model, things that don't exist, concepts! Promise!
Some synchronization conventions about JMM:
Threads are divided into working memory and main memory
1. Before the thread is unlocked, the shared variable must be flushed back to the main memory immediately ;
2. Before the thread is locked, the latest value in the main memory must be read into the working memory;
3. Locking and unlocking are the same lock
8 operations
- read (read) : acts on the main memory variable, it transfers the value of a variable from the main memory to the working memory of the thread, so that the subsequent load action can be used;
- load (load) : Acts on the variable of the working memory, it puts the read operation from the variable in the main memory into the working memory;
- use (use) : acts on the variables in the working memory, it transfers the variables in the working memory to the execution engine, whenever the virtual machine encounters a value that needs to use the variable, it will use this instruction;
- Assign (assignment ): acts on variables in the working memory, it puts a value received from the execution engine into the variable copy of the working memory;
- Store (storage) : acts on variables in the main memory, it transfers the value of a variable from the working memory to the main memory for subsequent write use;
- write (write) : acts on the variable in the main memory, it puts the value of the variable obtained from the working memory by the store operation into the variable in the main memory;
- lock (lock) : acts on the variables of the main memory, and marks a variable as a thread-exclusive state;
- unlock (unlock) : A variable that acts on the main memory, it releases a variable that is in a locked state, and the released variable can be locked by other threads;
JMM has given corresponding regulations on these 8 operations:
-
One of read and load, store and write operations is not allowed to occur alone. That is, if you use read, you must load, and if you use store, you must write
-
The thread is not allowed to discard its latest assign operation, that is, after the data of the working variable has changed, the main memory must be notified
-
A thread is not allowed to synchronize data without assignment from working memory back to main memory
-
A new variable must be created in main memory, working memory is not allowed to directly use an uninitialized variable. That is, before implementing use and store operations on variables, they must go through assign and load operations
-
Only one thread can lock a variable at a time. After multiple locks, the same number of unlocks must be executed to unlock
-
If the lock operation is performed on a variable, the value of this variable in all working memory will be cleared. Before the execution engine uses this variable, the value of the initialized variable must be re-loaded or assigned.
-
If a variable is not locked, it cannot be unlocked. Nor can you unlock a variable that is locked by another thread
-
Before unlocking a variable, the variable must be synchronized back to main memory
Problem: Program A does not know that the value in main memory has changed
17、volatile
1) Guaranteed visibility
public class JMMDemo {
// 如果不加volatile 程序会死循环
// 加了volatile是可以保证可见性的,volatile保证一旦数据被修改,其它线程立马能够感知到
private volatile static int num = 0;
public static void main(String[] args) { // main 线程
new Thread(()->{ // 线程1 不知道主内存中的值发生了变化
while (num == 0){
}
}).start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
num = 1;
System.out.println(num);
}
}
2) Atomicity is not guaranteed
Atomicity: indivisible
When thread A is performing tasks, it cannot be disturbed or divided. It either succeeds at the same time or fails at the same time.
/**
* 不保证原子性
*/
public class VDemo2 {
// volatile 不保证原子性
private volatile static int num = 0;
public static void add() {
num++;
}
public static void main(String[] args) {
// 20个线程,每个线程调用100次 理论值 2万
for (int i = 0; i < 20; i++) {
new Thread(() -> {
for (int j = 0; j < 1000; j++) {
add();
}
}).start();
}
while (Thread.activeCount() > 2) {
// main gc
Thread.yield();
}
System.out.println(Thread.currentThread().getName() + " " + num);
}
}
How to ensure atomicity if lock and synchronized are not added?
Use atomic classes to solve atomicity problems
public class VDemo2 {
// volatile 不保证原子性
// 原子类的 Integer
private volatile static AtomicInteger num = new AtomicInteger();
public static void add() {
//num++; //不是原子性操作
num.getAndIncrement(); // +1 操作 底层是CAS保证的原子性
}
public static void main(String[] args) {
// 20个线程,每个线程调用100次 理论值 2万
for (int i = 0; i < 20; i++) {
new Thread(() -> {
for (int j = 0; j < 1000; j++) {
add();
}
}).start();
}
while (Thread.activeCount() > 2) {
// main gc
Thread.yield();
}
System.out.println(Thread.currentThread().getName() + " " + num);
}
}
The bottom layer of the atomic class is directly linked to the operating system! Modify the value in memory!
Unsafe class is a very special existence
3) Prohibition of command rearrangement
command rearrangement
What is instruction rearrangement?
The programs we write, the computer does not execute as we write .
Source code –> compiler optimization rearrangement –> instruction parallelism may also be rearranged –> memory system will also be rearranged –> execution
When the processor performs instruction rearrangement, it will consider the dependencies between data!
int x=1; //1
int y=2; //2
x=x+5; //3
y=x*x; //4
//我们期望的执行顺序是 1234 可能执行的顺序会变成2134 1324
//可不可能是 4123? 不可能的
Possible impact results: Premise: The four values of abxy are all 0 by default
thread A | thread B |
---|---|
x=a | y=b |
b=1 | a=2 |
Normal result: x = 0; y =0;
thread A | thread B |
---|---|
b=1 | a=2 |
x=a | y=b |
Possible weird results: x = 2; y = 1; (very low probability)
volatile can avoid instruction rearrangement:
A memory barrier will be added to volatile, and this memory barrier can guarantee the order of instructions in this barrier.
Memory barriers: CPU instructions. effect:
1. Guarantee the execution order of specific operations;
2. The memory visibility of certain variables can be guaranteed (using these features, the visibility of volatile implementations can be guaranteed)
Summarize:
volatile can guarantee visibility; atomicity cannot be guaranteed; due to memory barriers, it can be guaranteed to avoid instruction rearrangement!
Interviewer: So do you know where this memory barrier is used the most? singleton pattern
18. Completely play with the singleton mode
Hungry style, DCL lazy style
1) Hungry Chinese style
/**
* 饿汉式单例
* 核心思想:构造器私有化
*/
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 byte[] data4 = new byte[1024*1024];
private static final Hungry HUNGRY = new Hungry();
private Hungry(){
}
public static Hungry getInstance(){
return HUNGRY;
}
}
2) DCL lazy style
/**
* 懒汉式单例
* 道高一尺,魔高一丈!
*/
public class LazyMan {
private volatile static LazyMan lazyMan;
private static boolean flag = false;
private LazyMan() {
synchronized (LazyMan.class) {
if (!flag) {
flag = true;
} else {
throw new RuntimeException("不要试图使用反射破坏异常!");
}
}
}
//双重检测锁模式的 懒汉式单例 DCL懒汉式
public static LazyMan getInstance() {
if (null == lazyMan) {
synchronized (LazyMan.class) {
if (null == lazyMan) {
lazyMan = new LazyMan(); // 不是一个原子性操作
}
}
}
return lazyMan;
}
//不加 synchronized 多线程情况下,不一定是单例
public static void main(String[] args) throws Exception {
// for (int i = 0; i < 10; i++) {
// new Thread(() -> {
// LazyMan.getInstance();
// }).start();
// }
//反射!
//LazyMan instance = LazyMan.getInstance();
Field flag = LazyMan.class.getDeclaredField("flag");
flag.setAccessible(true);
Constructor<LazyMan> constructor = LazyMan.class.getDeclaredConstructor(null);
constructor.setAccessible(true);
LazyMan instance = constructor.newInstance();
flag.set(instance,false);
LazyMan instance1 = constructor.newInstance();
System.out.println(instance == instance1);
}
/*
创建对象的步骤:
1.分配内存空间
2.执行构造方法,初始化对象
3.把这个对象指向这个空间
123
132 线程A
线程B // 此时lazyMan还没有完成构造
*/
}
3) Static inner class
/**
* 静态内部类
*/
public class Holder {
private Holder(){
}
public static class InnerClass{
private static final Holder HOLDER = new Holder();
}
public static Holder getInstance(){
return InnerClass.HOLDER;
}
}
Singletons are not safe because of reflection
4) Enumeration class
/**
* enum 是什么? 本身也是一个Class类
*/
public enum EnumSingle {
INSTANCE;
public EnumSingle getInstance(){
return INSTANCE;
}
}
class Test{
public static void main(String[] args) throws Exception {
EnumSingle instance1 = EnumSingle.INSTANCE;
//EnumSingle instance2 = EnumSingle.INSTANCE;
Constructor<EnumSingle> constructor = EnumSingle.class.getDeclaredConstructor(String.class,int.class);
constructor.setAccessible(true);
EnumSingle instance2 = constructor.newInstance(); //java.lang.NoSuchMethodException: com.lkl.singleton.EnumSingle.
System.out.println(instance1);
System.out.println(instance2);
}
}
Using enumerations, we can prevent reflection from breaking
The final decompiled source code of the enumeration type:
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
});
}
}
19. In-depth understanding of CAS
What is CAS
Big factories must study the bottom layer! Breakthrough! Self-cultivation, weak foundation, ground-shaking operating system, computer network principles
public class CASDemo {
//CAS compareAndSet:比较并交换!
public static void main(String[] args) {
AtomicInteger atomicInteger = new AtomicInteger(2021);
//期望、更新
//public final boolean compareAndSet(int expect, int update)
//如果我期望的值达到了,就更新,否则,就不更新,CAS 是CPU的并发原语!
System.out.println(atomicInteger.compareAndSet(2021, 2022));
System.out.println(atomicInteger.get());
System.out.println(atomicInteger.compareAndSet(2021, 2022));
System.out.println(atomicInteger.get());
}
}
Unsafe class
atomicInteger.getAndIncrement(); |
---|
![]() |
CAS: Compare the value currently in working memory with the value in main memory, and if the value is expected, perform the operation! If not, it keeps looping, using a spin lock.
shortcoming:
- Loops take time;
- The atomicity of only one shared variable can be guaranteed at a time;
- it will have ABA problem
CAS: ABA (civet cat for prince)
A=1 in main memory
Thread 1: The expected value is 1, and it will become 2;
Thread 2: Two operations:
- Expected value is 1, becomes 3
- Expected to be 3, becomes 1
So for thread 1, the value of A is still 1, so there is a problem, and thread 1 is fooled; thread 1 does not know that the value of A has been modified!
public class CASDemo2 {
//CAS compareAndSet:比较并交换!
public static void main(String[] args) {
AtomicInteger atomicInteger = new AtomicInteger(2021);
//期望、更新
//public final boolean compareAndSet(int expect, int update)
//如果我期望的值达到了,就更新,否则,就不更新,CAS 是CPU的并发原语!
// ======================= 捣乱的线程 ==============================
System.out.println(atomicInteger.compareAndSet(2021, 2022));
System.out.println(atomicInteger.get());
System.out.println(atomicInteger.compareAndSet(2022, 2021));
System.out.println(atomicInteger.get());
// ======================= 捣乱的线程 ==============================
//======================== 期望的线程 ==============================
System.out.println(atomicInteger.compareAndSet(2021, 6666));
System.out.println(atomicInteger.get());
}
}
20. Atomic Reference (AtomicReference)
Solve the ABA problem and introduce atomic references! Corresponding thought: Optimistic lock !
Atomic operations with version numbers!
public class CASDemo3 {
//CAS compareAndSet:比较并交换!
public static void main(String[] args) {
//AtomicStampedReference 泛型如果使用包装类,注意对象引用问题
//正常在业务操作,这里泛型都是一个个对象
AtomicStampedReference<Integer> stampedReference = new AtomicStampedReference<>(1, 1);
new Thread(() -> {
int stamp = stampedReference.getStamp(); //获得版本号
System.out.println(Thread.currentThread().getName() + " 1 -> " + stamp);
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(stampedReference.compareAndSet(1, 2,
stampedReference.getStamp(), stampedReference.getStamp() + 1));
System.out.println(Thread.currentThread().getName() + " 2 -> " + stampedReference.getStamp());
System.out.println(stampedReference.compareAndSet(2, 1,
stampedReference.getStamp(), stampedReference.getStamp() + 1));
System.out.println(Thread.currentThread().getName() + " 3 -> " + stampedReference.getStamp());
}, "a").start();
new Thread(() -> {
int stamp = stampedReference.getStamp(); //获得版本号
System.out.println(Thread.currentThread().getName() + " 1 -> " + stamp);
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(stampedReference.compareAndSet(1, 6,
stampedReference.getStamp(), stampedReference.getStamp() + 1));
System.out.println(Thread.currentThread().getName() + " 2 -> " + stampedReference.getStamp());
}, "b").start();
try {
TimeUnit.SECONDS.sleep(3);
System.out.println("**************");
System.out.println(stampedReference.getReference());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Notice:
Integer uses the object cache mechanism, the default range is -128~127, it is recommended to use the static factory method valueOf to obtain object instances instead of new, because valueOf uses cache, and new will definitely create new objects and allocate new memory space
integer wrapper |
---|
![]() |
21. Understanding of various locks
1. Fair lock and unfair lock
Fair lock: very fair, no queue jumping, first come, first served! (possibly less efficient)
Unfair lock: very unfair, you can jump in the queue (the default is unfair, and the efficiency is higher)
public ReentrantLock() {
sync = new NonfairSync();
}
Constructor with parameters, which can modify the fair state
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
2. Reentrant lock
Reentrant lock (recursive lock): After getting the outer lock, it will automatically get the inner lock (synchronized [implicit] and Lock [explicit] are both reentrant locks)
synchronized version
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 version
public class Demo02 {
public static void main(String[] args) {
Phone2 phone = new Phone2();
new Thread(()->{
phone.sms();
},"A").start();
new Thread(()->{
phone.sms();
},"B").start();
}
}
class Phone2{
private final Lock lock = new ReentrantLock();
public void sms(){
lock.lock(); //Lock锁必须配对,有加锁就必须有解锁! 否则就会死锁!
try {
System.out.println(Thread.currentThread().getName() + " sms");
call();
} finally {
lock.unlock();
}
}
public void call(){
lock.lock();
try {
System.out.println(Thread.currentThread().getName() + " call");
} finally {
lock.unlock();
}
}
}
3. Spin lock
spinlock
Custom spin lock :
public class spinlock {
// int 0
// 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);
}
}
Test a custom spinlock:
public class TestSpinLock {
public static void main(String[] args) {
// Lock lock = new ReentrantLock();
// lock.lock();
// lock.unlock();
// 底层使用的自旋锁CAS
spinlock spinlock = new spinlock();
new Thread(() -> {
spinlock.myLock();
try {
TimeUnit.SECONDS.sleep(4);
} catch (Exception e) {
e.printStackTrace();
} finally {
spinlock.myUnLock();
}
}, "T1").start();
new Thread(() -> {
spinlock.myLock();
try {
TimeUnit.SECONDS.sleep(3);
} catch (Exception e) {
e.printStackTrace();
} finally {
spinlock.myUnLock();
}
}, "T2").start();
}
}
output result |
---|
![]() |
Result analysis:
Two threads jointly operate a lock, and whoever gets the lock first executes first. The T1 thread first gets the lock and locks it, followed by the T2 thread, which first inputs the first line and then outputs the second line; the T1 thread releases the lock after 4s, and then the T2 thread gets the lock and locks it for operation, and releases the lock after 3s. Therefore: input the first and second lines first, output the third line after 4s, and output the fourth line after 3s.
4. Deadlock
What is deadlock? During the execution process, two or more processes wait for each other due to competition for resources. If there is no external interference, they cannot continue to execute.
Deadlock occurs when two threads try to acquire each other's lock while holding their own lock.
Deadlock test, how to eliminate deadlock!
/**
* 死锁样例
*/
public class DeadLockDemo {
static Object a = new Object();
static Object b = new Object();
public static void main(String[] args) {
new Thread(() -> {
synchronized (a) {
System.out.println(Thread.currentThread().getName() + " 获取到锁a,试图获取锁b");
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (b) {
System.out.println("获取到锁b");
}
}
}, "A").start();
new Thread(() -> {
synchronized (b) {
System.out.println(Thread.currentThread().getName() + " 获取到锁b,试图获取锁a");
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (a) {
System.out.println("获取到锁a");
}
}
}, "B").start();
}
}
Causes of deadlock:
First: Insufficient system resources
Second: The progress sequence of the process running is not appropriate
Third: Misallocation of resources
How to locate the deadlock and solve the problem?
1. Use jps to locate the process number, under the bin directory of jdk: there is a jps
Order:jps -l
2. Use jstack
the process process number to find the deadlock information (jstack is a stack tracking tool that comes with jvm)
Order:jstack 进程号
Interview, working! How to troubleshoot?
1. Log 90%
2. Stack information 10%