JUC 并发编程

作者:pox21s

概述

在Java中,线程部分是一个重点,本篇文章说的是关于线程并发编程。JUC就是java.util .concurrent工具包的简称。这是一个处理线程的工具包,从JDK 1.5开始出现。

1.基本概念

1.1 进程和线程

1.1.1 定义

进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个基本单位

线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器、java虚拟机栈、本地方法栈等),但是同属一个进程的线程之间可以共享这个进程的所有资源。(只是进程含有的,线程自己的不能互相共享)

1.1.2 关系

一个线程可以创建和撤销另一个线程;同一个进程中的多个线程之间可以并发执行。

相对进程而言,线程是一个更加接近于执行体的概念,它可以与同进程中的其他线程共享数据,但拥有自己的栈空间,拥有独立的执行序列。

1.1.3 区别

进程和线程的主要差别在于它们是不同的操作系统资源管理方式。进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程只是一个进程中的不同执行路径。线程有自己的栈和局部变量,但线程之间没有单独的地址空间,一个线程死掉就等于整个进程死掉,所以多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差一些。

但对于一些要求同时进行并且又要共享某些变量的并发操作,只能用线程,不能用进程。

  1. 简而言之,一个程序至少有一个进程,一个进程至少有一个线程

  2. 线程的划分尺度小于进程,使得多线程程序的并发性高

  3. 另外,进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序的运行效率

  4. 线程在执行过程中与进程还是有区别的。每个独立的进程有一个程序运行的入口、顺序执行序列和程序的出口。**但是线程不能够独立执行,**必须依存在应用程序中,由应用程序提供多个线程执行控制

  5. 从逻辑角度来看,多线程的意义在于一个应用程序中,有多个执行部分可以同时执行。但操作系统并没有将多个线程看做多个独立的应用,来实现进程的调度和管理以及资源分配。这就是进程和线程的重要区别

1.1.4 线程和进程在使用上各有优缺点

线程执行开销小,但不利于资源的管理和保护;

而进程正相反。同时,线程适合于在SMP机器上运行,而进程则可以跨机器迁移。

1.2 线程的状态

  1. 初始(NEW):新创建了一个线程对象,但还没有调用start()方法。

  2. 运行(RUNNABLE)

    • Java线程中将就绪(ready)和运行中(running)两种状态笼统的称为“运行”。

      线程对象创建后,其他线程(比如main线程)调用了该线程的start()方法。该状态的线程位于可运行线程池中,等待被线程调度选中,获取CPU的使用权,此时处于就绪状态(ready)。

      就绪状态的线程在获得CPU时间片后变为运行中状态(running)。

    • 就绪状态(RUNNABLE之READY)

      • 就绪状态只是说你资格运行,调度程序没有挑选到你,你就永远是就绪状态。

      • 调用线程的start()方法,此线程进入就绪状态。

      • 当前线程sleep()方法结束,其他线程join()结束,等待用户输入完毕,某个线程拿到对象锁,其他线程也将进入就绪状态。

        join()方法:

        ​ 当前线程调用其他线程的join方法,当前线程进行阻塞状态,等待被调用线程执行完毕以后,结束阻塞状态,等待CPU时间片分配,当前线程才能继续执行。

        t.join()/t.join(long millis),当前线程里调用其它线程t的join方法,当前线程进入TIME_WAITING状态,当前线程不释放已经持有的对象锁。线程t执行完毕或者millis时间到,当前线程进入就绪状态。

      • 当前线程时间片用完了,调用当前线程的yield()方法,当前线程进入就绪状态。

        yield 即 “谦让”,也是 Thread 类的方法。它让掉当前线程 CPU 的时间片,使正在运行中的线程重新变成就绪状态,并重新竞争 CPU 的调度权。它可能会获取到,也有可能被其他线程获取到。

      • 锁池里的线程拿到对象锁后,进入就绪状态。

        所有等待获取锁的线程都会进入锁池,进入阻塞状态。

    • 线程调度程序从可运行池中选择一个线程作为当前线程时,线程进入运行态。这也是线程进入运行状态的唯一的一种方式。

  3. 阻塞(BLOCKED)

    表示线程阻塞于锁。

  4. 等待(WAITING)

    处于这种状态的线程不会被分配CPU执行时间,它们要等待被显式地唤醒,否则会处于无限期等待的状态。

  5. 超时等待(TIMED_WAITING)

    处于这种状态的线程不会被分配CPU执行时间,不过无须无限期等待被其他线程显示地唤醒,在达到一定时间后它们会自动唤醒。

    sleep()方法就会使线程进入到超时等待状态,并且不会释放机锁。

  6. 终止(TERMINATED)

    • 当线程的run()方法完成时,或者主线程的main()方法完成时,我们就认为它终止了。这个线程对象也许是活的,但是它已经不是一个单独执行的线程。线程一旦终止了,就不能复生。

    • 在一个终止的线程上调用start()方法,会抛出java.lang.IllegalThreadStateException异常。

      线程的start()方法只能被调用一次。

这6种状态定义在Thread类的State枚举中,可查看源码进行一一对应。

1.2.1 wait和sleep

sleep()

sleep() 方法是线程类(Thread)的静态方法,让调用线程进入睡眠状态,让出执行机会给其他线程,等到休眠时间结束后,线程进入就绪状态和其他线程一起竞争cpu的执行时间。

因为sleep() 是static静态的方法(在synchronized前面加上static则说明锁的是这个类,单synchronized锁的是实例对象,sleep底层是通过本地方法实现),他不能改变对象的机锁,当一个synchronized块中调用了sleep() 方法,线程虽然进入休眠,但是对象的机锁没有被释放,其他线程依然无法访问这个对象

wait()

wait()是Object类的方法,当一个线程执行到wait方法时,它就进入到一个和该对象相关的等待池进入休眠,同时释放对象的机锁,使得其他线程能够访问这个对象,可以通过notify,notifyAll方法来唤醒等待的线程

notify,notifyAll方法并不会让当前线程进入休眠还会唤醒其他线程

1.2.1.1 区别

sleep会休眠但是会把锁握在手里,其他线程调用相同的对象,要等待锁,这时,锁还在sleep手中,其他线程则只有等待醒来并执行完方法后释放锁,才能执行方法。

但wait会休眠但是不会将锁握在手里,其他线程调用相同对象时可以直接执行方法,不用等待当前休眠的进程醒来。

1.3 并发和并行

1.3.1 概述

并发是逻辑上的同时发生(simultaneous),而并行是物理上的同时发生。

1.3.2 并发(一同出发,一起争夺一个资源)

与可以一起出发的并发(concurrent)相对的是不可以一起出发的顺序(sequential):

顺序:上一个开始执行的任务完成后,当前任务才能开始执行
​ 并发:无论上一个开始执行的任务是否完成,当前任务都可以开始执行

(也就是说,A B 顺序执行的话,A 一定会比 B 先完成,而并发执行则不一定。)

1.3.3 并行(一同出行)

与可以一起执行的并行(parallel)相对的是不可以一起执行的串行(serial):

串行:有一个任务执行单元,从物理上就只能一个任务、一个任务地执行
​ 并行:有多个任务执行单元,从物理上就可以多个任务一起执行

(也就是说,在任意时间点上,串行执行时必然只有一个任务在执行,而并行则不一定。)

综上,并发与并行并不是互斥的概念,只是前者关注的是任务的抽象调度、后者关注的是任务的实际执行。而它们又是相关的,比如并行一定会允许并发。

1.4 管程

一种监视器,俗称为锁,是一种同步机制,保证同一个时间,只能有一个线程访问被保护的数据或代码(块)等。

JVM的同步基于进入和退出,进入持有锁,退出释放锁,这个是使用管程对象来实现的。

synchronized关键字和wait()、notify()、notifyAll()这三个方法是Java中实现管程技术的组成部分。

1.5 用户线程和守护线程

1.5.1 概述

用户线程:使用者自己创建出来的线程(new)

守护线程:守护线程 – 也称“服务线程”,在没有用户线程可服务时会自动离开,JVm创建,当JVM中没有用户线程时,JVM会自动关闭。

1.5.2 设置

通过setDaemon(true)可以将用户设置线程为“守护线程”;

1.5.3 区别

守护线程就是JVM中存在的线程,当没有用户线程可以服务的时候JVM就会结束守护线程。(JVM中只有守护线程时也会结束)。用户线程就是用户自己创建出来的线程,当有用户线程存在的时候,守护线程才能存在。

2. 创建线程的方式

Java使用Thread类代表线程,所有的线程对象都必须是Thread类或其子类的实例。

2.1 Java可以用四种方式来创建线程

  • 继承Thread类创建线程
  • 实现Runnable接口创建线程
  • 使用Callable和Future创建线程
  • 使用线程池例如用Executor框架

2.1.1 继承Thread类创建线程

通过在类名上继承Thread并重写其中的run()方法来实现,在创建实例的时候调用xxx.start()方法来启动线程

2.1.2 实现Runnable接口创建线程

实现Runnable接口并重写其中的run()方法,创建实例以后通过放入线程来创建线程

2.1.2.1 实例
public class Main {public static void main(String[] args){undefined// 创建并启动线程,这里的myThread已经实现了Runnable接口并重写了run()方法MyThread2 myThread=new MyThread2();Thread thread=new Thread(myThread);thread().start();// 或者new Thread(new MyThread2()).start();}
}

2.1.3 使用Callable和Future创建线程

实现Callable接口并重写其中的call()方法。和实现Runnable接口不同的是,Callable有返回值,并且需要配合FutureTask来接收返回值,Thread并不能直接接收Callable接口的实现类,只能接收FutureTask。

2.1.4 实现
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
//实现Callable接口
public class CallableTest {public static void main(String[] args) {//执行Callable 方式,需要FutureTask 实现实现,用于接收运算结果FutureTask<Integer> futureTask = new FutureTask<Integer>(new MyCallable());new Thread(futureTask).start();//接收线程运算后的结果try {Integer sum = futureTask.get();System.out.println(sum);} catch (InterruptedException e) {e.printStackTrace();} catch (ExecutionException e) {e.printStackTrace();}}
}class MyCallable implements Callable<Integer> {@Overridepublic Integer call() throws Exception {int sum = 0;for (int i = 0; i < 100; i++) {sum += i;}return sum;}
}

2.1.4 使用线程池例如用Executor框架

线程池提供了一个线程队列,队列中保存着所有等待状态的线程。避免了创建与销毁额外开销,提升了响应速度。实现了线程复用。

2.1.4.1 实现
Callable<Singleton4> callable = new Callable<Singleton4>() {@Overridepublic Singleton4 call() throws Exception {return Singleton4.getInstance();}};// 创建一个线程池,并设定大小ExecutorService threadPool = Executors.newFixedThreadPool(2);// 通过future来接收线程启动后返回的结果Future<Singleton4> f1 = threadPool.submit(callable);Future<Singleton4> f2 = threadPool.submit(callable);// 获取返回值Singleton4 s6 = f1.get();Singleton4 s7 = f1.get();System.out.println(s6 == s7);threadPool.shutdown();
}

3.多线程编程步骤

  1. 创建资源类,编写属性和操作方法

  2. 在资源类设置操作方法

    • 判断
    • 执行
    • 通知
  3. 创建多个线程,调用资源类的操作方法

  4. 防止虚假唤醒,将在资源类操作方法的判断条件设置在while中

    为防止虚假唤醒,不要使用if来进行线程状态的判定,而是通过while来进行判断,实时判断更新。

4. Synchronized

4.1 概述

Java自带的关键字,它最大的特征就是在同一时刻只有一个线程能够获得对象的监视器(monitor),从而进入到同步代码块或者同步方法之中,即表现为互斥性(排它性)。在发生异常的时候会自动释放锁。

由JVM来控制锁的开和关,用户无法控制。

4.2 实例

public class SaleTicket {public static void main(String[] args) {Ticket ticket = new Ticket();new Thread(() ->{for (int i = 0; i < 40; i++) {ticket.sale();}},"售票员1").start();new Thread(() ->{for (int i = 0; i < 40; i++) {ticket.sale();}},"售票员2").start();new Thread(() ->{for (int i = 0; i < 40; i++) {ticket.sale();}},"售票员3").start();}}class Ticket{private int num = 30;public synchronized void sale(){if (num > 0 ){// 获取当前执行的线程的名字System.out.println(Thread.currentThread().getName()+"售卖第"+(num--)+"票"+"还剩下"+num+"票");}}
}

5.LOCK

5.1 概述

Lock不是java中的关键字,通过实例化来获取,可以让用户自己手动的上锁和解锁,如果没有设定解锁方式,在发生异常时不能正常解锁,则会发生死锁现象。

5.2 实例

import java.util.concurrent.locks.ReentrantLock;public class SaleTicketByLockDemo {public static void main(String[] args) {SaleTicketByLock ticket = new SaleTicketByLock();new Thread(() ->{for (int i = 0; i < 40; i++) {ticket.sale();}},"售票员1").start();new Thread(() ->{for (int i = 0; i < 40; i++) {ticket.sale();}},"售票员2").start();new Thread(() ->{for (int i = 0; i < 40; i++) {ticket.sale();}},"售票员3").start();}}class SaleTicketByLock{// 实现LockReentrantLock lock =new ReentrantLock();private int num = 30;// 这里使用try finally 的方式来确保无论是否发生异常最后都能正确的关闭锁,以确保不会发生死锁的方式public synchronized void sale(){lock.lock();try {if (num > 0 ){System.out.println(Thread.currentThread().getName()+"售卖第"+(num--)+"票"+"还剩下"+num+"票");}} finally {lock.unlock();}}
}

6.Lock和Synchronized的区别

6.1 概述

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-blIZGmmo-1643884728905)(F:\StudyNotepad\img\image-20211101221416591.png)]

6.2 区别

  • 来源

    lock是一个接口,而synchronized是java的一个关键字,synchronized是内置的语言实现。

  • 异常是否释放锁

    synchronized在发生异常时候会自动释放占有的锁,因此不会出现死锁;而lock发生异常时候,不会主动释放占有的锁,必须手动unlock来释放锁,可能引起死锁的发生。(所以最好将同步代码块用try catch包起来,finally中写入unlock,避免死锁的发生。)

    原因:synchronized底层会释放两次锁,第一次释放为正常释放,第二个为当出现异常时的释放

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kzBKAAvZ-1643884728907)(F:\StudyNotepad\img\image-20211112132351072.png)]

  • 是否响应中断

    lock等待锁过程中可以用interrupt来中断等待,而synchronized只能等待锁的释放,不能响应中断;

  • 是否知道获取锁

    Lock可以通过trylock来知道有没有获取锁,而synchronized不能;

  • Lock可以提高多个线程进行读操作的效率

    readwritelock就是实现读线程共享。

  • 在性能上来说,如果竞争资源不激烈,两者的性能是差不多的,而当竞争资源非常激烈时(即有大量线程同时竞争),此时Lock的性能要远远优于synchronized。所以说,在具体使用时要根据适当情况选择

  • synchronized使用Object对象本身的wait 、notify、notifyAll调度机制,而Lock可以使用Condition进行线程之间的调度

  • lock可以通过condition来达到精确唤醒

    synchronized只能随机唤醒或者全部唤醒

6.3 volatile关键字

保证数据的可见性,能够保证一定的有序性,不能保证原子性,禁止指令重排

锁的强度:synchronized > lock > volatile ,推荐解决问题从底用到高

7.线程通信

7.1 用Synchronized实现

package syn;/*** @Author PoX21s* @Date: 2021/11/1 17:01* @Version 1.0*/public class ThreadDemo1 {public static void main(String[] args) {share share = new share();new Thread(() ->{for (int i = 0; i < 10; i++) {try {share.incr();} catch (InterruptedException e) {e.printStackTrace();}}},"线程一").start();new Thread(() ->{for (int i = 0; i < 10; i++) {try {share.decr();} catch (InterruptedException e) {e.printStackTrace();}}},"线程二").start();}
}class share{private int num = 0;public synchronized void incr() throws InterruptedException {if (num == 1){this.wait();}num++;System.out.println(Thread.currentThread().getName()+"::"+num);this.notifyAll();}public synchronized void decr() throws InterruptedException {if (num == 0){this.wait();}num--;System.out.println(Thread.currentThread().getName()+"::"+num);this.notifyAll();}
}

7.2 虚假唤醒问题

我们可以将上面的线程数增加,就会发现结果不会出现0 1 0 1 交替出现,这是因为增加了多个线程以后,可能会出现这种情况,线程一已经将值加1,然后出于等待状态,然后其他线程执行了减1 的操作,接着通知了所有的线程,这个时候,线程一则会继续执行下面的代码(因为wait()方法在哪里睡就在哪里醒的特性导致),这就会导致虚假唤醒的情况。

7.2.1 解决办法

将判断条件中的if改为while,在醒来以后继续判断。

7.3 用Lock实现

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;/*** @Author PoX21s* @Date: 2021/11/1 17:01* @Version 1.0*/public class ThreadDemo2 {public static void main(String[] args) {share1 share = new share1();new Thread(() ->{for (int i = 0; i < 10; i++) {try {share.incr();} catch (InterruptedException e) {e.printStackTrace();}}},"线程一").start();new Thread(() ->{for (int i = 0; i < 10; i++) {try {share.decr();} catch (InterruptedException e) {e.printStackTrace();}}},"线程二").start();new Thread(() ->{for (int i = 0; i < 10; i++) {try {share.incr();} catch (InterruptedException e) {e.printStackTrace();}}},"线程三").start();new Thread(() ->{for (int i = 0; i < 10; i++) {try {share.incr();} catch (InterruptedException e) {e.printStackTrace();}}},"线程四").start();}
}class share1 {private int num = 0;ReentrantLock lock = new ReentrantLock();// 通过lock中的condition实例来实现类似synchronized中的wait和notifyprivate Condition condition = lock.newCondition();public void incr() throws InterruptedException {lock.lock();try {while (num == 1){condition.await();}num++;System.out.println(Thread.currentThread().getName()+"::"+num);condition.signalAll();} finally {lock.unlock();}}public void decr() throws InterruptedException {lock.lock();try {while (num == 0){condition.await();}num--;System.out.println(Thread.currentThread().getName()+"::"+num);condition.signalAll();} finally {lock.unlock();}}
}

8.线程定制化通信

按照规定的方式执行,执行完以后,更改标志位,唤醒下一个线程。使用Lock实现。

8.1 实现

package syn;import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;/*** @Author PoX21s* @Date: 2021/11/1 17:01* @Version 1.0*/public class ThreadDemo3 {public static void main(String[] args) {share2 share2 = new share2();new Thread(() -> {for (int i = 0; i < 10; i++) {try {share2.print5();} catch (InterruptedException e) {e.printStackTrace();}}},"aa").start();new Thread(() -> {for (int i = 0; i < 10; i++) {try {share2.print10();} catch (InterruptedException e) {e.printStackTrace();}}},"bb").start();new Thread(() -> {for (int i = 0; i < 10; i++) {try {share2.print15();} catch (InterruptedException e) {e.printStackTrace();}}},"cc").start();}}class share2 {// 设置标志位private int flag = 1;private ReentrantLock lock = new ReentrantLock();// 一个Lock对象中可以创建多个Condition实例(即对象监视器)private Condition c1 = lock.newCondition();private Condition c2 = lock.newCondition();private Condition c3 = lock.newCondition();public void print5() throws InterruptedException {lock.lock();try {while (flag != 1){c1.await();}for (int i = 0; i < 5; i++) {System.out.println(Thread.currentThread().getName()+" :: "+"打印第"+i);}flag = 2;c2.signal();} finally {lock.unlock();}}public void print10() throws InterruptedException {lock.lock();try {while (flag != 2){c2.await();}for (int i = 0; i < 10; i++) {System.out.println(Thread.currentThread().getName()+" :: "+"打印第"+i);}flag = 3;c3.signal();} finally {lock.unlock();}}public void print15() throws InterruptedException {lock.lock();try {while (flag != 3){c3.await();}for (int i = 0; i < 15; i++) {System.out.println(Thread.currentThread().getName()+" :: "+"打印第"+i);}flag = 1;c1.signal();} finally {lock.unlock();}}
}

这里通过当一个线程运行以后,更改判断条件,并通知下一个线程来执行达到按规则执行的目的。

9.集合中的线程安全

在多个线程同时读取和写入(并发修改)的时候可能会出现线程并发安全问题,下面通过三种方式来解决,推荐使用第三种。

Exception in thread "24" java.util.ConcurrentModificationException

9.1 ArryayList中的线程安全

9.1.1 通过Vector解决

本质是在Vector的添加方法加入了synchronized关键字,来实现线程并发安全。已过时,不推荐。

9.1.1.1 实现
public class ThreadDemo4 {public static void main(String[] args) {
//        List<String> list = new ArrayList<>();// 通过改变创建list引用的对象来实现在添加时的线程安全List<String> list = new Vector<>();for (int i = 0; i < 30; i++) {new Thread(() -> {list.add(UUID.randomUUID().toString().substring(0,7));System.out.println(list);},String.valueOf(i)).start();}}
}

9.1.2 通过Collections中的synchronizedList方法解决

本质也是在add方法中增加了synchronized关键字。

9.1.2.1 实现
public class ThreadDemo4 {public static void main(String[] args) {// 本质通过在add方法上添加了线程同步关键字实现List<String> list = Collections.synchronizedList(new ArrayList<String>());for (int i = 0; i < 30; i++) {new Thread(() -> {list.add(UUID.randomUUID().toString().substring(0,7));System.out.println(list);},String.valueOf(i)).start();}}
}

9.1.3 通过CopyOnWriteArrayList<>()类来实现

写时复制,本质是在写入的时候线程将原本的数组进行一个复制,在复制上的数组进行写入操作,然后合并更新原数组,后面的线程就使用新的数组进行写入。

在源代码中也是通过lock来实现的。

其他集合也有相应的类,如:new copyonwritearrayset()等。推荐使用。

9.1.3.1 实现
public class ThreadDemo4 {public static void main(String[] args) {// 写时复制技术,先复制出来一份,当前写完以后合并更新到原数组,后面的线程使用新的数组进行操作List<String> list = new CopyOnWriteArrayList<>();for (int i = 0; i < 30; i++) {new Thread(() -> {list.add(UUID.randomUUID().toString().substring(0,7));System.out.println(list);},String.valueOf(i)).start();}}
}

9.2 HashSet中的线程安全

9.2.1 通过Collections.synchronizedSet()方法

通过synchronized关键字实现。

9.2.1.1 实现
public class ThreadDemo5 {public static void main(String[] args) {
//        HashSet<String> set = new HashSet<>();Set<String> set;set = Collections.synchronizedSet(new HashSet<String>());for (int i = 0; i < 30; i++) {new Thread(() -> {set.add(UUID.randomUUID().toString().substring(0,6));System.out.println(set);},String.valueOf(i)).start();}}
}

9.3 HashMap中的线程安全

9.3.1 通过ConcurrentHashMap<>()解决

添加synchronized关键字代码块,实现线程同步。推荐使用,性能和HashMap差距不大。

9.3.1.1 实现
public class ThreadDemo5 {public static void main(String[] args) {Map<String, String> map = new ConcurrentHashMap<>();for (int i = 0; i < 30; i++) {String key = String.valueOf(i);new Thread(() -> {map.put(key,UUID.randomUUID().toString().substring(0,6));System.out.println(map);},String.valueOf(i)).start();}}
}

10.多线程锁

10.1 锁的几种情况

方法上的synchronized锁,单synchronized的时候锁的是当前实例对象(this),其他实例对象不受影响

方法上的synchronized锁,含有static时,锁的是当前的class类,所有实例对象都受影响

但是上面的两种情况只能限制含有synchronized关键字的方法,如果是一个普通方法则不受限制(不含有synchronized,没有锁,那么就没有进出限制)

当在一个类中含有static的synchronized方法和不含有static的synchronized时,当两个方法都被调用了,但是第一个锁的是整个类,但是类中的方法没有被锁住,也就是这是两把锁,虽然大楼的门关了,但是大楼的房间还是可以随便进入的,其他方法也可以执行

10.1.1 实例

class Person{public synchronized void test(){// ....}public static synchronized void test2(){// ....}public void test3(){// ....}
}// 当只有单synchronized时锁的是p1实例对象
// 当在synchronized前面加了时,锁的是Person类
public class Test{// psvmPerson p1 = new Person();
}

10.2 公平锁和非公平锁

10.2.1 非公平锁

  • 使用非公平锁,当前线程发现有空就会去使用,不会考虑其他线程

  • 如果占用失败,则会采用类似公平锁的方式,到队列后面

  • 效率高,但可能会造成单个线程做完了所有事,其他线程没事可干,一直陪跑

  • synchronized是非公平锁

  • lock默认是非公平,但是可以设置为公平锁 new reentrant(true)

10.2.2 公平锁

  • 使用公平锁,当前线程发现有空的时候,会询问其他线程是否有线程要使用,如果没有其他线程要使用,就自己用,如果有其他线程用,就排队。

  • 按照申请锁的顺序来执行

  • 效率相对于非公平锁会低,因为会有询问的过程,但是相对非公平锁来说,每一个线程都有执行的机会,不会出现单线程干完所有事的情况(线程饥饿问题)

10.3 可重入锁

  • synchronized(隐式的上锁和解锁交给JVM)和Lock(显式的上锁和解锁交给用户本身)都是可重入锁

  • 可重入锁又称为递归锁,当一把锁内部还有其他锁的时候,此时线程如果获得最外层的锁,那么内部的锁线程也可以随意进入,下面通过synchronized来演示

  • 最大的作用就是避免死锁,还是要注意lock的锁释放

10.3.1 实例

public class ThreadDemo6 {public static void main(String[] args) {Object o = new Object();new Thread(() -> {synchronized (o){System.out.println(Thread.currentThread().getName()+"外部的锁");synchronized (o) {System.out.println(Thread.currentThread().getName()+"中间的锁");synchronized (o){System.out.println(Thread.currentThread().getName()+"内部的锁");}}}},"线程一").start();}
}
10.3.1.1 注意

对于线程本身当获得了外部的锁以后是可以随意的进入执行方法内部的锁,但是还是要及时进行解锁操作,虽然当前线程不会收到没有解锁的影响,但是如果上来锁但一直没有解锁的话,其他的线程就无法获得执行的机会,对于synchronized来说是自动上锁和解锁,所以没有解锁的问题,但对于lock来说是用户自己进行上锁和解锁操作,所以虽然获得了外部的锁,内部的锁对当前线程本身是没有影响的,但为了其他线程能够获得执行的机会,使用lock的时候也要及时的进行解锁操作。

10.4 死锁

两个或两个以上的线程在执行过程中,为了争夺资源而造成一种相互等待的情况,如果没有外力干涉,则相互都无法继续往下执行。

线程A现在拥有锁A,线程B现在拥有锁B,此时,线程A试图获取锁B,线程B试图获取锁A,因为双方都没有进行释放锁,所以会导致互相一直等待,造成死锁现象。

10.4.1 产生死锁的原因

  1. 系统资源不足
  2. 进程运行推进不顺利
  3. 系统资源分配不当

10.4.1.1 实例演示

public class DeadLock {static Object o1 = new Object();static Object o2 = new Object();public static void main(String[] args) {new Thread(() -> {synchronized (o1){System.out.println(Thread.currentThread().getName()+"持有o1锁,试图获取o2锁");try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}synchronized (o2){System.out.println(Thread.currentThread().getName()+"试图获取o2锁");}}},"线程一").start();new Thread(() -> {synchronized (o2){System.out.println(Thread.currentThread().getName()+"持有o2锁,试图获取o1锁");try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}synchronized (o1){System.out.println(Thread.currentThread().getName()+"试图获取o2锁");}}},"线程二").start();}
}// 输出结果
// 线程一持有o1锁,试图获取o2锁
// 线程二持有o2锁,试图获取o1锁
// (一直等待中)

10.4.2 验证死锁

可以通过java中自带的工具查询:jps和jstack

  1. jps -l ==> 找到当前运行类的java进程号
  2. jstack [java进程号] ==》 查询当前的运行情况
10.4.2.1 实例演示
PS F:\Java\JUC> jps -l
17044 sun.tools.jps.Jps
18692 org.jetbrains.jps.cmdline.Launcher
13612
21276 syn.DeadLock
PS F:\Java\JUC> jstack 21276
Found 1 deadlock. #返回当前为死锁状态

11.Callable接口

在jdk1.5以后新增的一种创建线程的方式,和Runnable的区别

  1. Callable可以有返回值
  2. Callable当返回值有问题时,会抛出异常,而Runnable不会抛出异常
  3. 实现方法名称不同,Runnable通过run()方法实现,Callable通过call()方法实现

11.1 创建一个实现Callable接口的线程

不能直接传递给Thread,Thread只能接收Runnable对象,因此为了让Thread能够接收Callable对象,要通过中间类来实现,这就是FutureTask类。

11.1.1 关于FutureTask

在不影响主线程的情况下,可以再开启其他线程来执行其他操作,这些线程执行完了以后,把结果返回给主线程,并且所有结果只会汇总一次

11.2 实例演示

class Demo implements Callable{@Overridepublic Object call() throws Exception {return 1+2+3;}
}public class CallableDemo {public static void main(String[] args) throws ExecutionException, InterruptedException {Demo c1 = new Demo();FutureTask<Integer> task1 = new FutureTask<Integer>(c1);FutureTask<Integer> task2 = new FutureTask<Integer>(() -> {return 1024;});new Thread(task1,"线程一").start();new Thread(task2,"线程二").start();// 等待得到返回结果while (!task1.isDone()){System.out.println("等待返回结果");}System.out.println("结果:");// 当得到一次结果以后,以后的get()则会直接调取结果,不用再进行运算// 即只用汇总一次System.out.println(task1.get());System.out.println(task1.get());// 当所有的新开起的线程都结束了,主线程才会结束System.out.println(task2.get());System.out.println(Thread.currentThread().getName()+"over");}}

12.JUC强大辅助类 – 线程同步工具

12.1 CountDownLatch

可以设定初始值,当这个类中的count属性不为0的时候,会让其他线程进入阻塞状态,调用await()方法,只有减为0才会唤醒其他线程往下执行,也就是解除await()方法只能是count减为0的时候

12.1.1 实例演示

public class CountDownLatchDemo {public static void main(String[] args) throws InterruptedException {CountDownLatch count = new CountDownLatch(6);for (int i = 0; i < 6; i++) {new Thread(() -> {System.out.println(Thread.currentThread().getName()+ " 号同学离开教室");count.countDown();},String.valueOf(i)).start();}count.await();System.out.println(Thread.currentThread().getName()+" 关闭教室门");}
}

12.2 CyclicBarrier

设定一组现成的额个数,多个线程开始执行,当开始执行的线程达到设定的一组的个数时,这一组的线程同时开始执行。没有达到数量时,已经开始执行的线程处于阻塞状态,等待其他线程的到来,然后再一起执行。

12.2.1 实例演示

public class CyclicBarrierDemo {public static void main(String[] args) {CyclicBarrier cyclicBarrier = new CyclicBarrier(6, new Runnable() {@Overridepublic void run() {System.out.println("已经集齐了7颗龙珠,召唤神龙");}});for (int i = 0; i < 7; i++) {new Thread(() -> {try {System.out.println(Thread.currentThread().getName()+ " 号龙珠被得到");cyclicBarrier.await();} catch (InterruptedException e) {e.printStackTrace();} catch (BrokenBarrierException e) {e.printStackTrace();}},String.valueOf(i)).start();}}}

12.3 Semaphore

设定许可的数量,当一个线程获取到了许可,则可以执行方法,其他开启的线程没有获得许可,则进入等待状态,当有线程释放许可以后,等待的线程争夺许可,获得了许可执行,没有获得的继续等待。

12.3.1 实例演示

public class SemaphoreDemo {public static void main(String[] args) {Semaphore semaphore = new Semaphore(3);for (int i = 0; i < 6; i++) {new Thread(() -> {try {semaphore.acquire();System.out.println(Thread.currentThread().getName()+" 号获取到车位");TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();} finally {System.out.println(Thread.currentThread().getName()+" 离开车位====》");semaphore.release();}},String.valueOf(i)).start();}}
}

13.一些锁的概念

13.1 悲观锁

每次进行操作都会进行锁上,操作完以后才会解锁,当一个线程在执行的时候,其他线程都处于等待阻塞状态

例如:synchronized关键字

13.2 乐观锁

线程在执行的时候不会将要处理的资源锁上,会将资源复制一份,然后再复制的资源上进行操作,操作完以后提交到原有的数据上进行更新操作。但是在提交的时候,要进行比对判断,判断复制的原内容和现在的情况是否相同 ==》例如:现在有两个线程要操作同一个资源,它们在操作资源的时候都会先进行复制,然后在复制的资源上进行操作,但是现在线程一先处理完,提交更新了,这时的原资源已经发生了改变,当其他线程提交的时候,发现自己最开始复制的内容,和现在的资源内容不相同,则会提交失败,而是继续复制当前的内容,然后再提交判断。

例如:CAS,通过自旋来抵消阻塞,并不会真正的加锁而是通过代码编程的方式来解决线程同步问题。

13.3 表锁

在线程处理资源的时候,会将整个数据表都锁上,不允许其他线程进行操作,不会发生死锁现象。

13.4 行锁

在线程处理资源时,只会将要处理的一行进行上锁,表中的其他行是允许其他线程进行操作的,会出现死锁现象,即两个或两个以上的线程互相等待对方释放锁,而导致的多线程间互相等待的情况。

13.5 读锁

在读的时候,允许多个线程一起读,共享锁,会发生死锁现象 ==》 例如:多个线程都在读取同一行资源,但是互相之间都想要修改,则都在互相等待对方读取完毕然后进行修改的操作,因为都想修改,则会导致互相等待的情况。

13.6 写锁

在写的时候,不允许多个线程共同操作,独享锁,但也会发生死锁现象 == 》 例如:多个线程都在修改不同的资源,但是都又想操作对方的资源,而出现互相等待死锁的情况。

14.读写锁

读的时候可以共享,但是写的时候只能有一个线程进行操作。但是不能同时存在读写操作,读写是互斥的,读读是共享的。

14.1 优点

比其synchronized和lock独占锁的情况,可以提升读的性能

14.2 缺点

  1. 会造成线程饥饿,也就是说会导致有线程一直在读,进而无法进行写的操作
  2. 写的时候不能读,一直等待,写的时候可以读

14.3 读写锁的实例

public class ReadWriteLockDemo {public static void main(String[] args) {ContextDemo context = new ContextDemo();for (int i = 0; i < 5; i++) {final int num = i;new Thread(() -> {try {context.put(num+"",num+"");} catch (InterruptedException e) {e.printStackTrace();}},String.valueOf(i)).start();}for (int i = 0; i < 5; i++) {final int num = i;new Thread(() -> {try {context.get(num+"");} catch (InterruptedException e) {e.printStackTrace();}},String.valueOf(i)).start();}}
}class ContextDemo {private volatile Map<String,Object> map = new HashMap<>();private ReadWriteLock lock = new ReentrantReadWriteLock();public void put(String key, Object o) throws InterruptedException {lock.writeLock().lock();System.out.println(Thread.currentThread().getName()+" 正在写入~~");TimeUnit.MICROSECONDS.sleep(300);map.put(key,o);System.out.println(Thread.currentThread().getName()+" 写入完毕~~");lock.writeLock().unlock();}public Object get(String key) throws InterruptedException {Object res = null;lock.readLock().lock();System.out.println(Thread.currentThread().getName()+" 正在读取~~");TimeUnit.MICROSECONDS.sleep(300);res = map.get(key);System.out.println(Thread.currentThread().getName()+" 读取完毕~~");lock.readLock().unlock();return res;}
}// 结果
1 正在写入~~
1 写入完毕~~
2 正在写入~~
2 写入完毕~~
3 正在写入~~
3 写入完毕~~
0 正在写入~~
0 写入完毕~~
4 正在写入~~
4 写入完毕~~
// 写是独享锁,读是共享锁,一起读
0 正在读取~~
1 正在读取~~
2 正在读取~~
4 正在读取~~
3 正在读取~~
1 读取完毕~~
0 读取完毕~~
3 读取完毕~~
2 读取完毕~~
4 读取完毕~~

14.4 读写锁的降级

写锁可以降级为读锁,但是读锁不能升级写锁,只有在读锁释放了以后才能再进行获取写锁的操作,然后进行写的操作。写的时候可以进行读,但是读的时候不能进行写

14.4.1 过程

  1. 获取到写锁
  2. 获取到读锁
  3. 释放写锁
  4. 释放读锁

15.阻塞队列

  • 阻塞队列是一个共享队列,一边进行往队列中进行添加元素,一边进行往队列进行取出元素。

  • 对于添加元素的线程来说,当阻塞队列中的空间为满的时候,它则不能再继续添加元素,这时线程的操作就会被阻塞,线程也就被阻塞了。

  • 同理对于队列另一边的线程来说,当阻塞队列中的空间为空的时候,它则不能继续进行取出元素,这时线程的操作就会被阻塞,线程也就被阻塞了。

  • 当有线程在阻塞状态,等待阻塞队列空间发生变化,当空间发生变化更新,阻塞队列会唤醒相应的线程。阻塞队列的优点就是,阻塞和唤醒线程,都进行了自动化,无需外界干扰。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EfxmopPK-1643884728908)(F:\StudyNotepad\img\image-20211103110017441.png)]

15.1 阻塞队列的类型

  • ArrayBlockingQueue:由数组结构组成的有界阻塞队列
  • LinkedBlockingQueue:由链表结构组成的有界(但是默认值大小为Integer,MAX_VALUE)阻塞队列
  • PriorityBlockingQueue:支持优先级排序的无界阻塞队列
  • DelayQueue:使用优先级队列支持延迟的无界阻塞队列
  • SynchronousQueue:不存储元素的队列,也即单个元素的队列。没有容量,每一个put操作必须等待一个take操作,否则不能添加元素,反之亦然。(定制化,存储单个元素)
  • LinkedTransferQueue:由链表结构组成的无界阻塞队列
  • LinkedBlockingDeque:由链表结构组成的双向阻塞队列

15.2 方法

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-edGuBiOu-1643884728909)(F:\StudyNotepad\img\image-20211103112315084.png)]

15.3 实例演示

public class BlockingQueueDemo {public static void main(String[] args) throws InterruptedException {// 下面的演示方法都为一个共享队列BlockingQueue blockingQueue = new ArrayBlockingQueue(3);// 第一组方法演示,这组会主动抛出异常
//        System.out.println(blockingQueue.add("aa"));
//        System.out.println(blockingQueue.add("aa"));
//        System.out.println(blockingQueue.add("aa"));
//        System.out.println(blockingQueue.add("aa"));// 移除当前第一个元素
//        System.out.println(blockingQueue.remove());
//        blockingQueue.remove();
//        blockingQueue.remove();
//        blockingQueue.remove();// 检查当前队列,并查看队列头部      System.out.println(blockingQueue.element());/*Exception in thread "main" java.lang.IllegalStateException: Queue fullat java.util.AbstractQueue.add(AbstractQueue.java:98)at java.util.concurrent.ArrayBlockingQueue.add(ArrayBlockingQueue.java:312)at BlockingQueueDemo.main(BlockingQueueDemo.java:18)*/
//        System.out.println("===============================");// 第二组方法演示,会有返回时候加入成功
//        System.out.println(blockingQueue.offer("bbb"));
//        System.out.println(blockingQueue.offer("ccc"));
//        System.out.println(blockingQueue.offer("ddd"));/* truefalsefalse*/// 取出当前队列中的值,如果取出的时候队列为空,则会返回null
//        System.out.println(blockingQueue.poll());
//        System.out.println(blockingQueue.poll());
//        System.out.println(blockingQueue.poll());
//        System.out.println(blockingQueue.poll());// 查看当前队列的第一个值,但是不取出
//        System.out.println(blockingQueue.peek());
//        System.out.println(blockingQueue.peek());
//        System.out.println(blockingQueue.peek());System.out.println("===============================");// 演示第三组方法,满足相应的空间条件可以执行,否则线程进入阻塞等待状态// 添加元素,如果为空则可以添加成功,如果满则无法进行添加,线程阻塞
//        blockingQueue.put("tt1");
//        blockingQueue.put("tt2");
//        blockingQueue.put("tt3");// 同理
//        blockingQueue.take();
//        blockingQueue.take();
//        blockingQueue.take();// 演示第四组方法,返回添加情况,设置超时等待,超过时间则退出System.out.println(blockingQueue.offer("aa", 3, TimeUnit.SECONDS));}
}

SynchronousQueue<>()

public class SynchronousQueueDemo {public static void main(String[] args) {BlockingQueue<String> bq = new SynchronousQueue<>();new Thread(() -> {try {System.out.println(Thread.currentThread().getName()+ "放入第一个元素");bq.put("1");TimeUnit.SECONDS.sleep(1);System.out.println(Thread.currentThread().getName()+ "放入第二个元素");bq.put("2");TimeUnit.SECONDS.sleep(1);System.out.println(Thread.currentThread().getName()+ "放入第三个元素");bq.put("3");} catch (InterruptedException e) {e.printStackTrace();}},"线程一").start();new Thread(() -> {try {TimeUnit.SECONDS.sleep(1);System.out.println(Thread.currentThread().getName()+" 等待两秒后取出元素");TimeUnit.SECONDS.sleep(2);bq.take();System.out.println(Thread.currentThread().getName()+" 已取出第一个元素");System.out.println(Thread.currentThread().getName()+" 等待两秒后取出第二个元素");TimeUnit.SECONDS.sleep(2);bq.take();System.out.println(Thread.currentThread().getName()+" 已取出第二个元素");System.out.println(Thread.currentThread().getName()+" 等待两秒后取出第三个元素");TimeUnit.SECONDS.sleep(2);bq.take();System.out.println(Thread.currentThread().getName()+" 已取出第三个元素");} catch (InterruptedException e) {e.printStackTrace();}},"线程二").start();}
}

只有当前队列中的元素取出,才能放入下一个元素,否则就进入阻塞状态

16.线程池

16.1 概述

线程池是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务。线程池线程都是后台线程。每个线程都使用默认的堆栈大小,以默认的优先级运行,并处于多线程单元中。如果某个线程在托管代码中空闲(如正在等待某个事件),则线程池将插入另一个辅助线程来使所有处理器保持繁忙。如果所有线程池线程都始终保持繁忙,但队列中包含挂起的工作,则线程池将在一段时间后创建另一个辅助线程但线程的数目永远不会超过最大值。超过最大值的线程可以排队,但他们要等到其他线程完成后才启动。

线程在线程池中提前创建,当有任务来的时候,线程池可以调用线程来处理,提高了响应的速度,可以方便的管理线程,也可以减少内存的消耗。提高线程的复用性。

16.2 实现方式

Java中通过Executor工具类来实现线程池的创建和管理

  1. 第一种通过Executors.newFixedThreadPool来创建大小并设定线程数量
  2. 第二种通过Executors.newSingleThreadExecutor创建拥有一个线程的线程池
  3. 第三种通过Executors.newCachedThreadPool创建一个自适应的线程池大小

16.2.1 实现演示

public class ThreadPoolDemo {public static void main(String[] args) {// 第一种通过Executors.newFixedThreadPool来创建大小并设定线程数量ExecutorService threadPool1 = Executors.newFixedThreadPool(3);// 第二种通过Executors.newSingleThreadExecutor创建拥有一个线程的线程池ExecutorService threadPool2 = Executors.newSingleThreadExecutor();// 第三种通过Executors.newCachedThreadPool创建一个自适应的线程池大小ExecutorService threadPool3 = Executors.newCachedThreadPool();try {for (int i = 0; i < 20; i++) {threadPool3.execute(() -> {try {System.out.println(Thread.currentThread().getName()+ " 在办理业务");TimeUnit.SECONDS.sleep(1);System.out.println(Thread.currentThread().getName()+ " 业务办理完成");} catch (InterruptedException e) {e.printStackTrace();}});}} finally {// 处理完以后一定要关闭线程池threadPool3.shutdown();}}
}

16.2.2 分析

通过源码分析这三种创建方式,都是通过ThreadPoolExecutor来实现的,现在我们来分析一下这个类

16.3 ThreadPoolExecutor类

public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueueThreadFactory threadFactory,RejectedExecutionHandler handler)

16.3.1 参数解析

  • int corePoolSize

    常驻的线程数量,也称为核心的线程数量

  • int maximumPoolSize

    线程池中最大的线程数量

  • long keepAliveTime

    这个和下面的参数组合使用,上面这个是除了核心线程以外的存活时间,超过了存活时间则关闭,下面的参数是设定时间的类型

  • TimeUnit unit

  • BlockingQueue workQueue

    阻塞队列,当目前线程池中的线程都在进行任务处理的时候,将新来的任务,添加到阻塞队列中,等待有线程来处理任务

  • ThreadFactory threadFactory

    线程工厂,负责创建线程

  • RejectedExecutionHandler handle

    拒绝策略。当已经达到线程池的最大线程数以后,再来的任务,无法执行,而执行的一种拒绝策略

16.3.2 工作流程和拒绝策略

16.3.3 工作流程

  1. 当有任务来的时候才会创建线程池

  2. 先用常驻线程来进行任务处理,当常驻线程都在进行任务处理,再到来的任务进入阻塞队列

  3. 当阻塞队列已经排满了,这时启用线程池中除常用线程以外的线程(线程池最大线程数 - 常驻线程)处理到来的任务

    在阻塞队列满以后到达的任务会直接用备用的线程处理,而不用等待。阻塞队列等待的任务继续等待。

  4. 如果线程池中的已经达到最大线程数量在工作,继续有任务到来,则执行拒绝策略

16.3.4 拒绝策略

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jjQV3tJn-1643884728910)(F:\StudyNotepad\img\image-20211103123819897.png)]

  1. 抛出异常,无法处理。== 》 出门右拐
  2. 谁安排你让到这个线程池处理的找谁。==》 管我屁事
  3. 将在阻塞队列中排的最久的任务抛弃,然后将新来 的任务加入阻塞队列中。 ==》 谁让你等
  4. 将所有在排队的任务中,无法处理的任务不处理也不抛出异常。 == 》 不理不睬

16.4 自定义线程池

一般不使用上面的方式创建线程池,而是自定义。

使用上面的方式,可能会造成任务堆积或资源浪费。

16.4.1 实例

public class NewThreadByDIY {public static void main(String[] args) {ExecutorService diyThread = new ThreadPoolExecutor(2,7,3L,TimeUnit.SECONDS,new ArrayBlockingQueue<>(3),Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());try {for (int i = 0; i < 20; i++) {diyThread.execute(() -> {try {System.out.println(Thread.currentThread().getName()+ " 在办理业务");TimeUnit.SECONDS.sleep(1);System.out.println(Thread.currentThread().getName()+ " 业务办理完成");} catch (InterruptedException e) {e.printStackTrace();}});}} finally {// 处理完以后一定要关闭线程池diyThread.shutdown();}}
}

17.分支合并框架

17.1 概述

将大问题化为小问题,然后合并

17.2 实例

public class Task {public static void main(String[] args) throws ExecutionException, InterruptedException {// 创建对象MyTask myTask = new MyTask(1, 100);// 创建分支合并对象池ForkJoinPool pool = new ForkJoinPool();// 执行ForkJoinTask<Integer> submit = pool.submit(myTask);// 获取结果Integer integer = submit.get();System.out.println(integer);// 关闭线程池pool.shutdown();}
}class MyTask extends RecursiveTask<Integer> {private static final int VALUE = 10;private int begin;private int end;private int res;public MyTask(int begin, int end) {this.begin = begin;this.end = end;}@Overrideprotected Integer compute() {if ((end - begin) <= VALUE){for (int i = begin; i <= end; i++) {res = res + i;}} else {int mid = (begin + end) / 2;// 拆分左边MyTask task1 = new MyTask(begin, mid);// 拆分右边MyTask task2 = new MyTask(mid+1, end);task1.fork();task2.fork();// 合并res = task1.join() + task2.join();}return res;}
}

18.异步回调

18.1 同步回调

我们常用的一些请求都是同步回调的,同步回调是阻塞的,单个的线程需要等待结果的返回才能继续往下执行。

18.2 异步回调

有的时候,我们不希望程序在某个执行方法上一直阻塞,需要先执行后续的方法,那就是这里的异步回调。我们在调用一个方法时,如果执行时间比较长,我们可以传入一个回调的方法,当方法执行完时,让被调用者执行给定的回调方法。

异步回调,不需要等待分派的任务执行完毕以后再进行下面的任务执行,而是继续往下面执行,当分派的任务执行完毕以后接收结果即可。

18.3 两种创建方式

  1. CompletableFuture.runAsync

    没有返回值

  2. CompletableFuture.supplyAsync

    有返回值

18.4 实例

public class ThreadDemo7 {public static void main(String[] args) throws ExecutionException, InterruptedException {// 没有返回值CompletableFuture<Void> runAsync = CompletableFuture.runAsync(() -> {System.out.println(Thread.currentThread().getName() + "CompletableFuture1");});runAsync.get();// 有返回值CompletableFuture<Object> supplyAsync = CompletableFuture.supplyAsync(() -> {return 77;});supplyAsync.whenComplete((u,t) -> {// t打印返回值// u打印异常信息System.out.println(Thread.currentThread().getName() + t);System.out.println(Thread.currentThread().getName() + u);}).get();}
}
查看全文
如若内容造成侵权/违法违规/事实不符,请联系编程学习网邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!

相关文章

  1. 微信小程序学习笔记③——wxml+wxss+js基础入门[样例+解析]

    ✅ 文章目录一、从「网页三大件」到「微信小程序三大件」二、WXML三、WXMLWXSS四、WXMLWXSSJS五、参考附录微信小程序开发 &#x1f332; 上一篇文章链接: 微信小程序学习笔记②——页面和主体[app.json模拟QQ的tabBar栏] 下一篇文章链接: &#x1f6a7; &#x1f6a7; … 一…...

    2024/4/7 20:18:22
  2. Windows 10上安装ROS noetic平台

    根据ROS官网的指南&#xff0c; 安装ROS noetic的过程我摘录在下面&#xff0c;注意&#xff1a;和官网不一样的地方我用中文特别说明了一下。 ROS on Windows installation Windows Operating System ROS for Windows requires 64-bit Windows 10 Desktop or Windows 10 IoT…...

    2024/4/20 4:23:32
  3. openGauss认证@备考4

    备库4一、选择题1、pg_hba.conf中authentication-options参数取值范围2、将字符串转换为指定的编码类型3、使用gs_restore可以选择要从自定义归档导出文件中导入相应的数据库对象&#xff0c;需要指定哪些参数4、判断图形是否为闭合路径5、与或非优先级顺序6、几何函数7、查看表…...

    2024/4/18 14:23:16
  4. redis的key关键字相关使用介绍

    keys * 查看当前库中所有的key exists key 判断某个key是否存在 type key 查看key是什么数据类型 del key 删除指定的key数据(直接删除数据) unlink key 根据value选择非阻塞删除&#xff08;执行命令的时候&#xff0c;并没有真正删除&#xff0c;在后续中&#xff0c;再真…...

    2024/4/7 20:18:17
  5. 自己创建一个Mini-Vue

    published: true date: 2022-2-3 tags: ‘前端框架 Vue’ mini-vue 本章在之前的章节的基础中实现了一个简单的vue框架&#xff0c;其中响应式的函数有略微变化不过大致原理相同。 致谢Vue Mastery非常好的课程&#xff0c;可以转载&#xff0c;但请声明源链接&#xff1a;文章…...

    2024/5/8 17:26:03
  6. case when nknown column的原因之一

    sql语句如下&#xff1a; SELECT *, CASE WHEN companyName gfdgfs THEN 2 ELSE 0 END CASE WHEN companyName LIKE %gfdgfs% THEN 1 ELSE 0 END CASE WHEN companyName potato THEN 2 ELSE 0 END CASE WHEN companyName LIKE %potato% THEN 1 ELSE 0 END CASE WHEN address…...

    2024/4/13 17:51:39
  7. Unity3D 切分组图

    如何切取下面的各个图 将图片拖到Unity3D中&#xff0c;选择组图&#xff0c;再选择 multiple选项 然后再点击Sprite Editor按钮 然后再点击Slice里的Trim选项&#xff0c;自动选取各个对象轴心 切完之后的组图有几个对象...

    2024/4/13 17:51:59
  8. 超详细Vue3响应式原理介绍加源码阅读

    published: true date: 2022-2-3 tags: ‘前端框架 Vue’ Vue3 Reactivity 本章介绍Vue中另一个非常重要的模块&#xff0c;响应式。介绍了基本原理&#xff08;含图&#xff09;简单实现以及如何阅读源码。 致谢Vue Mastery非常好的课程&#xff0c;可以转载&#xff0c;但请…...

    2024/5/8 12:47:24
  9. 二维差分

    题目描述&#xff1a; 输入一个 n 行 m 列的整数矩阵&#xff0c;再输入 q 个操作&#xff0c;每个操作包含五个整数 x1,y1,x2,y2,c 其中 (x1,y1) 和 (x2,y2) 表示一个子矩阵的左上角坐标和右下角坐标。 每个操作都要将选中的子矩阵中的每个元素的值加上 c。 请你将进行完所有操…...

    2024/4/18 20:24:28
  10. JDBC-----什么是JDBC

    JDBC-----什么是JDBC文章目录&#x1f525;一、开胃小菜阶段&#x1f370;&#x1f525;二、应用程序如何访问JDBC&#x1f37e;&#x1f525;三、JDBC的具体实现细节❄&#x1f525;四、Driver接口✊&#x1f525;五、每日一夸&#x1f396;&#x1f34e;姓名:洋葱爱代码&…...

    2024/5/8 12:22:41
  11. 人工智能课程设计——植物识别专家系统

    系统总体功能结构图 算法设计 根据植物特征识别植物名称控制策略&#xff1a;检索知识库中的规则&#xff0c;若输入为一个特征&#xff0c;则从知识库中按照序号检索前项,找出后项,若后项又是其它规则的前项,则继续检索,直到没有匹配的前项。 例如&#xff1a;若在知识库中存…...

    2024/4/15 4:49:55
  12. webpack学习Day12-一些总结

    一些总结&#xff1a;...

    2024/5/8 18:46:49
  13. Pytorch:优化器、损失函数与深度神经网络框架

    Pytorch: 优化器、损失函数与深度神经网络框架 Copyright: Jingmin Wei, Pattern Recognition and Intelligent System, School of Artificial and Intelligence, Huazhong University of Science and Technology 文章目录Pytorch: 优化器、损失函数与深度神经网络框架[toc]常…...

    2024/4/13 17:51:44
  14. 单例设计模式【学习笔记】

    单例模式 饿汉式 import java.io.Serializable;/**在这里插入代码片* Innertwo* */ public class two implements Serializable{private two(){//防止反射 破环单例if(INSTANCE ! null){throw new RuntimeException("error");}}private static final two INSTANCE …...

    2024/4/22 3:26:20
  15. Xml—dom4j解析以及写入xml文档时的乱码问题,Android开发者跳槽面试

    public class Dom4j { private File file new File(“src/book.xml”); //遍历xml文档 Test public void testList() throws Exception{ SAXReader reader new SAXReader(); Document document reader.read(file); Element root document.getRootElement(); list(r…...

    2024/5/8 11:55:46
  16. JavaSE-循环语句(2)

    三、break的使用 /* 生成0-100随机数,直到生成88为止,停止循环! break:完全跳出循环 注意: 如果是多层循环,break只能跳出它所在那层循环 */ public class BreakDemo{public static void main(String[] args)} //定义一个计数器 /*int count = 0; while(true){in…...

    2024/4/13 17:51:39
  17. 为什么写博客、如何写?

    一、为什么写博客 1.博客是程序员最好的名片。&#xff08;面试加分&#xff09; 2.检验、总结自己的学习成果&#xff0c;对技术更加理解和深刻。&#xff08;写的过程 复现&#xff09; &#xff08;1&#xff09;能将自己所学用语言文字清晰地讲给别人听&#xff0c;才证…...

    2024/4/20 3:29:33
  18. 数据结构07:选择排序

    选择排序&#xff1a;每次从待排序列中找出一个最小值放在已排序序列的末尾位置。改进&#xff1a;每次从待排序列中找出一个最小值和一个最大值&#xff0c;最小值放在已排序序列末尾位置&#xff0c;最大值放在待排序序列末尾位置&#xff0c;并将这个位置指定为已排序位置 …...

    2024/5/8 15:29:27
  19. Vue3虚拟DOM介绍

    published: true date: 2022-2-3 tags: ‘前端框架 Vue’ 虚拟DOM 本章将从零介绍Vue中的虚拟DOM&#xff0c;从渲染函数带到mount函数以及最后的patch函数也都有具体的代码实现。 致谢Vue Mastery非常好的课程&#xff0c;可以转载&#xff0c;但请声明源链接&#xff1a;文章…...

    2024/4/13 17:51:49
  20. 搜索之广搜

    P1162 填涂颜色 在方阵外围加一圈零&#xff0c;方便搜索 bfs代码: #include<bits/stdc.h> using namespace std; int mapp[32][32] {0}, n; bool visit[32][32] {0}; int dis_x[4] {0, 0, 1, -1}; int dis_y[4] {1, -1, 0, 0}; struct Node{int x; int y; };void…...

    2024/5/8 3:02:33

最新文章

  1. 深入探索:npm详解

    在现代前端开发中&#xff0c;npm&#xff08;Node Package Manager&#xff09;扮演着至关重要的角色&#xff0c;它是JavaScript世界中最大的软件注册表&#xff0c;也是前端开发者分享和管理代码的主要平台。本篇博文将详细介绍npm的使用方法&#xff0c;并重点讨论如何发布…...

    2024/5/8 19:03:21
  2. 梯度消失和梯度爆炸的一些处理方法

    在这里是记录一下梯度消失或梯度爆炸的一些处理技巧。全当学习总结了如有错误还请留言&#xff0c;在此感激不尽。 权重和梯度的更新公式如下&#xff1a; w w − η ⋅ ∇ w w w - \eta \cdot \nabla w ww−η⋅∇w 个人通俗的理解梯度消失就是网络模型在反向求导的时候出…...

    2024/5/7 10:36:02
  3. 【Java】假如把集合体系看作购物中心

    购物中心入口&#xff1a;Java集合框架 “Java集合广场”的购物中心&#xff0c;这是一个集合了各种奇特商店的地方&#xff0c;每个商店都充满了不同的宝藏&#xff08;数据结构&#xff09;。 一楼&#xff1a;基础集合区 - Collection接口 一楼是基础集合区&#xff0c;这…...

    2024/5/5 15:55:12
  4. audio_video_img图片音视频异步可视化加载

    最近在做即时消息&#xff0c;消息类型除了文字还有音频、视频、图片展示&#xff0c;如果消息很多&#xff0c;在切换聊天框时&#xff0c;会有明显卡顿&#xff0c;后续做了懒加载&#xff0c;方案是只加载用户能看到的资源&#xff0c;看不到的先不加载&#xff1b; LazyAud…...

    2024/5/8 2:36:28
  5. 【外汇早评】美通胀数据走低,美元调整

    原标题:【外汇早评】美通胀数据走低,美元调整昨日美国方面公布了新一期的核心PCE物价指数数据,同比增长1.6%,低于前值和预期值的1.7%,距离美联储的通胀目标2%继续走低,通胀压力较低,且此前美国一季度GDP初值中的消费部分下滑明显,因此市场对美联储后续更可能降息的政策…...

    2024/5/8 6:01:22
  6. 【原油贵金属周评】原油多头拥挤,价格调整

    原标题:【原油贵金属周评】原油多头拥挤,价格调整本周国际劳动节,我们喜迎四天假期,但是整个金融市场确实流动性充沛,大事频发,各个商品波动剧烈。美国方面,在本周四凌晨公布5月份的利率决议和新闻发布会,维持联邦基金利率在2.25%-2.50%不变,符合市场预期。同时美联储…...

    2024/5/7 9:45:25
  7. 【外汇周评】靓丽非农不及疲软通胀影响

    原标题:【外汇周评】靓丽非农不及疲软通胀影响在刚结束的周五,美国方面公布了新一期的非农就业数据,大幅好于前值和预期,新增就业重新回到20万以上。具体数据: 美国4月非农就业人口变动 26.3万人,预期 19万人,前值 19.6万人。 美国4月失业率 3.6%,预期 3.8%,前值 3…...

    2024/5/4 23:54:56
  8. 【原油贵金属早评】库存继续增加,油价收跌

    原标题:【原油贵金属早评】库存继续增加,油价收跌周三清晨公布美国当周API原油库存数据,上周原油库存增加281万桶至4.692亿桶,增幅超过预期的74.4万桶。且有消息人士称,沙特阿美据悉将于6月向亚洲炼油厂额外出售更多原油,印度炼油商预计将每日获得至多20万桶的额外原油供…...

    2024/5/7 14:25:14
  9. 【外汇早评】日本央行会议纪要不改日元强势

    原标题:【外汇早评】日本央行会议纪要不改日元强势近两日日元大幅走强与近期市场风险情绪上升,避险资金回流日元有关,也与前一段时间的美日贸易谈判给日本缓冲期,日本方面对汇率问题也避免继续贬值有关。虽然今日早间日本央行公布的利率会议纪要仍然是支持宽松政策,但这符…...

    2024/5/4 23:54:56
  10. 【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响

    原标题:【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响近日伊朗局势升温,导致市场担忧影响原油供给,油价试图反弹。此时OPEC表态稳定市场。据消息人士透露,沙特6月石油出口料将低于700万桶/日,沙特已经收到石油消费国提出的6月份扩大出口的“适度要求”,沙特将满…...

    2024/5/4 23:55:05
  11. 【外汇早评】美欲与伊朗重谈协议

    原标题:【外汇早评】美欲与伊朗重谈协议美国对伊朗的制裁遭到伊朗的抗议,昨日伊朗方面提出将部分退出伊核协议。而此行为又遭到欧洲方面对伊朗的谴责和警告,伊朗外长昨日回应称,欧洲国家履行它们的义务,伊核协议就能保证存续。据传闻伊朗的导弹已经对准了以色列和美国的航…...

    2024/5/4 23:54:56
  12. 【原油贵金属早评】波动率飙升,市场情绪动荡

    原标题:【原油贵金属早评】波动率飙升,市场情绪动荡因中美贸易谈判不安情绪影响,金融市场各资产品种出现明显的波动。随着美国与中方开启第十一轮谈判之际,美国按照既定计划向中国2000亿商品征收25%的关税,市场情绪有所平复,已经开始接受这一事实。虽然波动率-恐慌指数VI…...

    2024/5/7 11:36:39
  13. 【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试

    原标题:【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试美国和伊朗的局势继续升温,市场风险情绪上升,避险黄金有向上突破阻力的迹象。原油方面稍显平稳,近期美国和OPEC加大供给及市场需求回落的影响,伊朗局势并未推升油价走强。近期中美贸易谈判摩擦再度升级,美国对中…...

    2024/5/4 23:54:56
  14. 【原油贵金属早评】市场情绪继续恶化,黄金上破

    原标题:【原油贵金属早评】市场情绪继续恶化,黄金上破周初中国针对于美国加征关税的进行的反制措施引发市场情绪的大幅波动,人民币汇率出现大幅的贬值动能,金融市场受到非常明显的冲击。尤其是波动率起来之后,对于股市的表现尤其不安。隔夜美国股市出现明显的下行走势,这…...

    2024/5/6 1:40:42
  15. 【外汇早评】美伊僵持,风险情绪继续升温

    原标题:【外汇早评】美伊僵持,风险情绪继续升温昨日沙特两艘油轮再次发生爆炸事件,导致波斯湾局势进一步恶化,市场担忧美伊可能会出现摩擦生火,避险品种获得支撑,黄金和日元大幅走强。美指受中美贸易问题影响而在低位震荡。继5月12日,四艘商船在阿联酋领海附近的阿曼湾、…...

    2024/5/4 23:54:56
  16. 【原油贵金属早评】贸易冲突导致需求低迷,油价弱势

    原标题:【原油贵金属早评】贸易冲突导致需求低迷,油价弱势近日虽然伊朗局势升温,中东地区几起油船被袭击事件影响,但油价并未走高,而是出于调整结构中。由于市场预期局势失控的可能性较低,而中美贸易问题导致的全球经济衰退风险更大,需求会持续低迷,因此油价调整压力较…...

    2024/5/4 23:55:17
  17. 氧生福地 玩美北湖(上)——为时光守候两千年

    原标题:氧生福地 玩美北湖(上)——为时光守候两千年一次说走就走的旅行,只有一张高铁票的距离~ 所以,湖南郴州,我来了~ 从广州南站出发,一个半小时就到达郴州西站了。在动车上,同时改票的南风兄和我居然被分到了一个车厢,所以一路非常愉快地聊了过来。 挺好,最起…...

    2024/5/7 9:26:26
  18. 氧生福地 玩美北湖(中)——永春梯田里的美与鲜

    原标题:氧生福地 玩美北湖(中)——永春梯田里的美与鲜一觉醒来,因为大家太爱“美”照,在柳毅山庄去寻找龙女而错过了早餐时间。近十点,向导坏坏还是带着饥肠辘辘的我们去吃郴州最富有盛名的“鱼头粉”。说这是“十二分推荐”,到郴州必吃的美食之一。 哇塞!那个味美香甜…...

    2024/5/4 23:54:56
  19. 氧生福地 玩美北湖(下)——奔跑吧骚年!

    原标题:氧生福地 玩美北湖(下)——奔跑吧骚年!让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 啊……啊……啊 两…...

    2024/5/4 23:55:06
  20. 扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!

    原标题:扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!扒开伪装医用面膜,翻六倍价格宰客!当行业里的某一品项火爆了,就会有很多商家蹭热度,装逼忽悠,最近火爆朋友圈的医用面膜,被沾上了污点,到底怎么回事呢? “比普通面膜安全、效果好!痘痘、痘印、敏感肌都能用…...

    2024/5/5 8:13:33
  21. 「发现」铁皮石斛仙草之神奇功效用于医用面膜

    原标题:「发现」铁皮石斛仙草之神奇功效用于医用面膜丽彦妆铁皮石斛医用面膜|石斛多糖无菌修护补水贴19大优势: 1、铁皮石斛:自唐宋以来,一直被列为皇室贡品,铁皮石斛生于海拔1600米的悬崖峭壁之上,繁殖力差,产量极低,所以古代仅供皇室、贵族享用 2、铁皮石斛自古民间…...

    2024/5/4 23:55:16
  22. 丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者

    原标题:丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者【公司简介】 广州华彬企业隶属香港华彬集团有限公司,专注美业21年,其旗下品牌: 「圣茵美」私密荷尔蒙抗衰,产后修复 「圣仪轩」私密荷尔蒙抗衰,产后修复 「花茵莳」私密荷尔蒙抗衰,产后修复 「丽彦妆」专注医学护…...

    2024/5/4 23:54:58
  23. 广州械字号面膜生产厂家OEM/ODM4项须知!

    原标题:广州械字号面膜生产厂家OEM/ODM4项须知!广州械字号面膜生产厂家OEM/ODM流程及注意事项解读: 械字号医用面膜,其实在我国并没有严格的定义,通常我们说的医美面膜指的应该是一种「医用敷料」,也就是说,医用面膜其实算作「医疗器械」的一种,又称「医用冷敷贴」。 …...

    2024/5/6 21:42:42
  24. 械字号医用眼膜缓解用眼过度到底有无作用?

    原标题:械字号医用眼膜缓解用眼过度到底有无作用?医用眼膜/械字号眼膜/医用冷敷眼贴 凝胶层为亲水高分子材料,含70%以上的水分。体表皮肤温度传导到本产品的凝胶层,热量被凝胶内水分子吸收,通过水分的蒸发带走大量的热量,可迅速地降低体表皮肤局部温度,减轻局部皮肤的灼…...

    2024/5/4 23:54:56
  25. 配置失败还原请勿关闭计算机,电脑开机屏幕上面显示,配置失败还原更改 请勿关闭计算机 开不了机 这个问题怎么办...

    解析如下&#xff1a;1、长按电脑电源键直至关机&#xff0c;然后再按一次电源健重启电脑&#xff0c;按F8健进入安全模式2、安全模式下进入Windows系统桌面后&#xff0c;按住“winR”打开运行窗口&#xff0c;输入“services.msc”打开服务设置3、在服务界面&#xff0c;选中…...

    2022/11/19 21:17:18
  26. 错误使用 reshape要执行 RESHAPE,请勿更改元素数目。

    %读入6幅图像&#xff08;每一幅图像的大小是564*564&#xff09; f1 imread(WashingtonDC_Band1_564.tif); subplot(3,2,1),imshow(f1); f2 imread(WashingtonDC_Band2_564.tif); subplot(3,2,2),imshow(f2); f3 imread(WashingtonDC_Band3_564.tif); subplot(3,2,3),imsho…...

    2022/11/19 21:17:16
  27. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机...

    win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”问题的解决方法在win7系统关机时如果有升级系统的或者其他需要会直接进入一个 等待界面&#xff0c;在等待界面中我们需要等待操作结束才能关机&#xff0c;虽然这比较麻烦&#xff0c;但是对系统进行配置和升级…...

    2022/11/19 21:17:15
  28. 台式电脑显示配置100%请勿关闭计算机,“准备配置windows 请勿关闭计算机”的解决方法...

    有不少用户在重装Win7系统或更新系统后会遇到“准备配置windows&#xff0c;请勿关闭计算机”的提示&#xff0c;要过很久才能进入系统&#xff0c;有的用户甚至几个小时也无法进入&#xff0c;下面就教大家这个问题的解决方法。第一种方法&#xff1a;我们首先在左下角的“开始…...

    2022/11/19 21:17:14
  29. win7 正在配置 请勿关闭计算机,怎么办Win7开机显示正在配置Windows Update请勿关机...

    置信有很多用户都跟小编一样遇到过这样的问题&#xff0c;电脑时发现开机屏幕显现“正在配置Windows Update&#xff0c;请勿关机”(如下图所示)&#xff0c;而且还需求等大约5分钟才干进入系统。这是怎样回事呢&#xff1f;一切都是正常操作的&#xff0c;为什么开时机呈现“正…...

    2022/11/19 21:17:13
  30. 准备配置windows 请勿关闭计算机 蓝屏,Win7开机总是出现提示“配置Windows请勿关机”...

    Win7系统开机启动时总是出现“配置Windows请勿关机”的提示&#xff0c;没过几秒后电脑自动重启&#xff0c;每次开机都这样无法进入系统&#xff0c;此时碰到这种现象的用户就可以使用以下5种方法解决问题。方法一&#xff1a;开机按下F8&#xff0c;在出现的Windows高级启动选…...

    2022/11/19 21:17:12
  31. 准备windows请勿关闭计算机要多久,windows10系统提示正在准备windows请勿关闭计算机怎么办...

    有不少windows10系统用户反映说碰到这样一个情况&#xff0c;就是电脑提示正在准备windows请勿关闭计算机&#xff0c;碰到这样的问题该怎么解决呢&#xff0c;现在小编就给大家分享一下windows10系统提示正在准备windows请勿关闭计算机的具体第一种方法&#xff1a;1、2、依次…...

    2022/11/19 21:17:11
  32. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”的解决方法...

    今天和大家分享一下win7系统重装了Win7旗舰版系统后&#xff0c;每次关机的时候桌面上都会显示一个“配置Windows Update的界面&#xff0c;提示请勿关闭计算机”&#xff0c;每次停留好几分钟才能正常关机&#xff0c;导致什么情况引起的呢&#xff1f;出现配置Windows Update…...

    2022/11/19 21:17:10
  33. 电脑桌面一直是清理请关闭计算机,windows7一直卡在清理 请勿关闭计算机-win7清理请勿关机,win7配置更新35%不动...

    只能是等着&#xff0c;别无他法。说是卡着如果你看硬盘灯应该在读写。如果从 Win 10 无法正常回滚&#xff0c;只能是考虑备份数据后重装系统了。解决来方案一&#xff1a;管理员运行cmd&#xff1a;net stop WuAuServcd %windir%ren SoftwareDistribution SDoldnet start WuA…...

    2022/11/19 21:17:09
  34. 计算机配置更新不起,电脑提示“配置Windows Update请勿关闭计算机”怎么办?

    原标题&#xff1a;电脑提示“配置Windows Update请勿关闭计算机”怎么办&#xff1f;win7系统中在开机与关闭的时候总是显示“配置windows update请勿关闭计算机”相信有不少朋友都曾遇到过一次两次还能忍但经常遇到就叫人感到心烦了遇到这种问题怎么办呢&#xff1f;一般的方…...

    2022/11/19 21:17:08
  35. 计算机正在配置无法关机,关机提示 windows7 正在配置windows 请勿关闭计算机 ,然后等了一晚上也没有关掉。现在电脑无法正常关机...

    关机提示 windows7 正在配置windows 请勿关闭计算机 &#xff0c;然后等了一晚上也没有关掉。现在电脑无法正常关机以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容&#xff0c;让我们赶快一起来看一下吧&#xff01;关机提示 windows7 正在配…...

    2022/11/19 21:17:05
  36. 钉钉提示请勿通过开发者调试模式_钉钉请勿通过开发者调试模式是真的吗好不好用...

    钉钉请勿通过开发者调试模式是真的吗好不好用 更新时间:2020-04-20 22:24:19 浏览次数:729次 区域: 南阳 > 卧龙 列举网提醒您:为保障您的权益,请不要提前支付任何费用! 虚拟位置外设器!!轨迹模拟&虚拟位置外设神器 专业用于:钉钉,外勤365,红圈通,企业微信和…...

    2022/11/19 21:17:05
  37. 配置失败还原请勿关闭计算机怎么办,win7系统出现“配置windows update失败 还原更改 请勿关闭计算机”,长时间没反应,无法进入系统的解决方案...

    前几天班里有位学生电脑(windows 7系统)出问题了&#xff0c;具体表现是开机时一直停留在“配置windows update失败 还原更改 请勿关闭计算机”这个界面&#xff0c;长时间没反应&#xff0c;无法进入系统。这个问题原来帮其他同学也解决过&#xff0c;网上搜了不少资料&#x…...

    2022/11/19 21:17:04
  38. 一个电脑无法关闭计算机你应该怎么办,电脑显示“清理请勿关闭计算机”怎么办?...

    本文为你提供了3个有效解决电脑显示“清理请勿关闭计算机”问题的方法&#xff0c;并在最后教给你1种保护系统安全的好方法&#xff0c;一起来看看&#xff01;电脑出现“清理请勿关闭计算机”在Windows 7(SP1)和Windows Server 2008 R2 SP1中&#xff0c;添加了1个新功能在“磁…...

    2022/11/19 21:17:03
  39. 请勿关闭计算机还原更改要多久,电脑显示:配置windows更新失败,正在还原更改,请勿关闭计算机怎么办...

    许多用户在长期不使用电脑的时候&#xff0c;开启电脑发现电脑显示&#xff1a;配置windows更新失败&#xff0c;正在还原更改&#xff0c;请勿关闭计算机。。.这要怎么办呢&#xff1f;下面小编就带着大家一起看看吧&#xff01;如果能够正常进入系统&#xff0c;建议您暂时移…...

    2022/11/19 21:17:02
  40. 还原更改请勿关闭计算机 要多久,配置windows update失败 还原更改 请勿关闭计算机,电脑开机后一直显示以...

    配置windows update失败 还原更改 请勿关闭计算机&#xff0c;电脑开机后一直显示以以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容&#xff0c;让我们赶快一起来看一下吧&#xff01;配置windows update失败 还原更改 请勿关闭计算机&#x…...

    2022/11/19 21:17:01
  41. 电脑配置中请勿关闭计算机怎么办,准备配置windows请勿关闭计算机一直显示怎么办【图解】...

    不知道大家有没有遇到过这样的一个问题&#xff0c;就是我们的win7系统在关机的时候&#xff0c;总是喜欢显示“准备配置windows&#xff0c;请勿关机”这样的一个页面&#xff0c;没有什么大碍&#xff0c;但是如果一直等着的话就要两个小时甚至更久都关不了机&#xff0c;非常…...

    2022/11/19 21:17:00
  42. 正在准备配置请勿关闭计算机,正在准备配置windows请勿关闭计算机时间长了解决教程...

    当电脑出现正在准备配置windows请勿关闭计算机时&#xff0c;一般是您正对windows进行升级&#xff0c;但是这个要是长时间没有反应&#xff0c;我们不能再傻等下去了。可能是电脑出了别的问题了&#xff0c;来看看教程的说法。正在准备配置windows请勿关闭计算机时间长了方法一…...

    2022/11/19 21:16:59
  43. 配置失败还原请勿关闭计算机,配置Windows Update失败,还原更改请勿关闭计算机...

    我们使用电脑的过程中有时会遇到这种情况&#xff0c;当我们打开电脑之后&#xff0c;发现一直停留在一个界面&#xff1a;“配置Windows Update失败&#xff0c;还原更改请勿关闭计算机”&#xff0c;等了许久还是无法进入系统。如果我们遇到此类问题应该如何解决呢&#xff0…...

    2022/11/19 21:16:58
  44. 如何在iPhone上关闭“请勿打扰”

    Apple’s “Do Not Disturb While Driving” is a potentially lifesaving iPhone feature, but it doesn’t always turn on automatically at the appropriate time. For example, you might be a passenger in a moving car, but your iPhone may think you’re the one dri…...

    2022/11/19 21:16:57