SEO基础

SEO基础

Products

当前位置:首页 > SEO基础 >

长沙如何建设响应式网站?厦门市同安区建设工程质量安全监督站的官方网站是什么?

96SEO 2026-02-20 10:39 0


java.util

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

长沙如何建设响应式网站?厦门市同安区建设工程质量安全监督站的官方网站是什么?

在当代面向线程设计的计算机结构中进程是线程的容器。

程序是指令、数据及其组织形式的描述进程是程序的实体。

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

程序是指令、数据及其组织形式的描述进程是程序的实体。

线程thread

是操作系统能够进行运算调度的最小单位。

它被包含在进程之中是进程中的实际运作单位。

一条线程指的是进程中一个单一顺序的控制流

总结来说:

进程指在系统中正在运行的一个应用程序程序一旦运行就是进程进程—

线程系统分配处理器时间资源的基本单元或者说进程之内独立执行的一个单元执行流。

线程——程序执行的最小单位。

1.3

{NEW,RUNNABLE,BLOCKED,WAITING,TIMED_WAITING,TERMINATED;}Java

NEW:

WAITING等待状态表示该线程需要等待其他线程做出一些特定动作通知或中断。

TIME_WAITING超时等待状态可以在指定的时间后自行返回而不是像

WAITING

现在的时分time-sharing多任务multi-task操作系统架构通常都是用所谓的“时间分片time

quantum

slice”方式进行抢占式preemptive轮转调度round-robin

CPU

秒这一量级时间片用后就要被切换下来放入调度队列的末尾等待再次调度。

也即回到

ready

状态。

进入等待状态的线程需要依靠其他线程的通知才能够返回到运行状态。

TIMED_WAITING(超时等待)

通常被用于线程间交互/通信sleep()通常被用于暂停执行。

wait()

notify()或者

方法。

sleep()方法执行完成后线程会自动苏醒或者也可以使用

wait(long

是让获得对象锁的线程实现等待会自动释放当前线程占有的对象锁。

每个对象Object都拥有对象锁既然要释放当前线程占有的对象锁并让其进入

WAITING

状态自然是要操作对应的对象Object而非当前的线程Thread。

sleep()

串行表示所有任务都一一按先后顺序进行。

串行意味着必须先装完一车柴才能

运送这车柴只有运送到了才能卸下这车柴并且只有完成了这整个三个步

1.4.2

并行意味着可以同时取得多个任务并同时去执行所取得的这些任务。

并行模

式相当于将长长的一条队列划分成了多条短队列所以并行缩短了任务队列的长度。

并行的效率从代码层次上强依赖于多进程/多线程代码从硬件角度上

则依赖于多核

并发(concurrent)指的是多个程序可以同时运行的现象更细化的是多进程可

以同时运行或者多指令可以同时运行。

但这不是重点在描述并发的时候也不

CPU

来说同一时刻只能运行一个线程。

所以这里的同时运行表示的不是真的同一时刻有多个线程运行的现象这是并行的概念而是提供一种功能让用户看来多个程序同

CPU

由于操作系统对进程的调度是随机的所以切分成多个小任务后可能会从任一小任务处执行。

这可能会出现一些现象

可能出现一个小任务执行了多次还没开始下个任务的情况。

这时一般会采用队列或类似的数据结构来存放各个小任务的成果

可能出现还没准备好第一步就执行第二步的可能。

这时一般采用多路复用或异步的方式比如只有准备好产生了事件通知才执行某个任务。

可以多进程/多线程的方式并行执行这些小任务。

也可以单进程/单线程执行这

1.4.4

内执行。

单核CPU根据时间片快速切换线程并行两个及两个以上的作业在同一

同时/同一时刻

管程(monitor)是保证了同一时刻只有一个进程在管程内活动,即管程内定义的操作在同一时刻只被一个进程调用(由编译器实现)。

但是这样并不能保证进程以设计的顺序执行。

JVM

中同步是基于进入和退出管程(monitor)对象实现的每个对象都会有一个管程

java

执行线程首先要持有管程对象然后才能执行方法当方法完成之后会释放管程方法在执行时候会持有管程其他线程无法再获取同一个管程。

1.6

通过thread.setDaemon(true)设置为守护线程,默认用户线程。

调用

start之后

修饰一个代码块被修饰的代码块称为同步语句块其作用的范围是大括号{}

修饰一个方法被修饰的方法称为同步方法其作用的范围是整个方法作用的对象是调用这个方法的对象

synchronized

关键字而在子类中覆盖了这个方法在子类中的这个方法默认情况下并不是同步的而必须显式地在子类的这个方法中加上synchronized

关键字才可以。

当然还可以在子类方法中调用父类中相应的方法这样虽然子类中的方法不是同步的但子类调用了父类的同步方法因此子类的方法也就相当于同步了。

修改一个静态的方法其作用的范围是整个静态方法作用的对象是这个类的所有对象

synchronized

System.out.println(Thread.currentThread().getName()

(number--)

修饰了当一个线程获取了对应的锁并执行该代码块时其他线程便只能一直等待等待获取锁的线程释放锁而这里获取锁的线程释放锁只会有两种情况

线程执行发生异常此时

方法被阻塞了但是又没有释放锁其他线程便只能干巴巴地等待试想一下这多么影响程序执行效率。

因此就需要有一种机制可以不让等待的线程一直无期限地等待下去比如只等待一定的时间或者能够响应中断通过

Lock

锁实现提供了比使用同步方法和语句可以获得的更广泛的锁操作。

它们允许更灵活的结构可能具有非常不同的属性并且可能支持多个关联的条件对象。

Lock

synchronized

lock()方法是平常使用得最多的一个方法就是用来获取锁。

如果锁已被其他线程获取则进行等待。

Lock必须主动去释放锁并且在发生异常时不会自动释放锁。

因此一般来说使用

Lock

wait()/notify()这两个方法一起使用可以实现等待/通知模式

Lock

await()会使当前线程等待,同时会释放锁,当其他线程调用signal()时,线程会重新获得锁并继续执行。

signal()用于唤醒一个等待的线程。

注意在调用

ReentrantLock意思是“可重入锁”关于可重入锁的概念将在后面讲述。

ReentrantLock

{test.insert(Thread.currentThread());}}.start();new

Thread()

{test.insert(Thread.currentThread());}}.start();}public

void

{System.out.println(thread.getName()

(int

{System.out.println(thread.getName()

}2.4

}一个用来获取读锁一个用来获取写锁。

也就是说将文件的读写操作分开分成

ReadWriteLock

newReentrantReadWriteLock();public

static

{test.get(Thread.currentThread());}}.start();new

Thread()

{test.get(Thread.currentThread());}}.start();}public

synchronized

System.currentTimeMillis();while

start

{System.out.println(thread.getName()

正在进行读操作);}System.out.println(thread.getName()

读操作完毕);}

newReentrantReadWriteLock();public

static

{test.get(Thread.currentThread());};}.start();new

Thread()

{test.get(Thread.currentThread());}}.start();}public

void

System.currentTimeMillis();while

start

{System.out.println(thread.getName()

正在进行读操作);}System.out.println(thread.getName()

读操作完毕);}

如果有一个线程已经占用了读锁则此时其他线程如果要申请写锁则申请写锁的线程会一直等待释放读锁。

如果有一个线程已经占用了写锁则此时其他线程如果申请写锁或者读锁则

2.5

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

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

(Exception

e){e.printStackTrace();}finally

减一*/public

0){condition.await();}number--;System.out.println(--------

Thread.currentThread().getName()

减一成功----------,值为:

number);condition.signalAll();}catch

(Exception

e){e.printStackTrace();}finally

{lock.unlock();}}

{conditionA.await();}System.out.println(Thread.currentThread().getName()

A,第

{conditionB.await();}System.out.println(Thread.currentThread().getName()

B,第

{conditionC.await();}System.out.println(Thread.currentThread().getName()

C,第

{System.out.println(C);}System.out.println(-----------------------------------------);//开始打印

Anumber

{list.add(UUID.randomUUID().toString());System.out.println(list);},

i).start();}}

java.util.ConcurrentModificationException

问题:

List所以它是一个队列支持相关的添加、删除、修改、遍历等功能。

Vector

{list.add(UUID.randomUUID().toString());System.out.println(list);},

i).start();}}

{modCount;ensureCapacityHelper(elementCount

e;return

Collections.synchronizedList(new

ArrayList());for

-{list.add(UUID.randomUUID().toString());System.out.println(list);},

i).start();}}

SynchronizedRandomAccessList(list)

:new

大小通常保持很小只读操作远多于可变操作需要在遍历期间防止线程间的冲突。

因为通常需要复制整个基础数组所以可变操作add()、set()

remove()

使用迭代器进行遍历的速度很快并且不会与其他线程发生冲突。

在构造迭代

独占锁效率低采用读写分离思想解决

当我们往一个容器添加元素的时候不直接往当前容器添加而是先将当前容器进行

Copy复制出一个新的容器然后新的容器里添加元素添加完元素之后再将原容器的引用指向新的容器。

这时候会抛出来一个新的问题也就是数据不一致的问题。

如果写线程还没来得及写会内存其他的线程就会读到了脏数据。

CopyOnWriteArrayList

Collections.synchronizedList(new

ArrayList());for

-{list.add(UUID.randomUUID().toString());System.out.println(list);},

i).start();}}

时都会新建一个数组并将更新后的数据拷贝到新建的数组中最后再将该

数组赋值给“volatile

的原因由于它在“添加/修改/删除”数据时都会新建数组所以涉及到修改数据的

“线程安全”机制

提供了“读取到的数据总是最新的”这个机制的保证。

通过互斥锁来保护数据。

在“添加/修改/删除”数据时会先“获取互斥锁”

数组”中然后再“释放互斥

内部通过链表、数组等方式实现了这个接口。

表示阻塞队列非常适合用于作为数据共享的通道。

多线程锁

公平锁多个线程按照申请锁的顺序去获得锁线程会直接进入队列去排队永远都是队列的第一位才能得到锁。

优点所有的线程都能得到资源不会饿死在队列中。

缺点吞吐量会下降很多队列里面除了第一个线程其他的线程都会阻塞cpu唤醒阻塞线程的开销会很大。

非公平锁多个线程去获取锁的时候会直接去尝试获取获取不到再去进入等待队列如果能获取到就直接获取到锁。

优点可以减少CPU唤醒线程的开销整体的吞吐效率会高点CPU也不必取唤醒所有线程会减少唤起线程的数量。

缺点你们可能也发现了这样可能导致队列中间的线程一直获取不到锁或者长时间获取不到锁导致饿死。

Synchronized

就是一个线程不用释放可以重复的获取同一个锁n次只是在释放的时候也需要相应的释放n次。

Synchronized

所谓死锁是指多个进程在运行过程中因争夺同一资源而造成的一种僵局当进程处于这种僵持状态时若无外力作用它们都将无法再向前推进。

因此我们举个例子来描述如果此时有一个线程A按照先锁a再获得锁b的的顺序获得锁而在此同时又有另外一个线程B按照先锁b再锁a的顺序获得锁。

如下代码所示

public

{System.out.println(Thread.currentThread().getName()

持有锁a试图获取锁b);try

{e.printStackTrace();}synchronized

(b)

{System.out.println(Thread.currentThread().getName()

(b)

{System.out.println(Thread.currentThread().getName()

持有锁b试图获取锁a);try

{e.printStackTrace();}synchronized

(a)

{System.out.println(Thread.currentThread().getName()

互斥条件进程要求对所分配的资源进行排它性控制即在一段时间内某资源仅为一进程所占用。

请求和保持条件当进程因请求资源而阻塞时对已获得的资源保持不放。

不剥夺条件进程已获得的资源在未使用完之前不能剥夺只能在使用完时由自己释放。

环路等待条件在发生死锁时必然存在一个进程–资源的环形链。

预防死锁

资源一次性分配一次性分配所有资源这样就不会再有请求了破坏请求条件只要有一个资源得不到分配也不给这个进程分配其他的资源破坏请保持条件可剥夺资源即当某进程获得了部分资源但得不到其它资源则释放已占有的资源破坏不可剥夺条件资源有序分配法系统给每类资源赋予一个编号每一个进程按编号递增的顺序请求资源释放则相反破坏环路等待条件

CallableFuture

call()方法完成时结果必须存储在主线程已知的对象中以便主线程可

Future

InterruptedExceptionExecutionException用于获取任务的结果。

public

在主线程中需要执行比较耗时的操作时但又不想阻塞主线程时可以把这些作业交给

Future

多用于耗时的计算主线程可以在完成自己的任务后再去获取结果。

仅在计算完成时才能检索结果如果计算尚未完成则阻塞

get

方法而获取结果只有在计算完成时获取否则会一直阻塞直到任务转入完

成状态然后会返回结果或者抛出异常get

{System.out.println(Thread.currentThread().getName()

run方法);}

{System.out.println(Thread.currentThread().getName()

call方法,开始准备睡觉);Thread.sleep(1000);System.out.println(Thread.currentThread().getName()

catch

System.currentTimeMillis();}}public

static

MyThread2();//future-callableFutureTaskLong

futureTask2

futureTask2.get();System.out.println(result1);}//线程一new

Thread(runable,

在主线程中需要执行比较耗时的操作时但又不想阻塞主线程时可以把这些作业交给

Future

多用于耗时的计算主线程可以在完成自己的任务后再去获取结果仅在计算完成时才能检索结果如果计算尚未完成则阻塞

get

时获取否则会一直阻塞直到任务转入完成状态然后会返回结果或者抛出异常。

只计算一次

JUC

中提供了三种常用的辅助类通过这些辅助类可以很好的解决线程数量过

Lock

{//创建CountDownLatch对象设置初始值CountDownLatch

countDownLatch

CountDownLatch(6);//6个同学陆续离开教室之后for

(int

Thread(()-{System.out.println(Thread.currentThread().getName()

-1countDownLatch.countDown();},String.valueOf(i)).start();}//等待countDownLatch.await();System.out.println(Thread.currentThread().getName()

}CountDownLatch

{//创建CyclicBarrierCyclicBarrier

cyclicBarrier

CyclicBarrier(NUMBER,()-{System.out.println(*****集齐7颗龙珠就可以召唤神龙);});//集齐七颗龙珠过程for

(int

{System.out.println(Thread.currentThread().getName()

星龙被收集到了);//等待cyclicBarrier.await();}

catch

{e.printStackTrace();}},String.valueOf(i)).start();}}

}CyclicBarrier

了表示这是这一代最后一个线程到达栅栏就尝试执行我们构造方法中输入的任务。

private

IllegalArgumentException();this.parties

parties;this.count

就代表了有拦截的线程的数量当拦截的线程数量达到这个值的时候就打开栅栏让所有线程通过。

2、当调用

方法就像树立起一个栅栏的行为一样将线程挡住了当拦住的线程数量达到

parties

BrokenBarrierException,TimeoutException

{final

之后说明最后一个线程已经到达栅栏了也就是达到了可以执行await

(index

(!ranAction)breakBarrier();}}//

loop

execution.Thread.currentThread().interrupt();}}if

(g.broken)throw

的构造方法中传入的第一个参数是最大信号量可以看成最大线程池每个信号量初始化为一个最多只能分发一个许可证。

使用

acquire

{//抢占semaphore.acquire();System.out.println(Thread.currentThread().getName()

抢到了车位);//设置随机停车时间TimeUnit.SECONDS.sleep(new

Random().nextInt(5));System.out.println(Thread.currentThread().getName()

catch

{//释放车位semaphore.release();}},String.valueOf(i)).start();}}

}Semaphore

{sync.acquireSharedInterruptibly(1);

/***

共享模式下获取许可证获取成功则返回失败则加入阻塞队列挂起线程*/

public

尝试获取许可证arg为获取许可证个数当可用许可证数减当前获取的许可证数结果小于0,则创建一个节点加入阻塞队列挂起当前线程。

if

0)doAcquireSharedInterruptibly(arg);

CAS

statestate1。

释放许可证成功之后同时会唤醒同步队列中的一个线程。

被唤醒的线程会重新尝试去修改

state

{//唤醒同步队列中的一个线程doReleaseShared();return

true;}return

现实中有这样一种场景对共享资源有读和写的操作且写操作没有读操作那么频繁。

在没有写操作的时候多个线程同时读一个资源没有任何问题所以

应该允许多个线程同时读取共享资源但是如果一个线程想去写这些共享资源

针对这种场景JAVA

平优于公平。

2重进入读锁和写锁都支持线程重进入。

3锁降级遵循获取写锁、获取读锁再释放写锁的次序写锁能够降级成为读锁。

9.2

ReentrantReadWriteLock.ReadLock

readerLock;/***

ReentrantReadWriteLock.WriteLock

writerLock;final

ReentrantReadWriteLock.WriteLock

writeLock()

ReentrantReadWriteLock.ReadLock

readLock()

ReentrantReadWriteLock();//放数据public

void

{//添加写锁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()

catch

{//释放写锁rwLock.writeLock().unlock();}}//取数据public

Object

{//添加读锁rwLock.readLock().lock();Object

result

{System.out.println(Thread.currentThread().getName()

正在读取操作key);//暂停一会TimeUnit.MICROSECONDS.sleep(300);result

map.get(key);System.out.println(Thread.currentThread().getName()

catch

{//释放读锁rwLock.readLock().unlock();}return

result;}

Thread(()-{myCache.put(num,num);},String.valueOf(i)).start();}TimeUnit.MICROSECONDS.sleep(300);//创建线程取数据for

(int

Thread(()-{myCache.get(num);},String.valueOf(i)).start();}}

}锁降级

ReentrantReadWriteLock();private

Lock

readWriteLock.writeLock();private

Lock

readWriteLock.readLock();private

volatile

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

}9.4

在线程持有读锁的情况下该线程不能取得写锁(因为获取写锁的时候如果发

现当前的读锁被占用就马上获取失败不管读锁是不是被当前线程持有)。

在线程持有写锁的情况下该线程可以继续获取读锁获取读锁时如果发现写锁被占用只有写锁没有被当前线程占用的情况才会获取失败。

原因:

当线程获取读锁的时候可能有其他线程同时也在持有读锁因此不能把获取读锁的线程“升级”为写锁而对于获得写锁的线程它一定独占了读写

锁因此可以继续让它获取读锁当它同时获取了写锁和读锁后还可以先释放写锁继续持有读锁这样一个写锁就“降级”为了读锁。

阻塞队列

“传输”数据的问题。

通过这些高效并且线程安全的队列类为我们快速搭建

BlockingQueue

试图从空的队列中获取元素的线程将会被阻塞直到其他线程往空的队列插入新的元素

试图向已满的队列中添加新元素的线程将会被阻塞直到其他线程从队列中移除一个或多

先进先出FIFO先插入的队列的元素也最先出队列类似于排队的功能。

从某种程度上来说这种队列也体现了一种公平性后进先出LIFO后插入队列的元素最先出队列这种队列优先处理最近发

在多线程领域所谓阻塞在某些情况下会挂起线程即阻塞一旦条件满足被挂起的线程又会自动被唤起

为什么需要

好处是我们不需要关心什么时候需要阻塞线程什么时候需要唤醒线程因为这一切

BlockingQueue

节尤其还要兼顾效率和线程安全而这会给我们的程序带来不小的复杂度。

多线程环境中通过队列可以很容易实现数据共享比如经典的“生产者”和

“消费者”模型中通过队列可以很便利地实现两者之间的数据共享。

假设我们有若干生产者线程另外又有若干个消费者线程。

如果生产者线程需要把准

备好的数据共享给消费者线程利用队列的方式来传递数据就可以很方便地解决他们之间的数据共享问题。

但如果生产者和消费者在某个时间段内万一

发生数据处理速度不匹配的情况呢理想情况下如果生产者产出数据的速度大于消费者消费的速度并且当生产出来的数据累积到一定程度的时候那么生产者必须暂停等待一下阻塞生产者线程以便等待消费者线程把累积的

直到有数据放入队列当队列中填满数据的情况下生产者端的所有线程都会被自动阻塞挂起

10.2

unit)可以设定等待的时间如果在指定的时间内还不能往队列中加入

BlockingQueue则返回失败put(anObject):把

anObject

如果在指定时间内队列一旦有数据可取则立即返回队列中的数据。

否则知

BlockingQueue

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,

BlockingQueue

内部维护了一个定长数组以便缓存队列中的数据对象这是一个常用的阻塞队列除了一个定长数组外ArrayBlockingQueue

内部还保存着两个整形变量分别标识着队列的头部和尾部在数组中的位置。

ArrayBlockingQueue

在生产者放入数据和消费者获取数据都是共用同一个锁对象由此也意味着两者无法真正并行运行这点尤其不同于

LinkedBlockingQueue按照实现原理来分析ArrayBlockingQueue

完全可以采用分离锁从而实现生产者和消费者操作的完全并行运行。

Doug

Lea

的数据写入和获取操作已经足够轻巧以至于引入独立的锁机制除了给代码带来额外的复杂性外其在性能上完全占不到任何便宜。

ArrayBlockingQueue

间还有一个明显的不同之处在于前者在插入或删除元素时不会产生或销毁任何额外的对象实例而后者则会生成一个额外的

Node

类似其内部也维持着一个数据缓冲队列该队列由一个链表构成当生产者往队列中放入一个数据时队列会从生产者手中获取数据并缓存在队列内部而生产者立即返回

只有当队列缓冲区达到最大值缓存容量时LinkedBlockingQueue

可以通过构造函数指定该值才会阻塞生产者队列直到消费者从队列中消费掉一份数据生产者线程会被唤醒反之对于消费者这端的处理也基于同样的原理。

而LinkedBlockingQueue

之所以能够高效的处理并发数据还因为其对于生产者端和消费者端分别采用了独立的锁来控制数据同步这也意味着在高并发的情况下生产者和消费者可以并行地操作队列中的数据以此来提高整个队列

ArrayBlockingQueue

的阻塞队列一般情况下在处理多线程间的生产者消费者问题使用这两个

一句话总结:

中的元素只有当其指定的延迟时间到了才能够从队列中获取到该元素。

DelayQueue

是一个没有大小限制的队列因此往队列中插入数据的操作生产者永远不会被阻塞而只有获取数据的操作消费者才会被阻塞。

一句话总结:

并不会阻塞数据生产者而只会在没有可消费的数据时阻塞数据的消费者。

因此使用的时候要特别注意生产者生产数据的速度绝对不能快于消费者消费

在实现

一种无缓冲的等待队列类似于无中介的直接交易有点像原始社会中的生产者和消费者生产者拿着产品去集市销售给产品的最终消费者而消费者必须亲自去集市找到所要商品的直接生产者如果一方没有找到合适的目标那么对不起大家都在集市等待。

相对于有缓冲的

BlockingQueue

来说少了一个中间经销商的环节缓冲区如果有经销商生产者直接把产品批发给经销商而无需在意经销商最终会将这些产品卖给那些消费者由于经销商可以库存一部分商品因此相对于直接交易模式总体来说采用中间经销商的模式

会吞吐量高一些可以批量买卖但另一方面又因为经销商的引入使得产品从生产者到消费者中间增加了额外的交易环节单个产品的及时响应性能可能会降低。

声明一个

队列来阻塞多余的生产者和消费者从而体系整体的公平策略非公平模式SynchronousQueue

默认SynchronousQueue

如果生产者和消费者的处理速度有差距则很容易出现饥渴的情况即可能有某些生产者或者是消费者的数据永远都得不到处理。

一句话总结:

队列。

相对于其他阻塞队列LinkedTransferQueue

tryTransfer

采用一种预占模式。

意思就是消费者线程取元素时如果队列不为空则直接取走数据若队列为空那就生成一个节点节点元素

null入队然后消费者线程被等待在这个节点上后面生产者线程入队时发现有一个元素为

null

的节点生产者线程就不入队了直接就将元素填充到该节点并唤醒该节点等待的线程被唤醒的消费者线程取走元素从调用的方法返回。

一句话总结:

是一个由链表结构组成的双向阻塞队列即可以从队列的两端插入和移除元素。

对于一些指定的操作在插入或者获取队列元素时如果队列状态不允许该操作可能会阻塞住该线程直到队列状态变更为允许操作这里的阻塞一般有两种情况。

插入元素时:

如果当前队列已满将会进入阻塞状态一直等到队列有空的位置时再将该元素插入该操作可以通过设置超时参数超时后返回

false

所谓阻塞在某些情况下会挂起线程即阻塞一旦条件满足被挂起的线程又会自动被唤起。

为什么需要

BlockingQueue?

我们每个程序员都必须去自己控制这些细节尤其还要兼顾效率和线程安全

而这会给我们的程序带来不小的复杂度。

使用后我们不需要关心什么时候需要阻塞线程什么时候需要唤醒线程因为这一切

BlockingQueue

进而影响缓存局部性和整体性能。

而线程池维护着多个线程等待着监督管理者分配可并发执行的任务。

这避免了在处理短时间任务时创建与销毁线程的代

这里借用《Java

降低资源消耗。

通过重复利用已创建的线程降低线程创建和销毁造成的消耗。

提高响应速度。

当任务到达时任务可以不需要等到线程创建就能立即执行。

提高线程的可管理性。

线程是稀缺资源如果无限制的创建不仅会消耗系统资源还会降低系统的稳定性使用线程池可以进行统一的分配调优和监控。

Java

ExecutorServiceThreadPoolExecutor

这几个类

用给定的初始参数创建一个新的ThreadPoolExecutor。

*/public

maximumPoolSize,//线程池的最大线程数long

keepAliveTime,//当线程数大于核心线程数时多余的空闲线程存活的最长时间TimeUnit

unit,//时间单位BlockingQueueRunnable

workQueue,//任务队列用来储存等待执行任务的队列ThreadFactory

threadFactory,//线程工厂用来创建线程一般默认即可RejectedExecutionHandler

handler//拒绝策略当提交的任务过多而不能及时处理时我们可以定制策略来处理任务)

{if

NullPointerException();this.corePoolSize

corePoolSize;this.maximumPoolSize

unit.toNanos(keepAliveTime);this.threadFactory

handler;}11.2.1

当队列中存放的任务达到队列容量的时候当前可以同时运行的线程数量变为最大线程数。

workQueue:

当新任务来的时候会先判断当前运行的线程数量是否达到核心线程数如果达到的话新任务就会被存放在队列中。

corePoolSize

的时候如果这时没有新的任务提交核心线程外的线程不会立即销毁而是会等待直到等待的时间超过了

keepAliveTime

如果当前同时运行的线程数量达到最大线程数量并且队列也已经被放满了任务时ThreadPoolTaskExecutor

定义一些策略:

RejectedExecutionException来拒绝新任务的处理。

ThreadPoolExecutor.CallerRunsPolicy

调用执行自己的线程运行任务也就是直接在调用execute方法的线程中运行(run)被拒绝的任务如果执行程序已关闭则会丢弃该任务。

因此这种策略会降低对于新任务提交速度影响程序的整体性能。

如果您的应用程序可以承受此延迟并且你要求任何一个任务请求都要被执行的话你可以选择这个策略。

ThreadPoolExecutor.DiscardPolicy

ThreadPoolExecutor.DiscardOldestPolicy

举个例子

ThreadPoolExecutor.AbortPolicy。

在默认情况下ThreadPoolExecutor

将抛出

ThreadPoolExecutor.CallerRunsPolicy。

当最大池被填满时此策略为我们提供可伸缩队列。

这个直接查看

ThreadPoolExecutor

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());

}场景:

适用于需要保证顺序执行各个任务并且在任意时间点不会同时有多个线程的场景

11.3.4

ScheduledThreadPoolExecutor(corePoolSize,

threadFactory);

ForkJoinPool(parallelism,ForkJoinPool.defaultForkJoinWorkerThreadFactory,null,true);

}场景:

ThreadPoolExecutor(3,3,60L,TimeUnit.SECONDS,new

LinkedBlockingQueue(),Executors.defaultThreadFactory(),

new

ThreadPoolExecutor.DiscardOldestPolicy());try

{//10

{System.out.println(Thread.currentThread().getName()

窗口,开始卖票);Thread.sleep(5000);System.out.println(Thread.currentThread().getName()

窗口买票结束);}

{//完成后结束threadService.shutdown();}}

}11.5

maximumPoolSize那么线程池会启动饱和拒绝策略来执行。

当一个线程无事可做超过一定的时间keepAliveTime时线程会判断

4.1

项目中创建多线程时使用常见的三种线程池创建方式单一、可变、定长都有一定问题原因是

FixedThreadPool

分别从双端队列里获取任务执行。

子任务执行完的结果都放在另外一个队列里

Java

RecursiveAction用于没有返回结果的任务RecursiveTask:用于有返回结果的任务

ForkJoinPool:

ForkJoinWorkerThread)((ForkJoinWorkerThread)

t).workQueue.push(this);elseForkJoinPool.common.externalPush(this);return

this;}pushTask

signalWork()方法唤醒或创建一个工作线程来执行任务。

代码如下

final

visibilityU.putOrderedObject(a,

((m

null)p.signalWork(p.workQueues,

this);//执行}

NORMAL)reportException(s);return

doJoin

已完成NORMAL、被取消CANCELLED、信号SIGNAL和出

现异常EXCEPTIONAL

如果任务状态是已完成则直接返回任务结果。

如果任务状态是被取消则直接抛出

CancellationException如果任务状态是抛出异常则直接抛出对应的异常

让我们分析一下

(ForkJoinWorkerThread)t).workQueue).tryUnpush(this)

doExec())

setExceptionalCompletion(rex);}if

(completed)s

首先通过查看任务的状态看任务是否已经执行完成如果执行完成则直接

返回任务状态如果没有执行完则从任务数组里取出任务并执行。

如果任务顺利执行完成则设置任务状态为

NORMAL如果出现异常则记

CancellationException。

如果任务没有完成或者没有抛出异常则返回

12.5

end;}//拆分和合并过程Overrideprotected

Integer

{//判断相加两个数值是否大于10if((end-begin)VALUE)

{//相加操作for

MyTask(begin,middle);//拆分右边MyTask

task02

MyTask(middle1,end);//调用方法拆分task01.fork();task02.fork();//合并结果result

task01.join()task02.join();}return

result;}

MyTask(0,100);//创建分支合并池对象ForkJoinPool

forkJoinPool

ForkJoinPool();ForkJoinTaskInteger

forkJoinTask

forkJoinPool.submit(myTask);//获取最终合并之后结果Integer

result

forkJoinTask.get();System.out.println(result);//关闭池对象forkJoinPool.shutdown();}

}13

里面被用于异步编程异步通常意味着非阻塞可以使得我们的任务单独运行在与主线程分离的其他线程中并且通过回调可以在主线程中得到异步任务的执行状态是否完成和是否异常等信息。

CompletableFuture

接口才是异步编程的接口抽象里面定义多种异步方法通过这两者集合从而打造出了强大的CompletableFuture

13.2

里面通常用来表示一个异步任务的引用比如我们将任务提交到线程池里面然后我们会得到一个

Futrue在

方法可以一直阻塞直到任务结束然后获取结果但整体来说这种方式还是同步的因为需要客户端不断阻塞等待或者不断轮询才能知道任务是否完成。

Future

我提交了一个任务但是执行太慢了我通过其他路径已经获取到了任务结果

现在没法把这个任务结果通知到正在执行的线程所以必须主动取消或者一直等待它执行完成

Future

方法会一直阻塞到任务完成但是想在获取任务之后执行额外的任务因为

Future

异步神器CompletableFuture让你的代码免受阻塞之苦



SEO优化服务概述

作为专业的SEO优化服务提供商,我们致力于通过科学、系统的搜索引擎优化策略,帮助企业在百度、Google等搜索引擎中获得更高的排名和流量。我们的服务涵盖网站结构优化、内容优化、技术SEO和链接建设等多个维度。

百度官方合作伙伴 白帽SEO技术 数据驱动优化 效果长期稳定

SEO优化核心服务

网站技术SEO

  • 网站结构优化 - 提升网站爬虫可访问性
  • 页面速度优化 - 缩短加载时间,提高用户体验
  • 移动端适配 - 确保移动设备友好性
  • HTTPS安全协议 - 提升网站安全性与信任度
  • 结构化数据标记 - 增强搜索结果显示效果

内容优化服务

  • 关键词研究与布局 - 精准定位目标关键词
  • 高质量内容创作 - 原创、专业、有价值的内容
  • Meta标签优化 - 提升点击率和相关性
  • 内容更新策略 - 保持网站内容新鲜度
  • 多媒体内容优化 - 图片、视频SEO优化

外链建设策略

  • 高质量外链获取 - 权威网站链接建设
  • 品牌提及监控 - 追踪品牌在线曝光
  • 行业目录提交 - 提升网站基础权威
  • 社交媒体整合 - 增强内容传播力
  • 链接质量分析 - 避免低质量链接风险

SEO服务方案对比

服务项目 基础套餐 标准套餐 高级定制
关键词优化数量 10-20个核心词 30-50个核心词+长尾词 80-150个全方位覆盖
内容优化 基础页面优化 全站内容优化+每月5篇原创 个性化内容策略+每月15篇原创
技术SEO 基本技术检查 全面技术优化+移动适配 深度技术重构+性能优化
外链建设 每月5-10条 每月20-30条高质量外链 每月50+条多渠道外链
数据报告 月度基础报告 双周详细报告+分析 每周深度报告+策略调整
效果保障 3-6个月见效 2-4个月见效 1-3个月快速见效

SEO优化实施流程

我们的SEO优化服务遵循科学严谨的流程,确保每一步都基于数据分析和行业最佳实践:

1

网站诊断分析

全面检测网站技术问题、内容质量、竞争对手情况,制定个性化优化方案。

2

关键词策略制定

基于用户搜索意图和商业目标,制定全面的关键词矩阵和布局策略。

3

技术优化实施

解决网站技术问题,优化网站结构,提升页面速度和移动端体验。

4

内容优化建设

创作高质量原创内容,优化现有页面,建立内容更新机制。

5

外链建设推广

获取高质量外部链接,建立品牌在线影响力,提升网站权威度。

6

数据监控调整

持续监控排名、流量和转化数据,根据效果调整优化策略。

SEO优化常见问题

SEO优化一般需要多长时间才能看到效果?
SEO是一个渐进的过程,通常需要3-6个月才能看到明显效果。具体时间取决于网站现状、竞争程度和优化强度。我们的标准套餐一般在2-4个月内开始显现效果,高级定制方案可能在1-3个月内就能看到初步成果。
你们使用白帽SEO技术还是黑帽技术?
我们始终坚持使用白帽SEO技术,遵循搜索引擎的官方指南。我们的优化策略注重长期效果和可持续性,绝不使用任何可能导致网站被惩罚的违规手段。作为百度官方合作伙伴,我们承诺提供安全、合规的SEO服务。
SEO优化后效果能持续多久?
通过我们的白帽SEO策略获得的排名和流量具有长期稳定性。一旦网站达到理想排名,只需适当的维护和更新,效果可以持续数年。我们提供优化后维护服务,确保您的网站长期保持竞争优势。
你们提供SEO优化效果保障吗?
我们提供基于数据的SEO效果承诺。根据服务套餐不同,我们承诺在约定时间内将核心关键词优化到指定排名位置,或实现约定的自然流量增长目标。所有承诺都会在服务合同中明确约定,并提供详细的KPI衡量标准。

SEO优化效果数据

基于我们服务的客户数据统计,平均优化效果如下:

+85%
自然搜索流量提升
+120%
关键词排名数量
+60%
网站转化率提升
3-6月
平均见效周期

行业案例 - 制造业

  • 优化前:日均自然流量120,核心词无排名
  • 优化6个月后:日均自然流量950,15个核心词首页排名
  • 效果提升:流量增长692%,询盘量增加320%

行业案例 - 电商

  • 优化前:月均自然订单50单,转化率1.2%
  • 优化4个月后:月均自然订单210单,转化率2.8%
  • 效果提升:订单增长320%,转化率提升133%

行业案例 - 教育

  • 优化前:月均咨询量35个,主要依赖付费广告
  • 优化5个月后:月均咨询量180个,自然流量占比65%
  • 效果提升:咨询量增长414%,营销成本降低57%

为什么选择我们的SEO服务

专业团队

  • 10年以上SEO经验专家带队
  • 百度、Google认证工程师
  • 内容创作、技术开发、数据分析多领域团队
  • 持续培训保持技术领先

数据驱动

  • 自主研发SEO分析工具
  • 实时排名监控系统
  • 竞争对手深度分析
  • 效果可视化报告

透明合作

  • 清晰的服务内容和价格
  • 定期进展汇报和沟通
  • 效果数据实时可查
  • 灵活的合同条款

我们的SEO服务理念

我们坚信,真正的SEO优化不仅仅是追求排名,而是通过提供优质内容、优化用户体验、建立网站权威,最终实现可持续的业务增长。我们的目标是与客户建立长期合作关系,共同成长。

提交需求或反馈

Demand feedback