96SEO 2026-02-20 10:39 0
是计算机中的程序关于某数据集合上的一次运行活动是系统进行资源分配和调度的基本单位是操作系统结构的基础。

在当代面向线程设计的计算机结构中进程是线程的容器。
程序是指令、数据及其组织形式的描述进程是程序的实体。
是计算机中的程序关于某数据集合上的一次运行活动是系统进行资源分配和调度的基本单位是操作系统结构的基础。
程序是指令、数据及其组织形式的描述进程是程序的实体。
是操作系统能够进行运算调度的最小单位。
它被包含在进程之中是进程中的实际运作单位。
一条线程指的是进程中一个单一顺序的控制流
进程指在系统中正在运行的一个应用程序程序一旦运行就是进程进程—
线程系统分配处理器时间资源的基本单元或者说进程之内独立执行的一个单元执行流。
线程——程序执行的最小单位。
{NEW,RUNNABLE,BLOCKED,WAITING,TIMED_WAITING,TERMINATED;}Java
WAITING等待状态表示该线程需要等待其他线程做出一些特定动作通知或中断。
TIME_WAITING超时等待状态可以在指定的时间后自行返回而不是像
现在的时分time-sharing多任务multi-task操作系统架构通常都是用所谓的“时间分片time
slice”方式进行抢占式preemptive轮转调度round-robin
秒这一量级时间片用后就要被切换下来放入调度队列的末尾等待再次调度。
也即回到
状态。
进入等待状态的线程需要依靠其他线程的通知才能够返回到运行状态。
通常被用于线程间交互/通信sleep()通常被用于暂停执行。
wait()
方法。
sleep()方法执行完成后线程会自动苏醒或者也可以使用
是让获得对象锁的线程实现等待会自动释放当前线程占有的对象锁。
每个对象Object都拥有对象锁既然要释放当前线程占有的对象锁并让其进入
状态自然是要操作对应的对象Object而非当前的线程Thread。
串行表示所有任务都一一按先后顺序进行。
串行意味着必须先装完一车柴才能
运送这车柴只有运送到了才能卸下这车柴并且只有完成了这整个三个步
并行意味着可以同时取得多个任务并同时去执行所取得的这些任务。
并行模
式相当于将长长的一条队列划分成了多条短队列所以并行缩短了任务队列的长度。
并行的效率从代码层次上强依赖于多进程/多线程代码从硬件角度上
并发(concurrent)指的是多个程序可以同时运行的现象更细化的是多进程可
以同时运行或者多指令可以同时运行。
但这不是重点在描述并发的时候也不
来说同一时刻只能运行一个线程。
所以这里的同时运行表示的不是真的同一时刻有多个线程运行的现象这是并行的概念而是提供一种功能让用户看来多个程序同
由于操作系统对进程的调度是随机的所以切分成多个小任务后可能会从任一小任务处执行。
这可能会出现一些现象
可能出现一个小任务执行了多次还没开始下个任务的情况。
这时一般会采用队列或类似的数据结构来存放各个小任务的成果
可能出现还没准备好第一步就执行第二步的可能。
这时一般采用多路复用或异步的方式比如只有准备好产生了事件通知才执行某个任务。
可以多进程/多线程的方式并行执行这些小任务。
也可以单进程/单线程执行这
内执行。
单核CPU根据时间片快速切换线程并行两个及两个以上的作业在同一
管程(monitor)是保证了同一时刻只有一个进程在管程内活动,即管程内定义的操作在同一时刻只被一个进程调用(由编译器实现)。
但是这样并不能保证进程以设计的顺序执行。
中同步是基于进入和退出管程(monitor)对象实现的每个对象都会有一个管程
执行线程首先要持有管程对象然后才能执行方法当方法完成之后会释放管程方法在执行时候会持有管程其他线程无法再获取同一个管程。
通过thread.setDaemon(true)设置为守护线程,默认用户线程。
调用
修饰一个代码块被修饰的代码块称为同步语句块其作用的范围是大括号{}
修饰一个方法被修饰的方法称为同步方法其作用的范围是整个方法作用的对象是调用这个方法的对象
关键字而在子类中覆盖了这个方法在子类中的这个方法默认情况下并不是同步的而必须显式地在子类的这个方法中加上synchronized
关键字才可以。
当然还可以在子类方法中调用父类中相应的方法这样虽然子类中的方法不是同步的但子类调用了父类的同步方法因此子类的方法也就相当于同步了。
修改一个静态的方法其作用的范围是整个静态方法作用的对象是这个类的所有对象
System.out.println(Thread.currentThread().getName()
修饰了当一个线程获取了对应的锁并执行该代码块时其他线程便只能一直等待等待获取锁的线程释放锁而这里获取锁的线程释放锁只会有两种情况
方法被阻塞了但是又没有释放锁其他线程便只能干巴巴地等待试想一下这多么影响程序执行效率。
因此就需要有一种机制可以不让等待的线程一直无期限地等待下去比如只等待一定的时间或者能够响应中断通过
锁实现提供了比使用同步方法和语句可以获得的更广泛的锁操作。
它们允许更灵活的结构可能具有非常不同的属性并且可能支持多个关联的条件对象。
Lock
lock()方法是平常使用得最多的一个方法就是用来获取锁。
如果锁已被其他线程获取则进行等待。
Lock必须主动去释放锁并且在发生异常时不会自动释放锁。
因此一般来说使用
wait()/notify()这两个方法一起使用可以实现等待/通知模式
await()会使当前线程等待,同时会释放锁,当其他线程调用signal()时,线程会重新获得锁并继续执行。
signal()用于唤醒一个等待的线程。
ReentrantLock意思是“可重入锁”关于可重入锁的概念将在后面讲述。
{test.insert(Thread.currentThread());}}.start();new
{test.insert(Thread.currentThread());}}.start();}public
{System.out.println(thread.getName()
{System.out.println(thread.getName()
}一个用来获取读锁一个用来获取写锁。
也就是说将文件的读写操作分开分成
newReentrantReadWriteLock();public
{test.get(Thread.currentThread());}}.start();new
{test.get(Thread.currentThread());}}.start();}public
System.currentTimeMillis();while
{System.out.println(thread.getName()
正在进行读操作);}System.out.println(thread.getName()
newReentrantReadWriteLock();public
{test.get(Thread.currentThread());};}.start();new
{test.get(Thread.currentThread());}}.start();}public
System.currentTimeMillis();while
{System.out.println(thread.getName()
正在进行读操作);}System.out.println(thread.getName()
如果有一个线程已经占用了读锁则此时其他线程如果要申请写锁则申请写锁的线程会一直等待释放读锁。
如果有一个线程已经占用了写锁则此时其他线程如果申请写锁或者读锁则
可以提高多个线程进行读操作的效率。
synchronized底层采用的是objectMonitor,lock采用的AQS;synchronized只支持非公平锁,lock支持非公平锁和公平锁;synchronized使用了object类的wait和notify进行等待和唤醒,
lock使用了condition接口进行等待和唤醒(await和signal);
在性能上来说如果竞争资源不激烈两者的性能是差不多的而当竞争资源
线程间通信的模型有两种共享内存和消息传递以下方式都是基本这两种模型来实现的。
我们来基本一道面试常见的题目来分析
0){this.wait();}number;System.out.println(--------
Thread.currentThread().getName()
0){this.wait();}number--;System.out.println(--------
Thread.currentThread().getName()
0){condition.await();}number;System.out.println(--------
Thread.currentThread().getName()
number);condition.signalAll();}catch
e){e.printStackTrace();}finally
0){condition.await();}number--;System.out.println(--------
Thread.currentThread().getName()
number);condition.signalAll();}catch
e){e.printStackTrace();}finally
{conditionA.await();}System.out.println(Thread.currentThread().getName()
{conditionB.await();}System.out.println(Thread.currentThread().getName()
{conditionC.await();}System.out.println(Thread.currentThread().getName()
{System.out.println(C);}System.out.println(-----------------------------------------);//开始打印
{list.add(UUID.randomUUID().toString());System.out.println(list);},
java.util.ConcurrentModificationException
List所以它是一个队列支持相关的添加、删除、修改、遍历等功能。
{list.add(UUID.randomUUID().toString());System.out.println(list);},
{modCount;ensureCapacityHelper(elementCount
Collections.synchronizedList(new
-{list.add(UUID.randomUUID().toString());System.out.println(list);},
SynchronizedRandomAccessList(list)
大小通常保持很小只读操作远多于可变操作需要在遍历期间防止线程间的冲突。
因为通常需要复制整个基础数组所以可变操作add()、set()
使用迭代器进行遍历的速度很快并且不会与其他线程发生冲突。
在构造迭代
当我们往一个容器添加元素的时候不直接往当前容器添加而是先将当前容器进行
Copy复制出一个新的容器然后新的容器里添加元素添加完元素之后再将原容器的引用指向新的容器。
这时候会抛出来一个新的问题也就是数据不一致的问题。
如果写线程还没来得及写会内存其他的线程就会读到了脏数据。
Collections.synchronizedList(new
-{list.add(UUID.randomUUID().toString());System.out.println(list);},
时都会新建一个数组并将更新后的数据拷贝到新建的数组中最后再将该
的原因由于它在“添加/修改/删除”数据时都会新建数组所以涉及到修改数据的
提供了“读取到的数据总是最新的”这个机制的保证。
通过互斥锁来保护数据。
在“添加/修改/删除”数据时会先“获取互斥锁”
内部通过链表、数组等方式实现了这个接口。
表示阻塞队列非常适合用于作为数据共享的通道。
公平锁多个线程按照申请锁的顺序去获得锁线程会直接进入队列去排队永远都是队列的第一位才能得到锁。
优点所有的线程都能得到资源不会饿死在队列中。
缺点吞吐量会下降很多队列里面除了第一个线程其他的线程都会阻塞cpu唤醒阻塞线程的开销会很大。
非公平锁多个线程去获取锁的时候会直接去尝试获取获取不到再去进入等待队列如果能获取到就直接获取到锁。
优点可以减少CPU唤醒线程的开销整体的吞吐效率会高点CPU也不必取唤醒所有线程会减少唤起线程的数量。
缺点你们可能也发现了这样可能导致队列中间的线程一直获取不到锁或者长时间获取不到锁导致饿死。
就是一个线程不用释放可以重复的获取同一个锁n次只是在释放的时候也需要相应的释放n次。
所谓死锁是指多个进程在运行过程中因争夺同一资源而造成的一种僵局当进程处于这种僵持状态时若无外力作用它们都将无法再向前推进。
因此我们举个例子来描述如果此时有一个线程A按照先锁a再获得锁b的的顺序获得锁而在此同时又有另外一个线程B按照先锁b再锁a的顺序获得锁。
如下代码所示
{System.out.println(Thread.currentThread().getName()
{e.printStackTrace();}synchronized
{System.out.println(Thread.currentThread().getName()
{System.out.println(Thread.currentThread().getName()
{e.printStackTrace();}synchronized
{System.out.println(Thread.currentThread().getName()
互斥条件进程要求对所分配的资源进行排它性控制即在一段时间内某资源仅为一进程所占用。
请求和保持条件当进程因请求资源而阻塞时对已获得的资源保持不放。
不剥夺条件进程已获得的资源在未使用完之前不能剥夺只能在使用完时由自己释放。
环路等待条件在发生死锁时必然存在一个进程–资源的环形链。
资源一次性分配一次性分配所有资源这样就不会再有请求了破坏请求条件只要有一个资源得不到分配也不给这个进程分配其他的资源破坏请保持条件可剥夺资源即当某进程获得了部分资源但得不到其它资源则释放已占有的资源破坏不可剥夺条件资源有序分配法系统给每类资源赋予一个编号每一个进程按编号递增的顺序请求资源释放则相反破坏环路等待条件
call()方法完成时结果必须存储在主线程已知的对象中以便主线程可
InterruptedExceptionExecutionException用于获取任务的结果。
在主线程中需要执行比较耗时的操作时但又不想阻塞主线程时可以把这些作业交给
多用于耗时的计算主线程可以在完成自己的任务后再去获取结果。
仅在计算完成时才能检索结果如果计算尚未完成则阻塞
方法而获取结果只有在计算完成时获取否则会一直阻塞直到任务转入完
{System.out.println(Thread.currentThread().getName()
{System.out.println(Thread.currentThread().getName()
call方法,开始准备睡觉);Thread.sleep(1000);System.out.println(Thread.currentThread().getName()
System.currentTimeMillis();}}public
MyThread2();//future-callableFutureTaskLong
futureTask2.get();System.out.println(result1);}//线程一new
在主线程中需要执行比较耗时的操作时但又不想阻塞主线程时可以把这些作业交给
多用于耗时的计算主线程可以在完成自己的任务后再去获取结果仅在计算完成时才能检索结果如果计算尚未完成则阻塞
时获取否则会一直阻塞直到任务转入完成状态然后会返回结果或者抛出异常。
只计算一次
中提供了三种常用的辅助类通过这些辅助类可以很好的解决线程数量过
{//创建CountDownLatch对象设置初始值CountDownLatch
CountDownLatch(6);//6个同学陆续离开教室之后for
Thread(()-{System.out.println(Thread.currentThread().getName()
-1countDownLatch.countDown();},String.valueOf(i)).start();}//等待countDownLatch.await();System.out.println(Thread.currentThread().getName()
{//创建CyclicBarrierCyclicBarrier
CyclicBarrier(NUMBER,()-{System.out.println(*****集齐7颗龙珠就可以召唤神龙);});//集齐七颗龙珠过程for
{System.out.println(Thread.currentThread().getName()
星龙被收集到了);//等待cyclicBarrier.await();}
{e.printStackTrace();}},String.valueOf(i)).start();}}
了表示这是这一代最后一个线程到达栅栏就尝试执行我们构造方法中输入的任务。
IllegalArgumentException();this.parties
就代表了有拦截的线程的数量当拦截的线程数量达到这个值的时候就打开栅栏让所有线程通过。
方法就像树立起一个栅栏的行为一样将线程挡住了当拦住的线程数量达到
BrokenBarrierException,TimeoutException
之后说明最后一个线程已经到达栅栏了也就是达到了可以执行await
(!ranAction)breakBarrier();}}//
execution.Thread.currentThread().interrupt();}}if
的构造方法中传入的第一个参数是最大信号量可以看成最大线程池每个信号量初始化为一个最多只能分发一个许可证。
使用
{//抢占semaphore.acquire();System.out.println(Thread.currentThread().getName()
抢到了车位);//设置随机停车时间TimeUnit.SECONDS.sleep(new
Random().nextInt(5));System.out.println(Thread.currentThread().getName()
{//释放车位semaphore.release();}},String.valueOf(i)).start();}}
{sync.acquireSharedInterruptibly(1);
共享模式下获取许可证获取成功则返回失败则加入阻塞队列挂起线程*/
尝试获取许可证arg为获取许可证个数当可用许可证数减当前获取的许可证数结果小于0,则创建一个节点加入阻塞队列挂起当前线程。
if
0)doAcquireSharedInterruptibly(arg);
statestate1。
释放许可证成功之后同时会唤醒同步队列中的一个线程。
被唤醒的线程会重新尝试去修改
{//唤醒同步队列中的一个线程doReleaseShared();return
现实中有这样一种场景对共享资源有读和写的操作且写操作没有读操作那么频繁。
在没有写操作的时候多个线程同时读一个资源没有任何问题所以
应该允许多个线程同时读取共享资源但是如果一个线程想去写这些共享资源
平优于公平。
2重进入读锁和写锁都支持线程重进入。
3锁降级遵循获取写锁、获取读锁再释放写锁的次序写锁能够降级成为读锁。
ReentrantReadWriteLock.ReadLock
ReentrantReadWriteLock.WriteLock
ReentrantReadWriteLock.WriteLock
ReentrantReadWriteLock.ReadLock
ReentrantReadWriteLock();//放数据public
{//添加写锁rwLock.writeLock().lock();try
{System.out.println(Thread.currentThread().getName()
正在写操作key);//暂停一会TimeUnit.MICROSECONDS.sleep(300);//放数据map.put(key,value);System.out.println(Thread.currentThread().getName()
{//释放写锁rwLock.writeLock().unlock();}}//取数据public
{//添加读锁rwLock.readLock().lock();Object
{System.out.println(Thread.currentThread().getName()
正在读取操作key);//暂停一会TimeUnit.MICROSECONDS.sleep(300);result
map.get(key);System.out.println(Thread.currentThread().getName()
{//释放读锁rwLock.readLock().unlock();}return
Thread(()-{myCache.put(num,num);},String.valueOf(i)).start();}TimeUnit.MICROSECONDS.sleep(300);//创建线程取数据for
Thread(()-{myCache.get(num);},String.valueOf(i)).start();}}
ReentrantReadWriteLock();private
readWriteLock.writeLock();private
readWriteLock.readLock();private
test(){readLock.lock();if(!updateFlag){readLock.unlock();//肯定要先开释了读锁再去获取写锁如果间接获取写锁以后线程会被阻塞writeLock.lock();//step1,获取写锁try
{if(!updateFlag){//批改数据逻辑略。
updateFlag
true;}readLock.lock();//step2,获取读锁排挤其它写锁批改数据}finally
{writeLock.unlock();//step3,开释写锁。
到这里锁降级实现}}try
{//如果step2不先获取读锁在step3开释了写锁后其它线程会对数据进行批改//会使得上面‘读取数据逻辑’里呈现数据读取不精确的问题//读取数据逻辑略。
}finally
在线程持有读锁的情况下该线程不能取得写锁(因为获取写锁的时候如果发
现当前的读锁被占用就马上获取失败不管读锁是不是被当前线程持有)。
在线程持有写锁的情况下该线程可以继续获取读锁获取读锁时如果发现写锁被占用只有写锁没有被当前线程占用的情况才会获取失败。
当线程获取读锁的时候可能有其他线程同时也在持有读锁因此不能把获取读锁的线程“升级”为写锁而对于获得写锁的线程它一定独占了读写
锁因此可以继续让它获取读锁当它同时获取了写锁和读锁后还可以先释放写锁继续持有读锁这样一个写锁就“降级”为了读锁。
“传输”数据的问题。
通过这些高效并且线程安全的队列类为我们快速搭建
试图从空的队列中获取元素的线程将会被阻塞直到其他线程往空的队列插入新的元素
试图向已满的队列中添加新元素的线程将会被阻塞直到其他线程从队列中移除一个或多
先进先出FIFO先插入的队列的元素也最先出队列类似于排队的功能。
从某种程度上来说这种队列也体现了一种公平性后进先出LIFO后插入队列的元素最先出队列这种队列优先处理最近发
在多线程领域所谓阻塞在某些情况下会挂起线程即阻塞一旦条件满足被挂起的线程又会自动被唤起
好处是我们不需要关心什么时候需要阻塞线程什么时候需要唤醒线程因为这一切
节尤其还要兼顾效率和线程安全而这会给我们的程序带来不小的复杂度。
多线程环境中通过队列可以很容易实现数据共享比如经典的“生产者”和
“消费者”模型中通过队列可以很便利地实现两者之间的数据共享。
假设我们有若干生产者线程另外又有若干个消费者线程。
如果生产者线程需要把准
备好的数据共享给消费者线程利用队列的方式来传递数据就可以很方便地解决他们之间的数据共享问题。
但如果生产者和消费者在某个时间段内万一
发生数据处理速度不匹配的情况呢理想情况下如果生产者产出数据的速度大于消费者消费的速度并且当生产出来的数据累积到一定程度的时候那么生产者必须暂停等待一下阻塞生产者线程以便等待消费者线程把累积的
直到有数据放入队列当队列中填满数据的情况下生产者端的所有线程都会被自动阻塞挂起
unit)可以设定等待的时间如果在指定的时间内还不能往队列中加入
BlockingQueue则返回失败put(anObject):把
如果在指定时间内队列一旦有数据可取则立即返回队列中的数据。
否则知
System.out.println(blockingQueue.add(a));System.out.println(blockingQueue.add(b));System.out.println(blockingQueue.add(c));//System.out.println(blockingQueue.element());//System.out.println(blockingQueue.add(w));System.out.println(blockingQueue.remove());System.out.println(blockingQueue.remove());System.out.println(blockingQueue.remove());System.out.println(blockingQueue.remove());//第二组
System.out.println(blockingQueue.offer(a));
System.out.println(blockingQueue.offer(b));
System.out.println(blockingQueue.offer(c));
System.out.println(blockingQueue.offer(www));
System.out.println(blockingQueue.poll());
System.out.println(blockingQueue.poll());
System.out.println(blockingQueue.poll());
System.out.println(blockingQueue.poll());//第三组
System.out.println(blockingQueue.take());
System.out.println(blockingQueue.take());
System.out.println(blockingQueue.take());
System.out.println(blockingQueue.take());//第四组System.out.println(blockingQueue.offer(a));System.out.println(blockingQueue.offer(b));System.out.println(blockingQueue.offer(c));System.out.println(blockingQueue.offer(w,3L,
内部维护了一个定长数组以便缓存队列中的数据对象这是一个常用的阻塞队列除了一个定长数组外ArrayBlockingQueue
内部还保存着两个整形变量分别标识着队列的头部和尾部在数组中的位置。
在生产者放入数据和消费者获取数据都是共用同一个锁对象由此也意味着两者无法真正并行运行这点尤其不同于
LinkedBlockingQueue按照实现原理来分析ArrayBlockingQueue
完全可以采用分离锁从而实现生产者和消费者操作的完全并行运行。
Doug
的数据写入和获取操作已经足够轻巧以至于引入独立的锁机制除了给代码带来额外的复杂性外其在性能上完全占不到任何便宜。
间还有一个明显的不同之处在于前者在插入或删除元素时不会产生或销毁任何额外的对象实例而后者则会生成一个额外的
类似其内部也维持着一个数据缓冲队列该队列由一个链表构成当生产者往队列中放入一个数据时队列会从生产者手中获取数据并缓存在队列内部而生产者立即返回
只有当队列缓冲区达到最大值缓存容量时LinkedBlockingQueue
可以通过构造函数指定该值才会阻塞生产者队列直到消费者从队列中消费掉一份数据生产者线程会被唤醒反之对于消费者这端的处理也基于同样的原理。
之所以能够高效的处理并发数据还因为其对于生产者端和消费者端分别采用了独立的锁来控制数据同步这也意味着在高并发的情况下生产者和消费者可以并行地操作队列中的数据以此来提高整个队列
的阻塞队列一般情况下在处理多线程间的生产者消费者问题使用这两个
中的元素只有当其指定的延迟时间到了才能够从队列中获取到该元素。
DelayQueue
是一个没有大小限制的队列因此往队列中插入数据的操作生产者永远不会被阻塞而只有获取数据的操作消费者才会被阻塞。
并不会阻塞数据生产者而只会在没有可消费的数据时阻塞数据的消费者。
因此使用的时候要特别注意生产者生产数据的速度绝对不能快于消费者消费
一种无缓冲的等待队列类似于无中介的直接交易有点像原始社会中的生产者和消费者生产者拿着产品去集市销售给产品的最终消费者而消费者必须亲自去集市找到所要商品的直接生产者如果一方没有找到合适的目标那么对不起大家都在集市等待。
相对于有缓冲的
来说少了一个中间经销商的环节缓冲区如果有经销商生产者直接把产品批发给经销商而无需在意经销商最终会将这些产品卖给那些消费者由于经销商可以库存一部分商品因此相对于直接交易模式总体来说采用中间经销商的模式
会吞吐量高一些可以批量买卖但另一方面又因为经销商的引入使得产品从生产者到消费者中间增加了额外的交易环节单个产品的及时响应性能可能会降低。
队列来阻塞多余的生产者和消费者从而体系整体的公平策略非公平模式SynchronousQueue
如果生产者和消费者的处理速度有差距则很容易出现饥渴的情况即可能有某些生产者或者是消费者的数据永远都得不到处理。
队列。
相对于其他阻塞队列LinkedTransferQueue
采用一种预占模式。
意思就是消费者线程取元素时如果队列不为空则直接取走数据若队列为空那就生成一个节点节点元素
null入队然后消费者线程被等待在这个节点上后面生产者线程入队时发现有一个元素为
的节点生产者线程就不入队了直接就将元素填充到该节点并唤醒该节点等待的线程被唤醒的消费者线程取走元素从调用的方法返回。
是一个由链表结构组成的双向阻塞队列即可以从队列的两端插入和移除元素。
对于一些指定的操作在插入或者获取队列元素时如果队列状态不允许该操作可能会阻塞住该线程直到队列状态变更为允许操作这里的阻塞一般有两种情况。
如果当前队列已满将会进入阻塞状态一直等到队列有空的位置时再将该元素插入该操作可以通过设置超时参数超时后返回
所谓阻塞在某些情况下会挂起线程即阻塞一旦条件满足被挂起的线程又会自动被唤起。
为什么需要
我们每个程序员都必须去自己控制这些细节尤其还要兼顾效率和线程安全
而这会给我们的程序带来不小的复杂度。
使用后我们不需要关心什么时候需要阻塞线程什么时候需要唤醒线程因为这一切
进而影响缓存局部性和整体性能。
而线程池维护着多个线程等待着监督管理者分配可并发执行的任务。
这避免了在处理短时间任务时创建与销毁线程的代
降低资源消耗。
通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
提高响应速度。
当任务到达时任务可以不需要等到线程创建就能立即执行。
提高线程的可管理性。
线程是稀缺资源如果无限制的创建不仅会消耗系统资源还会降低系统的稳定性使用线程池可以进行统一的分配调优和监控。
ExecutorServiceThreadPoolExecutor
用给定的初始参数创建一个新的ThreadPoolExecutor。
*/public
maximumPoolSize,//线程池的最大线程数long
keepAliveTime,//当线程数大于核心线程数时多余的空闲线程存活的最长时间TimeUnit
unit,//时间单位BlockingQueueRunnable
workQueue,//任务队列用来储存等待执行任务的队列ThreadFactory
threadFactory,//线程工厂用来创建线程一般默认即可RejectedExecutionHandler
handler//拒绝策略当提交的任务过多而不能及时处理时我们可以定制策略来处理任务)
NullPointerException();this.corePoolSize
corePoolSize;this.maximumPoolSize
unit.toNanos(keepAliveTime);this.threadFactory
当队列中存放的任务达到队列容量的时候当前可以同时运行的线程数量变为最大线程数。
当新任务来的时候会先判断当前运行的线程数量是否达到核心线程数如果达到的话新任务就会被存放在队列中。
的时候如果这时没有新的任务提交核心线程外的线程不会立即销毁而是会等待直到等待的时间超过了
如果当前同时运行的线程数量达到最大线程数量并且队列也已经被放满了任务时ThreadPoolTaskExecutor
RejectedExecutionException来拒绝新任务的处理。
ThreadPoolExecutor.CallerRunsPolicy
调用执行自己的线程运行任务也就是直接在调用execute方法的线程中运行(run)被拒绝的任务如果执行程序已关闭则会丢弃该任务。
因此这种策略会降低对于新任务提交速度影响程序的整体性能。
如果您的应用程序可以承受此延迟并且你要求任何一个任务请求都要被执行的话你可以选择这个策略。
ThreadPoolExecutor.DiscardPolicy
ThreadPoolExecutor.DiscardOldestPolicy
ThreadPoolExecutor.AbortPolicy。
在默认情况下ThreadPoolExecutor
ThreadPoolExecutor.CallerRunsPolicy。
当最大池被填满时此策略为我们提供可伸缩队列。
这个直接查看
MAX_VALUE线程池中的线程可进行缓存重复利用和回收回收默认时间为
ThreadPoolExecutor(0,Integer.MAX_VALUE,60L,TimeUnit.SECONDS,new
SynchronousQueue(),Executors.defaultThreadFactory(),new
ThreadPoolExecutor.AbortPolicy());
作用创建一个可重用固定线程数的线程池以共享的无界队列方式来运行这些线程。
在任意点在大多数线程会处于处理任务的活动状态。
如果在所有线程处于活动状态时提交附加任务则在有可用线程之前附加任务将在队列中等待。
如果在关闭前的执行期间由于失败而导致任何线程终止那么一个新线程将代替它执行后续的任务如果需要。
在某个线程被显式地关闭之前池
线程池中的线程处于一定的量可以很好的控制线程的并发量线程可以重复被使用在显示关闭之前都将一直存在超出一定量的线程被提交时候需在队列中等待
ThreadPoolExecutor(10,10,0L,TimeUnit.SECONDS,new
LinkedBlockingQueue(),Executors.defaultThreadFactory(),new
ThreadPoolExecutor.AbortPolicy());
Executor以无界队列方式来运行该线程。
注意如果因为在关闭前的执行期间出现失败而终止了此单个线程
那么如果需要一个新线程将代替它执行后续的任务。
可保证顺序地执行各个任务并且在任意给定的时间不会有多个线程是活动的。
与其他等效的newFixedThreadPool
不同可保证无需重新配置此方法所返回的执行程序即可使用其他的线程。
ThreadPoolExecutor(1,1,0L,TimeUnit.SECONDS,new
LinkedBlockingQueue(),Executors.defaultThreadFactory(),new
ThreadPoolExecutor.AbortPolicy());
适用于需要保证顺序执行各个任务并且在任意时间点不会同时有多个线程的场景
ScheduledThreadPoolExecutor(corePoolSize,
ForkJoinPool(parallelism,ForkJoinPool.defaultForkJoinWorkerThreadFactory,null,true);
ThreadPoolExecutor(3,3,60L,TimeUnit.SECONDS,new
LinkedBlockingQueue(),Executors.defaultThreadFactory(),
ThreadPoolExecutor.DiscardOldestPolicy());try
{System.out.println(Thread.currentThread().getName()
窗口,开始卖票);Thread.sleep(5000);System.out.println(Thread.currentThread().getName()
{//完成后结束threadService.shutdown();}}
maximumPoolSize那么线程池会启动饱和拒绝策略来执行。
当一个线程无事可做超过一定的时间keepAliveTime时线程会判断
项目中创建多线程时使用常见的三种线程池创建方式单一、可变、定长都有一定问题原因是
分别从双端队列里获取任务执行。
子任务执行完的结果都放在另外一个队列里
RecursiveAction用于没有返回结果的任务RecursiveTask:用于有返回结果的任务
ForkJoinWorkerThread)((ForkJoinWorkerThread)
t).workQueue.push(this);elseForkJoinPool.common.externalPush(this);return
signalWork()方法唤醒或创建一个工作线程来执行任务。
代码如下
visibilityU.putOrderedObject(a,
null)p.signalWork(p.workQueues,
NORMAL)reportException(s);return
已完成NORMAL、被取消CANCELLED、信号SIGNAL和出
如果任务状态是已完成则直接返回任务结果。
如果任务状态是被取消则直接抛出
CancellationException如果任务状态是抛出异常则直接抛出对应的异常
(ForkJoinWorkerThread)t).workQueue).tryUnpush(this)
setExceptionalCompletion(rex);}if
首先通过查看任务的状态看任务是否已经执行完成如果执行完成则直接
返回任务状态如果没有执行完则从任务数组里取出任务并执行。
如果任务顺利执行完成则设置任务状态为
CancellationException。
如果任务没有完成或者没有抛出异常则返回
end;}//拆分和合并过程Overrideprotected
{//判断相加两个数值是否大于10if((end-begin)VALUE)
MyTask(begin,middle);//拆分右边MyTask
MyTask(middle1,end);//调用方法拆分task01.fork();task02.fork();//合并结果result
task01.join()task02.join();}return
MyTask(0,100);//创建分支合并池对象ForkJoinPool
ForkJoinPool();ForkJoinTaskInteger
forkJoinPool.submit(myTask);//获取最终合并之后结果Integer
forkJoinTask.get();System.out.println(result);//关闭池对象forkJoinPool.shutdown();}
里面被用于异步编程异步通常意味着非阻塞可以使得我们的任务单独运行在与主线程分离的其他线程中并且通过回调可以在主线程中得到异步任务的执行状态是否完成和是否异常等信息。
接口才是异步编程的接口抽象里面定义多种异步方法通过这两者集合从而打造出了强大的CompletableFuture
里面通常用来表示一个异步任务的引用比如我们将任务提交到线程池里面然后我们会得到一个
方法可以一直阻塞直到任务结束然后获取结果但整体来说这种方式还是同步的因为需要客户端不断阻塞等待或者不断轮询才能知道任务是否完成。
我提交了一个任务但是执行太慢了我通过其他路径已经获取到了任务结果
现在没法把这个任务结果通知到正在执行的线程所以必须主动取消或者一直等待它执行完成
方法会一直阻塞到任务完成但是想在获取任务之后执行额外的任务因为
异步神器CompletableFuture让你的代码免受阻塞之苦
作为专业的SEO优化服务提供商,我们致力于通过科学、系统的搜索引擎优化策略,帮助企业在百度、Google等搜索引擎中获得更高的排名和流量。我们的服务涵盖网站结构优化、内容优化、技术SEO和链接建设等多个维度。
| 服务项目 | 基础套餐 | 标准套餐 | 高级定制 |
|---|---|---|---|
| 关键词优化数量 | 10-20个核心词 | 30-50个核心词+长尾词 | 80-150个全方位覆盖 |
| 内容优化 | 基础页面优化 | 全站内容优化+每月5篇原创 | 个性化内容策略+每月15篇原创 |
| 技术SEO | 基本技术检查 | 全面技术优化+移动适配 | 深度技术重构+性能优化 |
| 外链建设 | 每月5-10条 | 每月20-30条高质量外链 | 每月50+条多渠道外链 |
| 数据报告 | 月度基础报告 | 双周详细报告+分析 | 每周深度报告+策略调整 |
| 效果保障 | 3-6个月见效 | 2-4个月见效 | 1-3个月快速见效 |
我们的SEO优化服务遵循科学严谨的流程,确保每一步都基于数据分析和行业最佳实践:
全面检测网站技术问题、内容质量、竞争对手情况,制定个性化优化方案。
基于用户搜索意图和商业目标,制定全面的关键词矩阵和布局策略。
解决网站技术问题,优化网站结构,提升页面速度和移动端体验。
创作高质量原创内容,优化现有页面,建立内容更新机制。
获取高质量外部链接,建立品牌在线影响力,提升网站权威度。
持续监控排名、流量和转化数据,根据效果调整优化策略。
基于我们服务的客户数据统计,平均优化效果如下:
我们坚信,真正的SEO优化不仅仅是追求排名,而是通过提供优质内容、优化用户体验、建立网站权威,最终实现可持续的业务增长。我们的目标是与客户建立长期合作关系,共同成长。
Demand feedback