一次性搞清楚,Java并发编程在各主流框架中的应用,保证看懂
Spring、Netty、Mybatis 等框架的代码中大量运用了 Java 多线程编程技巧。并发编程处理的恰当与否,将直接影响架构的性能。 本文通过对这些框架源码的分析,结合并发编程的常用技巧,来讲解多线程编程在这些主流框架中的应用。
Java 内存模型
JVM 规范定义了 Java 内存模型来屏蔽掉各种操作系统、虚拟机实现厂商和硬件的内存访问差异,以确保 Java 程序在所有操作系统和平台上能够达到一致的内存访问效果。
工作内存和主内存
Java 内存模型规定所有的变量都存储在主内存中,每个线程都有自己独立的工作内存,工作内存保存了对应该线程使用的变量的主内存副本拷贝。 线程对这些变量的操作都在自己的工作内存中进行,不能直接操作主内存和其他工作内存中存储的变量或者变量副本。线程间的变量传递需通过主内存来完成,三者的关系如下图所示。
Java 内存操作协议
Java 内存模型定义了 8 种操作来完成主内存和工作内存的变量访问,具体如下。
- read:把一个变量的值从主内存传输到线程的工作内存中,以便随后的 load 动作使用。
- load:把从主内存中读取的变量值载入工作内存的变量副本中。
- use:把工作内存中一个变量的值传递给 Java 虚拟机执行引擎。
- assign:把从执行引擎接收到的变量的值赋值给工作内存中的变量。
- store:把工作内存中一个变量的值传送到主内存中,以便随后的 write 操作。
- write:工作内存传递过来的变量值放入主内存中。
- lock:把主内存的一个变量标识为某个线程独占的状态。
- unlock:把主内存中 一个处于锁定状态的变量释放出来,被释放后的变量才可以被其他线程锁定。
内存模型三大特性
1、原子性
这个概念与事务中的原子性大概一致,表明此操作是不可分割,不可中断的,要么全部执行,要么全部不执行。Java 内存模型直接保证的原子性操作包括 read、load、use、assign、store、write、lock、unlock 这八个。
2、可见性
可见性是指当一个线程修改了共享变量的值,其他线程能够立即得知这个修改。 Java 内存模型是通过在变量修改后将新值同步回主内存,在变量读取前从主内存刷新变量值这种依赖主内存作为传递媒介的方式来实现可见性的,无论是普通变量还是 volatile 变量都是如此,普通变量与 volatile 变量的区别是,volatile 的特殊规则保证了新值能立即同步到主内存,以及每次使用前立即从主内存刷新。因此,可以说 volatile 保证了多线程操作时变量的可见性,而普通变量则不能保证这一点。除了 volatile 外,synchronized 也提供了可见性,synchronized 的可见性是由 “对一个变量执行 unlock 操作 之前,必须先把此变量同步回主内存中(执行 store、write 操作)” 这条规则获得。
3、有序性
单线程环境下,程序会 “有序的”执行,即:线程内表现为串行语义。但是在多线程环境下,由于指令重排,并发执行的正确性会受到影响。在 Java 中使用 volatile 和 synchronized 关键字,可以保证多线程执行的有序性。volatile 通过加入内存屏障指令来禁止内存的重排序。synchronized 通过加锁,保证同一时刻只有一个线程来执行同步代码。
volatile 的应用
打开 NioEventLoop 的代码中,有一个控制 IO 操作 和 其他任务运行比例的,用 volatile 修饰的 int 类型字段 ioRatio,代码如下。
private volatile int ioRatio = 50;
这里为什么要用 volatile 修饰呢?我们首先对 volatile 关键字进行说明,然后再结合 Netty 的代码进行分析。
关键字 volatile 是 Java 提供的最轻量级的同步机制,Java 内存模型对 volatile 专门定义了一些特殊的访问规则。下面我们就看它的规则。当一个变量被 volatile 修饰后,它将具备以下两种特性。
- 线程可见性:当一个线程修改了被 volatile 修饰的变量后,无论是否加锁,其他线程都可以立即看到最新的修改,而普通变量却做不到这点。
- 禁止指令重排序优化:普通的变量仅仅保证在该方法的执行过程中所有依赖赋值结果的地方都能获取正确的结果,而不能保证变量赋值操作的顺序与程序代码的执行顺序一致。举个简单的例子说明下指令重排序优化问题,代码如下:
public class ThreadStopExample {private static boolean stop;public static void main(String[] args) throws InterruptedException {Thread workThread = new Thread(new Runnable() {public void run() {int i= 0;while (!stop) {i++; try{TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace(); } } } }); workThread.start(); TimeUnit.SECONDS.sleep(3);stop = true;}}
我们预期程序会在 3s 后停止,但是实际上它会一直执行下去,原因就是虚拟机对代码进行了指令重排序和优化,优化后的指令如下:
if (!stop)While(true)......
workThread 线程在执行重排序后的代码时,是无法发现变量 stop 被其它线程修改的,因此无法停止运行。要解决这个问题,只要将 stop 前增加 volatile 修饰符即可。volatile 解决了如下两个问题。第一,主线程对 stop 的修改在 workThread 线程 中可见,也就是说 workThread 线程 立即看到了其他线程对于 stop 变量 的修改。第二,禁止指令重排序,防止因为重排序导致的并发访问逻辑混乱。
一些人认为使用 volatile 可以代替传统锁,提升并发性能,这个认识是错误的。volatile 仅仅解决了可见性的问题,但是它并不能保证互斥性,也就是说多个线程并发修改某个变量时,依旧会产生多线程问题。因此,不能靠 volatile 来完全替代传统的锁。根据经验总结,volatile 最适用的场景是 “ 一个线程写,其他线程读 ”,如果有多个线程并发写操作,仍然需要使用锁或者线程安全的容器或者原子变量来代替。下面我们继续对 Netty 的源码做分析。上面讲到了 ioRatio 被定义成 volatile,下面看看代码为什么要这样定义。
final long ioTime = System.nanoTime() - ioStartTime;runAllTasks(ioTime * (100 - ioRatio) / ioRatio);
通过代码分析我们发现,在 NioEventLoop 线程 中,ioRatio 并没有被修改,它是只读操作。既然没有修改,为什么要定义成 volatile 呢?继续看代码,我们发现 NioEventLoop 提供了重新设置 IO 执行时间比例的公共方法。
public void setIoRatio(int ioRatio) {if (ioRatio <= 0 || ioRatio > 100) {throw new IllegalArgumentException("ioRatio: " + ioRatio + " (expected: 0 < ioRatio <= 100)");} this.ioRatio = ioRatio;}
首先,NioEventLoop 线程 没有调用该 set 方法,说明调整 IO 执行时间比例 是外部发起的操作,通常是由业务的线程调用该方法,重新设置该参数。这样就形成了一个线程写、一个线程读。根据前面针对 volatile 的应用总结,此时可以使用 volatile 来代替传统的 synchronized 关键字,以提升并发访问的性能。
ThreadLocal 的应用及源码解析
ThreadLocal 又称为线程本地存储区(Thread Local Storage,简称为 TLS),每个线程都有自己的私有的本地存储区域,不同线程之间彼此不能访问对方的 TLS 区域。使用 ThreadLocal 变量 的 set(T value) 方法 可以将数据存入该线程本地存储区,使用 get() 方法 可以获取到之前存入的值。
ThreadLocal 的常见应用
不使用 ThreadLocal。
public class SessionBean {public static class Session {private String id;public String getId() {return id;} public void setId(String id) {this.id = id;} } public Session createSession() {return new Session();} public void setId(Session session, String id) {session.setId(id); } public String getId(Session session) {return session.getId();} public static void main(String[] args) {//没有使用ThreadLocal,在方法间共享session需要进行session在方法间的传递new Thread(() -> {SessionBean bean = new SessionBean();Session session = bean.createSession();bean.setId(session, "susan");System.out.println(bean.getId(session));}).start();}
}
上述代码中,session 需要在方法间传递才可以修改和读取,保证线程中各方法操作的是一个。下面看一下使用 ThreadLocal 的代码。
public class SessionBean {
//定义一个静态ThreadLocal变量session,就能够保证各个线程有自己的一份,并且方法可以方便获取,不用传递private static ThreadLocal<Session> session = new ThreadLocal<>();public static class Session {private String id;public String getId() {return id;}public void setId(String id) {this.id = id;}}public void createSession() {session.set(new Session());}public void setId(String id) {session.get().setId(id);}public String getId() {return session.get().getId();}public static void main(String[] args) {new Thread(() -> {SessionBean bean = new SessionBean();bean.createSession();bean.setId("susan");System.out.println(bean.getId());}).start();}
}
在方法的内部实现中,直接可以通过 session.get() 获取到当前线程的 session,省掉了参数在方法间传递的环节。
ThreadLocal 的实现原理
一般,类属性中的数据是多个线程共享的,但 ThreadLocal 类型的数据 声明为类属性,却可以为每一个使用它(通过 set(T value)方法)的线程存储线程私有的数据,通过其源码我们可以发现其中的原理。
public class ThreadLocal<T> {/*** 下面的 getMap()方法 传入当前线程,获得一个ThreadLocalMap对象,说明每一个线程维护了* 自己的一个 map,保证读取出来的value是自己线程的。** ThreadLocalMap 是ThreadLocal静态内部类,存储value的键值就是ThreadLocal本身。** 因此可以断定,每个线程维护一个ThreadLocalMap的键值对映射Map。不同线程的Map的 key值 是一样的,* 都是ThreadLocal,但 value 是不同的。*/public T get() {Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t);if (map != null) {ThreadLocalMap.Entry e = map.getEntry(this);if (e != null) {@SuppressWarnings("unchecked")T result = (T)e.value; return result;} } return setInitialValue();} public void set(T value) {Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t);if (map != null)map.set(this, value);elsecreateMap(t, value); }}
ThreadLocal 在 Spring 中的使用
Spring 事务处理的设计与实现中大量使用了 ThreadLocal 类,比如,TransactionSynchronizationManager 维护了一系列的 ThreadLocal 变量,用于存储线程私有的 事务属性及资源。源码如下。
/*** 管理每个线程的资源和事务同步的中心帮助程序。供资源管理代码使用,但不供典型应用程序代码使用。** 资源管理代码应该检查线程绑定的资源,如,JDBC连接 或 Hibernate Sessions。* 此类代码通常不应该将资源绑定到线程,因为这是事务管理器的职责。另一个选项是,* 如果事务同步处于活动状态,则在首次使用时延迟绑定,以执行跨任意数量资源的事务。*/
public abstract class TransactionSynchronizationManager {/*** 一般是一个线程持有一个 独立的事务,以相互隔离地处理各自的事务。* 所以这里使用了很多 ThreadLocal对象,为每个线程绑定 对应的事务属性及资源,* 以便后续使用时能直接获取。*/private static final ThreadLocal<Map<Object, Object>> resources =new NamedThreadLocal<Map<Object, Object>>("Transactional resources");private static final ThreadLocal<Set<TransactionSynchronization>> synchronizations =new NamedThreadLocal<Set<TransactionSynchronization>>("Transaction synchronizations");private static final ThreadLocal<String> currentTransactionName =new NamedThreadLocal<String>("Current transaction name");private static final ThreadLocal<Boolean> currentTransactionReadOnly =new NamedThreadLocal<Boolean>("Current transaction read-only status");private static final ThreadLocal<Integer> currentTransactionIsolationLevel =new NamedThreadLocal<Integer>("Current transaction isolation level");private static final ThreadLocal<Boolean> actualTransactionActive =new NamedThreadLocal<Boolean>("Actual transaction active");/*** 为当前线程 绑定 对应的resource资源*/public static void bindResource(Object key, Object value) throws IllegalStateException {Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key);Assert.notNull(value, "Value must not be null");Map<Object, Object> map = resources.get();// 如果当前线程的 resources中,绑定的数据map为空,则为 resources 绑定 mapif (map == null) {map = new HashMap<Object, Object>();resources.set(map);}Object oldValue = map.put(actualKey, value);if (oldValue instanceof ResourceHolder && ((ResourceHolder) oldValue).isVoid()) {oldValue = null;}if (oldValue != null) {throw new IllegalStateException("Already value [" + oldValue + "] for key [" +actualKey + "] bound to thread [" + Thread.currentThread().getName() + "]");}if (logger.isTraceEnabled()) {logger.trace("Bound value [" + value + "] for key [" + actualKey + "] to thread [" +Thread.currentThread().getName() + "]");}}/*** 返回当前线程绑定的所有资源*/public static Map<Object, Object> getResourceMap() {Map<Object, Object> map = resources.get();return (map != null ? Collections.unmodifiableMap(map) : Collections.emptyMap());}
}
ThreadLocal 在 Mybatis 中的使用
Mybatis 的 SqlSession 对象 也是各线程私有的资源,所以对其的管理也使用到了 ThreadLocal 类。源码如下。
public class SqlSessionManager implements SqlSessionFactory, SqlSession {private final ThreadLocal<SqlSession> localSqlSession = new ThreadLocal<>();public void startManagedSession() {this.localSqlSession.set(openSession());} public void startManagedSession(boolean autoCommit) {this.localSqlSession.set(openSession(autoCommit));} public void startManagedSession(Connection connection) {this.localSqlSession.set(openSession(connection));} public void startManagedSession(TransactionIsolationLevel level) {this.localSqlSession.set(openSession(level));} public void startManagedSession(ExecutorType execType) {this.localSqlSession.set(openSession(execType));} public void startManagedSession(ExecutorType execType, boolean autoCommit) {this.localSqlSession.set(openSession(execType, autoCommit));} public void startManagedSession(ExecutorType execType, TransactionIsolationLevel level) {this.localSqlSession.set(openSession(execType, level));} public void startManagedSession(ExecutorType execType, Connection connection) {this.localSqlSession.set(openSession(execType, connection));} public boolean isManagedSessionStarted() {return this.localSqlSession.get() != null;} @Overridepublic Connection getConnection() {final SqlSession sqlSession = localSqlSession.get();if (sqlSession == null) {throw new SqlSessionException("Error: Cannot get connection. No managed session is started.");} return sqlSession.getConnection();} @Overridepublic void clearCache() {final SqlSession sqlSession = localSqlSession.get();if (sqlSession == null) {throw new SqlSessionException("Error: Cannot clear the cache. No managed session is started.");} sqlSession.clearCache(); } @Overridepublic void commit() {final SqlSession sqlSession = localSqlSession.get();if (sqlSession == null) {throw new SqlSessionException("Error: Cannot commit. No managed session is started.");} sqlSession.commit(); } @Overridepublic void commit(boolean force) {final SqlSession sqlSession = localSqlSession.get();if (sqlSession == null) {throw new SqlSessionException("Error: Cannot commit. No managed session is started.");} sqlSession.commit(force); } @Overridepublic void rollback() {final SqlSession sqlSession = localSqlSession.get();if (sqlSession == null) {throw new SqlSessionException("Error: Cannot rollback. No managed session is started.");} sqlSession.rollback(); } @Overridepublic void rollback(boolean force) {final SqlSession sqlSession = localSqlSession.get();if (sqlSession == null) {throw new SqlSessionException("Error: Cannot rollback. No managed session is started.");} sqlSession.rollback(force); } @Overridepublic List<BatchResult> flushStatements() {final SqlSession sqlSession = localSqlSession.get();if (sqlSession == null) {throw new SqlSessionException("Error: Cannot rollback. No managed session is started.");} return sqlSession.flushStatements();} @Overridepublic void close() {final SqlSession sqlSession = localSqlSession.get();if (sqlSession == null) {throw new SqlSessionException("Error: Cannot close. No managed session is started.");} try {sqlSession.close(); } finally {localSqlSession.set(null);} }}
J.U.C 包的实际应用
线程池 ThreadPoolExecutor
首先通过 ThreadPoolExecutor 的源码 看一下线程池的主要参数及方法。
public class ThreadPoolExecutor extends AbstractExecutorService {/*** 核心线程数* 当向线程池提交一个任务时,若线程池已创建的线程数小于corePoolSize,即便此时存在空闲线程,* 也会通过创建一个新线程来执行该任务,直到已创建的线程数大于或等于corePoolSize*/private volatile int corePoolSize;/*** 最大线程数* 当队列满了,且已创建的线程数小于maximumPoolSize,则线程池会创建新的线程来执行任务。* 另外,对于无界队列,可忽略该参数*/private volatile int maximumPoolSize;/*** 线程存活保持时间* 当线程池中线程数 超出核心线程数,且线程的空闲时间也超过 keepAliveTime时,* 那么这个线程就会被销毁,直到线程池中的线程数小于等于核心线程数*/private volatile long keepAliveTime;/*** 任务队列* 用于传输和保存等待执行任务的阻塞队列*/private final BlockingQueue<Runnable> workQueue;/*** 线程工厂* 用于创建新线程。threadFactory 创建的线程也是采用 new Thread() 方式,threadFactory* 创建的线程名都具有统一的风格:pool-m-thread-n(m为线程池的编号,n为线程池中线程的编号*/private volatile ThreadFactory threadFactory;/*** 线程饱和策略* 当线程池和队列都满了,再加入的线程会执行此策略*/private volatile RejectedExecutionHandler handler;/*** 构造方法提供了多种重载,但实际上都使用了最后一个重载 完成了实例化*/public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue) {this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,Executors.defaultThreadFactory(), defaultHandler); } public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory) {this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,threadFactory, defaultHandler); } public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,RejectedExecutionHandler handler) {this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,Executors.defaultThreadFactory(), handler); } public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler) {if (corePoolSize < 0 ||maximumPoolSize <= 0 ||maximumPoolSize < corePoolSize || keepAliveTime < 0)throw new IllegalArgumentException();if (workQueue == null || threadFactory == null || handler == null)throw new NullPointerException();this.corePoolSize = corePoolSize;this.maximumPoolSize = maximumPoolSize;this.workQueue = workQueue;this.keepAliveTime = unit.toNanos(keepAliveTime);this.threadFactory = threadFactory;this.handler = handler;} /*** 执行一个任务,但没有返回值*/public void execute(Runnable command) {if (command == null)throw new NullPointerException();int c = ctl.get();if (workerCountOf(c) < corePoolSize) {if (addWorker(command, true))return;c = ctl.get();} if (isRunning(c) && workQueue.offer(command)) {int recheck = ctl.get();if (! isRunning(recheck) && remove(command))reject(command); else if (workerCountOf(recheck) == 0)addWorker(null, false);} else if (!addWorker(command, false))reject(command); } /*** 提交一个线程任务,有返回值。该方法继承自其父类 AbstractExecutorService,有多种重载,这是最常用的一个。* 通过future.get()获取返回值(阻塞直到任务执行完)*/public <T> Future<T> submit(Callable<T> task) {if (task == null) throw new NullPointerException();RunnableFuture<T> ftask = newTaskFor(task); execute(ftask); return ftask;} /*** 关闭线程池,不再接收新的任务,但会把已有的任务执行完*/public void shutdown() {final ReentrantLock mainLock = this.mainLock;mainLock.lock();try {checkShutdownAccess(); advanceRunState(SHUTDOWN); interruptIdleWorkers(); onShutdown(); // hook for ScheduledThreadPoolExecutor} finally {mainLock.unlock();}tryTerminate();}/*** 立即关闭线程池,已有的任务也会被抛弃*/public List<Runnable> shutdownNow() {List<Runnable> tasks;final ReentrantLock mainLock = this.mainLock;mainLock.lock();try {checkShutdownAccess();advanceRunState(STOP);interruptWorkers();tasks = drainQueue();} finally {mainLock.unlock();}tryTerminate();return tasks;}public boolean isShutdown() {return ! isRunning(ctl.get());}
}
线程池执行流程,如下图所示。
Executors 提供的 4 种线程池
Executors 类 通过 ThreadPoolExecutor 封装了 4 种常用的线程池:CachedThreadPool,FixedThreadPool,ScheduledThreadPool 和 SingleThreadExecutor。其功能如下。
- CachedThreadPool:用来创建一个几乎可以无限扩大的线程池(最大线程数为 Integer.MAX_VALUE),适用于执行大量短生命周期的异步任务。
- FixedThreadPool:创建一个固定大小的线程池,保证线程数可控,不会造成线程过多,导致系统负载更为严重。
- SingleThreadExecutor:创建一个单线程的线程池,可以保证任务按调用顺序执行。
- ScheduledThreadPool:适用于执行 延时 或者 周期性 任务。
如何配置线程池
- CPU 密集型任务
尽量使用较小的线程池,一般为 CPU 核心数+1。因为 CPU 密集型任务 使得 CPU 使用率 很高,若开过多的线程数,会造成 CPU 过度切换。
- IO 密集型任务
可以使用稍大的线程池,一般为 2*CPU 核心数。IO 密集型任务 CPU 使用率 并不高,因此可以让 CPU 在等待 IO 的时候有其他线程去处理别的任务,充分利用 CPU 时间。
线程池的实际应用
Tomcat 在分发 web 请求时使用了线程池来处理。
BlockingQueue
核心方法
public interface BlockingQueue<E> extends Queue<E> {// 将给定元素设置到队列中,如果设置成功返回true, 否则返回false。如果是往限定了长度的队列中设置值,推荐使用offer()方法。boolean add(E e); // 将给定的元素设置到队列中,如果设置成功返回true, 否则返回false. e的值不能为空,否则抛出空指针异常。boolean offer(E e); // 将元素设置到队列中,如果队列中没有多余的空间,该方法会一直阻塞,直到队列中有多余的空间。void put(E e) throws InterruptedException; // 将给定元素在给定的时间内设置到队列中,如果设置成功返回true, 否则返回false.boolean offer(E e, long timeout, TimeUnit unit) throws InterruptedException; // 从队列中获取值,如果队列中没有值,线程会一直阻塞,直到队列中有值,并且该方法取得了该值。E take() throws InterruptedException; // 在给定的时间里,从队列中获取值,时间到了直接调用普通的 poll()方法,为null则直接返回null。E poll(long timeout, TimeUnit unit) throws InterruptedException; // 获取队列中剩余的空间。int remainingCapacity(); // 从队列中移除指定的值。boolean remove(Object o); // 判断队列中是否拥有该值。public boolean contains(Object o); // 将队列中值,全部移除,并发设置到给定的集合中。int drainTo(Collection<? super E> c);// 指定最多数量限制将队列中值,全部移除,并发设置到给定的集合中。int drainTo(Collection<? super E> c, int maxElements);
}
主要实现类
- ArrayBlockingQueue
基于数组的阻塞队列实现,在 ArrayBlockingQueue 内部,维护了一个定长数组,以便缓存队列中的数据对象,这是一个常用的阻塞队列,除了一个定长数组外,ArrayBlockingQueue 内部还保存着两个整形变量,分别标识着队列的头部和尾部在数组中的位置。
ArrayBlockingQueue 在生产者放入数据 和 消费者获取数据时,都是共用同一个锁对象,由此也意味着两者无法真正并行运行,这点尤其不同于 LinkedBlockingQueue。ArrayBlockingQueue 和 LinkedBlockingQueue 间还有一个明显的不同之处在于,前者在插入或删除元素时不会产生或销毁任何额外的对象实例,而后者则会生成一个额外的 Node 对象。这在长时间内需要高效并发地处理大批量数据的系统中,其对于 GC 的影响还是存在一定的区别。而在创建 ArrayBlockingQueue 时,我们还可以控制对象的内部锁是否采用公平锁,默认采用非公平锁。
- LinkedBlockingQueue
基于链表的阻塞队列,同 ArrayListBlockingQueue 类似,其内部也维持着一个数据缓冲队列(该队列由一个链表构成),当生产者往队列中放入一个数据时,队列会从生产者手中获取数据,并缓存在队列内部,而生产者立即返回;只有当队列缓冲区达到最大值缓存容量时(LinkedBlockingQueue 可以通过构造函数指定该值),才会阻塞生产者队列,直到消费者从队列中消费掉一份数据,生产者线程会被唤醒,反之对于消费者这端的处理也基于同样的原理。而 LinkedBlockingQueue 之所以能够高效的处理并发数据,还因为其对于生产者端和消费者端分别采用了独立的锁来控制数据同步,这也意味着在高并发的情况下生产者和消费者可以并行地操作队列中的数据,以此来提高整个队列的并发性能。
需要注意的是,如果构造一个 LinkedBlockingQueue 对象,而没有指定其容量大小,LinkedBlockingQueue 会默认一个类似无限大小的容量(Integer.MAX_VALUE),这样的话,如果生产者的速度一旦大于消费者的速度,也许还没有等到队列满阻塞产生,系统内存就有可能已被消耗殆尽了。
- PriorityBlockingQueue
基于优先级的阻塞队列(优先级的判断通过构造函数传入的 Compator 对象来决定),但需要注意的是 PriorityBlockingQueue 并不会阻塞数据生产者,而只会在没有可消费的数据时,阻塞数据的消费者。因此使用的时候要特别注意,生产者生产数据的速度绝对不能快于消费者消费数据的速度,否则时间一长,会最终耗尽所有的可用堆内存空间。在实现 PriorityBlockingQueue 时,内部控制线程同步的锁采用的是公平锁。
CAS 指令和原子类(应用比较多的就是计数器)
互斥同步最主要的问题就是进行线程阻塞和唤醒所带来的性能的额外损耗,因此这种同步被称为阻塞同步,它属于一种悲观的并发策略,我们称之为悲观锁。随着硬件和操作系统指令集的发展和优化,产生了非阻塞同步,被称为乐观锁。简单地说,就是先进行操作,操作完成之后再判断操作是否成功,是否有并发问题,如果有则进行失败补偿,如果没有就算操作成功,这样就从根本上避免了同步锁的弊端。
目前,在 Java 中应用最广泛的非阻塞同步就是 CAS。从 JDK1.5 以后,可以使用 CAS 操作,该操作由 sun.misc.Unsafe 类里的 compareAndSwapInt() 和 compareAndSwapLong() 等方法实现。通常情况下 sun.misc.Unsafe 类 对于开发者是不可见的,因此,JDK 提供了很多 CAS 包装类 简化开发者的使用,如 AtomicInteger。使用 Java 自带的 Atomic 原子类,可以避免同步锁带来的并发访问性能降低的问题,减少犯错的机会。
全文完!
如若内容造成侵权/违法违规/事实不符,请联系编程学习网邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
相关文章
- Flutter List.map()时获取index
通常使用List渲染,写法如下:Widget _medicPlanDisplayContent(BuildContext context, PlanModels planModels) {return Container(child: Column(children: planModels.plan.map((planModel) {return _medicPlanDisplayItem(context, planModels, planModel);}).toList(),),);…...
2024/5/1 8:47:22 - 网站建设的基本流程,4个术语帮助新手快速入门
网站对于人们来说,既熟悉又陌生,我们每天都在浏览网站,但却不一定了解网站是由哪些方面组成的。下面准度科技小编就给大家介绍一下网站建设的基本流程,用4个行业术语帮助新手快速入门。 1、网站域名 就是在浏览器里访问的网址,一个有代表性的域名有利于网站的长远发展,需…...
2024/4/13 20:12:32 - Mybatis01——相关概念
Mybatis的相关概念 1. Mybatis是什么?MyBatis是一款一流的支持自定义SQL、存储过程和高级映射的持久化框架。MyBatis几乎消除了所有的JDBC代码,也基本不需要手工去设置参数和获取检索结果。MyBatis能够使用简单的XML格式或者注解进行来配置,能够映射基本数据元素、Map接口和…...
2024/4/22 1:10:47 - font字体的一些常用代码
作者:王宝旺2020-09-08 14:34阅读:1 font字体 1、font-size:字体大小 单位:1em(相对像素例如:设置html的px为10px那么1em就是10px);px像素(常用); 2、font-family:字体 一般使用宋体和微软雅黑,因为一般电脑都会有这两个字体,你设置的这个字体必须访问电脑安装了…...
2024/4/26 5:39:07 - MySQL笔记--2、3、4、5
1、目的:设置合适的数据类型给字段一、整数类型例如 (1)create table student(xh char(10),nl tinyint unsigned,sr dataime,dz varchar(200)) (2)score tinyint unsigned 二、小数类型decimal可以用来表示货币 decimal(5,2)表示小数取值范围-999.99~999.99(表示五位,…...
2024/4/28 5:05:40 - 从零搭建vue组件库并发布
从零搭建vue组件库并发布背景步骤1、安装vue-cli创建项目2、目录改造3、创建组件4、指定main5、发布后续 背景 之前写过一个vuepress编写API文档verdaccio搭建并发布组件库的博客,可以发布组件,但是存在一个问题,就是进入Wui目录(也就是我们的组件库)里面安装的依赖自定义…...
2024/4/13 12:58:41 - H5移动端适配屏幕(ios和android)
全面屏手机把整个webview(包括状态栏)给H5的话,不对状态栏进行控制往往会有以下如图问题出现,这样头部的返回按钮置于页面的安全区外,无法进行操作。 有问题的 想要的效果:具体的实现: 针对ios的刘海屏问题的解决: 一、设置网页在可视窗口的布局方式(重点是添加:view…...
2024/4/13 20:33:36 - Flink 任务提交失败 Could not deploy Yarn job cluster
1.Flink提价任务,读取Hbase数据和hdfs数据,需要单独倒入一些hbase的依赖jar,所以加入了maven依赖,本地能正常测试运行,但是发布到yarn上运行报错,所以测试读取hdfs文件的时候,去掉依赖就可以了。2.加入打包依赖的时候,注意加入过滤不需要的文件,不然会报错。org.apach…...
2024/5/1 4:39:44 - springboot配置多数据源redis
相对于redis的集群配置 这篇文章相对比较死板。用于学习redis集群配置的一个过渡 现在application.yml中配置redis链接属性文件redis:host: ${ip} //输入你要填入的IP地址password: passwordport: 6379timeout: 3000database: 10 #可不配,因为底层默认值为6379pool:max-act…...
2024/5/1 7:31:30 - webpack打包新能优化
在js中引入的第三方包,很少变动,分析一次就可以了,不用每次都分析1.,打包一次生成dll文件夹:const path=require(path); const webpack=require(webpack); module.exports={mode:production,performance:false,entry:{vendors:[jquery,echarts,vue]},output:{filename:[na…...
2024/4/26 23:47:25 - C_获得当前的工作路径 _getcwd()函数:取得当前的工作目录
相关函数:get_current_dir_name, getwd, chdir 头文件:#include <unistd.h> 或者 #include <direct.h> 定义函数:char * getcwd(char * buf, size_t size); 函数说明:getcwd()会将当前的工作目录绝对路径复制到参数buf 所指的内存空间,参数size 为buf 的空间大…...
2024/4/23 0:48:16 - DAVSE DMA8825 8个250瓦输出功率的数字矩阵放大器
8通道放大器 – 每通道250瓦 4欧姆和恒定电压型号(70V或100V) 8进x 8出的矩阵混合使路由简单 8个欧式连接器的麦克风前置放大器 双RCA和平衡欧式连接器输入 可选Dante网络型号(型号DT88) 44.1 kHz或48kHz采样率 24位A/D D/A转换 + 48V幻象电源 SD卡插槽用于音频播放 AES / …...
2024/4/28 4:57:11 - Python使用Selenium模拟浏览器自动操作功能
概述在进行网站爬取数据的时候,会发现很多网站都进行了反爬虫的处理,如JS加密,Ajax加密,反Debug等方法,通过请求获取数据和页面展示的内容完全不同,这时候就用到Selenium技术,来模拟浏览器的操作,然后获取数据。本文以一个简单的小例子,简述Python搭配Tkinter和Seleni…...
2024/4/13 8:59:55 - IDEA无法启动怎么办?
问题简述: 最近使用idea因为本人资金有限就寻求了破解版本的idea 但是我安装的时候却出现了问题 就是idea无法打开,因为这个问题我卸载了又下载,重复了无数遍,终于找到了问题所在。太难了问题解决: 打开idea目录,比如 D:\ideaut\IntelliJ IDEA 2019.3.3\bin,找到idea.b…...
2024/4/24 21:33:18 - 如何基于大数据及AI平台实现业务系统实时化?
简介: 后疫情时代的新社会模式及经济形态必将催生出新的商业模式,在线业务及相关应用场景的流量呈现井喷式发展,常规的离线系统及离线机器学习平台已无法满足业务发展要求。作者:高旸(吾与),阿里巴巴高级技术专家1. 前言随着互联网“人口红利”的“消耗殆尽”,基于“T+…...
2024/4/13 18:37:43 - 基于图像语义的视觉同步定位和建图综述:面向应用的移动机器人自主导航解决方案
基于图像语义的视觉同步定位和建图综述:面向应用的移动机器人自主导航解决方案 论文名称: A survey of image semantics-based visual simultaneous localization and mapping Application-oriented solutions to autonomous navigation of mobile robots 作者: Linlin Xia, J…...
2024/4/29 0:25:51 - 呐喊!国人开源了一款超好用的Redis客户端,太香了
大家都知道,Redis Desktop Manager 是一款非常好用的 Redis 可视化客户端工具,但可惜的是 v0.9.4 版本之后需要收费了:这个工具不再免费提供安装包了,要对所有安装包收费,收费还不便宜啊,但是源码依旧是公开的,要使用就必须要自行编译。市面上也有一些破解版本或是别人编…...
2024/4/24 7:05:51 - Kubernetes中比较全面的Service笔记
Service:服务发现 Service入门介绍 需要Service的原因,我觉得主要从两个方面考虑:Pod的状态并不稳定,导致PodIP会随时变化; 水平伸缩会使多个Pod提供相同的服务来负载均衡,但每个Pod的ip肯定说不一样的;所以就需要一个稳定的地址来暴露服务给用户,用户完全不需要考虑po…...
2024/4/13 6:32:50 - vue计算属性和监听器区别
区别计算属性可以简化差值表达式写法 计算属性变量定义在computed中,可以直接使用在{}中的,跟methods中函数类似,只不过有利于缓存,性能更好 计算属性可以防止监听属性的滥用,但一些异步请求,计算属性做不到,还得watch来完成。例如:解决element-ui分页中删除页面最后…...
2024/4/13 15:39:31 - Django+Nginx个人博客环境搭建
使用 Django + nginx 组成 Django2 uwsgi python3.7 nginx环境搭建步骤安装gityum install git安装python3.7教程 https://www.jianshu.com/p/7c2b62c37223 yum install gcc openssl-devel bzip2-devel expat-devel gdbm-devel readline-devel sqlite-devel libffi-devel tk-…...
2024/4/25 6:23:53
最新文章
- 【MySQL精炼宝库】深度解析索引 | 事务
目录 一、索引 1.1 索引(index)概念: 1.2 索引的作用: 1.3 索引的缺点: 1.4 索引的使用场景: 1.5 索引的使用: 1.6 面试题:索引底层的数据结构(核心内容): 1.7 索引列查询(主…...
2024/5/1 9:40:08 - 梯度消失和梯度爆炸的一些处理方法
在这里是记录一下梯度消失或梯度爆炸的一些处理技巧。全当学习总结了如有错误还请留言,在此感激不尽。 权重和梯度的更新公式如下: w w − η ⋅ ∇ w w w - \eta \cdot \nabla w ww−η⋅∇w 个人通俗的理解梯度消失就是网络模型在反向求导的时候出…...
2024/3/20 10:50:27 - Java多路查找树(含面试大厂题和源码)
多路查找树(Multiway Search Tree),也称为B树或B树,是一种自平衡的树形数据结构,用于存储大量数据,通常用于数据库和文件系统中。它允许在查找、插入和删除操作中保持数据的有序性,同时优化了磁…...
2024/4/30 7:21:22 - Redis Stack十部曲之三:理解Redis Stack中的数据类型
文章目录 前言String字符串作为计数器限制 List限制列表阻塞列表自动创建和删除聚合类型键限制 Set限制 Hash限制 Sorted Set范围操作字典操作更新分数 JSON路径限制 BitMapBitfieldProbabilisticHyperLogLogBloom filterCuckoo filtert-digestTop-KCount-min sketchConfigurat…...
2024/4/30 17:08:20 - 在 Visual Studio Code (VSCode) 中隐藏以 . 开头的文件
打开VSCode。 按下Ctrl ,快捷键打开设置。您也可以点击屏幕左下角的齿轮图标,然后选择“Settings”。 在设置搜索框中,键入files.exclude。 在找到的Files: Exclude项中,点击Add Pattern按钮来添加一个新的模式,或者直接在搜索…...
2024/4/29 19:58:39 - 416. 分割等和子集问题(动态规划)
题目 题解 class Solution:def canPartition(self, nums: List[int]) -> bool:# badcaseif not nums:return True# 不能被2整除if sum(nums) % 2 ! 0:return False# 状态定义:dp[i][j]表示当背包容量为j,用前i个物品是否正好可以将背包填满ÿ…...
2024/4/30 9:36:27 - 【Java】ExcelWriter自适应宽度工具类(支持中文)
工具类 import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.CellType; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet;/*** Excel工具类** author xiaoming* date 2023/11/17 10:40*/ public class ExcelUti…...
2024/4/30 0:57:52 - Spring cloud负载均衡@LoadBalanced LoadBalancerClient
LoadBalance vs Ribbon 由于Spring cloud2020之后移除了Ribbon,直接使用Spring Cloud LoadBalancer作为客户端负载均衡组件,我们讨论Spring负载均衡以Spring Cloud2020之后版本为主,学习Spring Cloud LoadBalance,暂不讨论Ribbon…...
2024/4/29 18:43:42 - TSINGSEE青犀AI智能分析+视频监控工业园区周界安全防范方案
一、背景需求分析 在工业产业园、化工园或生产制造园区中,周界防范意义重大,对园区的安全起到重要的作用。常规的安防方式是采用人员巡查,人力投入成本大而且效率低。周界一旦被破坏或入侵,会影响园区人员和资产安全,…...
2024/5/1 4:07:45 - VB.net WebBrowser网页元素抓取分析方法
在用WebBrowser编程实现网页操作自动化时,常要分析网页Html,例如网页在加载数据时,常会显示“系统处理中,请稍候..”,我们需要在数据加载完成后才能继续下一步操作,如何抓取这个信息的网页html元素变化&…...
2024/4/30 23:32:22 - 【Objective-C】Objective-C汇总
方法定义 参考:https://www.yiibai.com/objective_c/objective_c_functions.html Objective-C编程语言中方法定义的一般形式如下 - (return_type) method_name:( argumentType1 )argumentName1 joiningArgument2:( argumentType2 )argumentName2 ... joiningArgu…...
2024/4/30 23:16:16 - 【洛谷算法题】P5713-洛谷团队系统【入门2分支结构】
👨💻博客主页:花无缺 欢迎 点赞👍 收藏⭐ 留言📝 加关注✅! 本文由 花无缺 原创 收录于专栏 【洛谷算法题】 文章目录 【洛谷算法题】P5713-洛谷团队系统【入门2分支结构】🌏题目描述🌏输入格…...
2024/5/1 6:35:25 - 【ES6.0】- 扩展运算符(...)
【ES6.0】- 扩展运算符... 文章目录 【ES6.0】- 扩展运算符...一、概述二、拷贝数组对象三、合并操作四、参数传递五、数组去重六、字符串转字符数组七、NodeList转数组八、解构变量九、打印日志十、总结 一、概述 **扩展运算符(...)**允许一个表达式在期望多个参数࿰…...
2024/4/29 21:25:29 - 摩根看好的前智能硬件头部品牌双11交易数据极度异常!——是模式创新还是饮鸩止渴?
文 | 螳螂观察 作者 | 李燃 双11狂欢已落下帷幕,各大品牌纷纷晒出优异的成绩单,摩根士丹利投资的智能硬件头部品牌凯迪仕也不例外。然而有爆料称,在自媒体平台发布霸榜各大榜单喜讯的凯迪仕智能锁,多个平台数据都表现出极度异常…...
2024/5/1 4:35:02 - Go语言常用命令详解(二)
文章目录 前言常用命令go bug示例参数说明 go doc示例参数说明 go env示例 go fix示例 go fmt示例 go generate示例 总结写在最后 前言 接着上一篇继续介绍Go语言的常用命令 常用命令 以下是一些常用的Go命令,这些命令可以帮助您在Go开发中进行编译、测试、运行和…...
2024/4/30 14:53:47 - 用欧拉路径判断图同构推出reverse合法性:1116T4
http://cplusoj.com/d/senior/p/SS231116D 假设我们要把 a a a 变成 b b b,我们在 a i a_i ai 和 a i 1 a_{i1} ai1 之间连边, b b b 同理,则 a a a 能变成 b b b 的充要条件是两图 A , B A,B A,B 同构。 必要性显然࿰…...
2024/4/30 22:14:26 - 【NGINX--1】基础知识
1、在 Debian/Ubuntu 上安装 NGINX 在 Debian 或 Ubuntu 机器上安装 NGINX 开源版。 更新已配置源的软件包信息,并安装一些有助于配置官方 NGINX 软件包仓库的软件包: apt-get update apt install -y curl gnupg2 ca-certificates lsb-release debian-…...
2024/5/1 6:34:45 - Hive默认分割符、存储格式与数据压缩
目录 1、Hive默认分割符2、Hive存储格式3、Hive数据压缩 1、Hive默认分割符 Hive创建表时指定的行受限(ROW FORMAT)配置标准HQL为: ... ROW FORMAT DELIMITED FIELDS TERMINATED BY \u0001 COLLECTION ITEMS TERMINATED BY , MAP KEYS TERMI…...
2024/4/30 22:57:18 - 【论文阅读】MAG:一种用于航天器遥测数据中有效异常检测的新方法
文章目录 摘要1 引言2 问题描述3 拟议框架4 所提出方法的细节A.数据预处理B.变量相关分析C.MAG模型D.异常分数 5 实验A.数据集和性能指标B.实验设置与平台C.结果和比较 6 结论 摘要 异常检测是保证航天器稳定性的关键。在航天器运行过程中,传感器和控制器产生大量周…...
2024/4/30 20:39:53 - --max-old-space-size=8192报错
vue项目运行时,如果经常运行慢,崩溃停止服务,报如下错误 FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - JavaScript heap out of memory 因为在 Node 中,通过JavaScript使用内存时只能使用部分内存(64位系统&…...
2024/5/1 4:45:02 - 基于深度学习的恶意软件检测
恶意软件是指恶意软件犯罪者用来感染个人计算机或整个组织的网络的软件。 它利用目标系统漏洞,例如可以被劫持的合法软件(例如浏览器或 Web 应用程序插件)中的错误。 恶意软件渗透可能会造成灾难性的后果,包括数据被盗、勒索或网…...
2024/5/1 8:32:56 - JS原型对象prototype
让我简单的为大家介绍一下原型对象prototype吧! 使用原型实现方法共享 1.构造函数通过原型分配的函数是所有对象所 共享的。 2.JavaScript 规定,每一个构造函数都有一个 prototype 属性,指向另一个对象,所以我们也称为原型对象…...
2024/4/29 3:42:58 - C++中只能有一个实例的单例类
C中只能有一个实例的单例类 前面讨论的 President 类很不错,但存在一个缺陷:无法禁止通过实例化多个对象来创建多名总统: President One, Two, Three; 由于复制构造函数是私有的,其中每个对象都是不可复制的,但您的目…...
2024/4/29 19:56:39 - python django 小程序图书借阅源码
开发工具: PyCharm,mysql5.7,微信开发者工具 技术说明: python django html 小程序 功能介绍: 用户端: 登录注册(含授权登录) 首页显示搜索图书,轮播图࿰…...
2024/5/1 5:23:20 - 电子学会C/C++编程等级考试2022年03月(一级)真题解析
C/C++等级考试(1~8级)全部真题・点这里 第1题:双精度浮点数的输入输出 输入一个双精度浮点数,保留8位小数,输出这个浮点数。 时间限制:1000 内存限制:65536输入 只有一行,一个双精度浮点数。输出 一行,保留8位小数的浮点数。样例输入 3.1415926535798932样例输出 3.1…...
2024/4/30 20:52:33 - 配置失败还原请勿关闭计算机,电脑开机屏幕上面显示,配置失败还原更改 请勿关闭计算机 开不了机 这个问题怎么办...
解析如下:1、长按电脑电源键直至关机,然后再按一次电源健重启电脑,按F8健进入安全模式2、安全模式下进入Windows系统桌面后,按住“winR”打开运行窗口,输入“services.msc”打开服务设置3、在服务界面,选中…...
2022/11/19 21:17:18 - 错误使用 reshape要执行 RESHAPE,请勿更改元素数目。
%读入6幅图像(每一幅图像的大小是564*564) 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 - 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机...
win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”问题的解决方法在win7系统关机时如果有升级系统的或者其他需要会直接进入一个 等待界面,在等待界面中我们需要等待操作结束才能关机,虽然这比较麻烦,但是对系统进行配置和升级…...
2022/11/19 21:17:15 - 台式电脑显示配置100%请勿关闭计算机,“准备配置windows 请勿关闭计算机”的解决方法...
有不少用户在重装Win7系统或更新系统后会遇到“准备配置windows,请勿关闭计算机”的提示,要过很久才能进入系统,有的用户甚至几个小时也无法进入,下面就教大家这个问题的解决方法。第一种方法:我们首先在左下角的“开始…...
2022/11/19 21:17:14 - win7 正在配置 请勿关闭计算机,怎么办Win7开机显示正在配置Windows Update请勿关机...
置信有很多用户都跟小编一样遇到过这样的问题,电脑时发现开机屏幕显现“正在配置Windows Update,请勿关机”(如下图所示),而且还需求等大约5分钟才干进入系统。这是怎样回事呢?一切都是正常操作的,为什么开时机呈现“正…...
2022/11/19 21:17:13 - 准备配置windows 请勿关闭计算机 蓝屏,Win7开机总是出现提示“配置Windows请勿关机”...
Win7系统开机启动时总是出现“配置Windows请勿关机”的提示,没过几秒后电脑自动重启,每次开机都这样无法进入系统,此时碰到这种现象的用户就可以使用以下5种方法解决问题。方法一:开机按下F8,在出现的Windows高级启动选…...
2022/11/19 21:17:12 - 准备windows请勿关闭计算机要多久,windows10系统提示正在准备windows请勿关闭计算机怎么办...
有不少windows10系统用户反映说碰到这样一个情况,就是电脑提示正在准备windows请勿关闭计算机,碰到这样的问题该怎么解决呢,现在小编就给大家分享一下windows10系统提示正在准备windows请勿关闭计算机的具体第一种方法:1、2、依次…...
2022/11/19 21:17:11 - 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”的解决方法...
今天和大家分享一下win7系统重装了Win7旗舰版系统后,每次关机的时候桌面上都会显示一个“配置Windows Update的界面,提示请勿关闭计算机”,每次停留好几分钟才能正常关机,导致什么情况引起的呢?出现配置Windows Update…...
2022/11/19 21:17:10 - 电脑桌面一直是清理请关闭计算机,windows7一直卡在清理 请勿关闭计算机-win7清理请勿关机,win7配置更新35%不动...
只能是等着,别无他法。说是卡着如果你看硬盘灯应该在读写。如果从 Win 10 无法正常回滚,只能是考虑备份数据后重装系统了。解决来方案一:管理员运行cmd:net stop WuAuServcd %windir%ren SoftwareDistribution SDoldnet start WuA…...
2022/11/19 21:17:09 - 计算机配置更新不起,电脑提示“配置Windows Update请勿关闭计算机”怎么办?
原标题:电脑提示“配置Windows Update请勿关闭计算机”怎么办?win7系统中在开机与关闭的时候总是显示“配置windows update请勿关闭计算机”相信有不少朋友都曾遇到过一次两次还能忍但经常遇到就叫人感到心烦了遇到这种问题怎么办呢?一般的方…...
2022/11/19 21:17:08 - 计算机正在配置无法关机,关机提示 windows7 正在配置windows 请勿关闭计算机 ,然后等了一晚上也没有关掉。现在电脑无法正常关机...
关机提示 windows7 正在配置windows 请勿关闭计算机 ,然后等了一晚上也没有关掉。现在电脑无法正常关机以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!关机提示 windows7 正在配…...
2022/11/19 21:17:05 - 钉钉提示请勿通过开发者调试模式_钉钉请勿通过开发者调试模式是真的吗好不好用...
钉钉请勿通过开发者调试模式是真的吗好不好用 更新时间:2020-04-20 22:24:19 浏览次数:729次 区域: 南阳 > 卧龙 列举网提醒您:为保障您的权益,请不要提前支付任何费用! 虚拟位置外设器!!轨迹模拟&虚拟位置外设神器 专业用于:钉钉,外勤365,红圈通,企业微信和…...
2022/11/19 21:17:05 - 配置失败还原请勿关闭计算机怎么办,win7系统出现“配置windows update失败 还原更改 请勿关闭计算机”,长时间没反应,无法进入系统的解决方案...
前几天班里有位学生电脑(windows 7系统)出问题了,具体表现是开机时一直停留在“配置windows update失败 还原更改 请勿关闭计算机”这个界面,长时间没反应,无法进入系统。这个问题原来帮其他同学也解决过,网上搜了不少资料&#x…...
2022/11/19 21:17:04 - 一个电脑无法关闭计算机你应该怎么办,电脑显示“清理请勿关闭计算机”怎么办?...
本文为你提供了3个有效解决电脑显示“清理请勿关闭计算机”问题的方法,并在最后教给你1种保护系统安全的好方法,一起来看看!电脑出现“清理请勿关闭计算机”在Windows 7(SP1)和Windows Server 2008 R2 SP1中,添加了1个新功能在“磁…...
2022/11/19 21:17:03 - 请勿关闭计算机还原更改要多久,电脑显示:配置windows更新失败,正在还原更改,请勿关闭计算机怎么办...
许多用户在长期不使用电脑的时候,开启电脑发现电脑显示:配置windows更新失败,正在还原更改,请勿关闭计算机。。.这要怎么办呢?下面小编就带着大家一起看看吧!如果能够正常进入系统,建议您暂时移…...
2022/11/19 21:17:02 - 还原更改请勿关闭计算机 要多久,配置windows update失败 还原更改 请勿关闭计算机,电脑开机后一直显示以...
配置windows update失败 还原更改 请勿关闭计算机,电脑开机后一直显示以以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!配置windows update失败 还原更改 请勿关闭计算机&#x…...
2022/11/19 21:17:01 - 电脑配置中请勿关闭计算机怎么办,准备配置windows请勿关闭计算机一直显示怎么办【图解】...
不知道大家有没有遇到过这样的一个问题,就是我们的win7系统在关机的时候,总是喜欢显示“准备配置windows,请勿关机”这样的一个页面,没有什么大碍,但是如果一直等着的话就要两个小时甚至更久都关不了机,非常…...
2022/11/19 21:17:00 - 正在准备配置请勿关闭计算机,正在准备配置windows请勿关闭计算机时间长了解决教程...
当电脑出现正在准备配置windows请勿关闭计算机时,一般是您正对windows进行升级,但是这个要是长时间没有反应,我们不能再傻等下去了。可能是电脑出了别的问题了,来看看教程的说法。正在准备配置windows请勿关闭计算机时间长了方法一…...
2022/11/19 21:16:59 - 配置失败还原请勿关闭计算机,配置Windows Update失败,还原更改请勿关闭计算机...
我们使用电脑的过程中有时会遇到这种情况,当我们打开电脑之后,发现一直停留在一个界面:“配置Windows Update失败,还原更改请勿关闭计算机”,等了许久还是无法进入系统。如果我们遇到此类问题应该如何解决呢࿰…...
2022/11/19 21:16:58 - 如何在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