热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

java并发编程在协作对象_Java并发编程六线程间的协作方式

线程间的协作指的是多个线程在共同完成某项任务时,需要线程之间互相通信,协调共同完成,最经典的例子就是生产者-消费者模型(网上烂大街的例子.

线程间的协作指的是多个线程在共同完成某项任务时,需要线程之间互相通信,协调共同完成,最经典的例子就是生产者-消费者模型(网上烂大街的例子...)。

所谓生产者消费者模型是指:有个共同的数据结构(比如阻塞队列)作为生产者线程和消费者线程读和写的临界资源,当队列满时,生产者线程阻塞(挂起),生产者线程释放临界资源的占有权(比如对象锁),直到队列有空间时才能重新获得临界资源的占有权向队列里放入商品;当队列为空时,消费者线程阻塞,消费者线程释放临界资源的占有权,直到队列里有商品时才能重新获得临界资源的占有权消费商品。生产者-消费者模型最经典的例子是线程池。

当生产者生产了一个商品,会去通知消费者可以消费了;当消费者消费了一个商品,会通知生产者可以再生产一个,这就是线程间的通信协作来完成共同的一件事情。

之前介绍的Thread类的实例方法join()也是线程间通信的方式,这里介绍线程协作中最常见的两种方式:Object类的wait()、notify()、notifyAll()和依赖于Lock接口的Condition接口。

1、Object类的wait()、notify()、notifyAll()

1. 1 API说明

/*** Wakes up a single thread that is waiting on this object's* monitor. If any threads are waiting on this object, one of them* is chosen to be awakened. The choice is arbitrary and occurs at* the discretion of the implementation. A thread waits on an object's* monitor by calling one of the wait methods*/

public final native void notify();

/*** Wakes up all threads that are waiting on this object's monitor. A* thread waits on an object's monitor by calling one of the* wait methods.*/

public final native void notifyAll();

/*** Causes the current thread to wait until either another thread invokes the* {@link java.lang.Object#notify()} method or the* {@link java.lang.Object#notifyAll()} method for this object, or a* specified amount of time has elapsed.*

* The current thread must own this object's monitor.*/

public final native void wait(long timeout) throws InterruptedException;

(1)wait()、notify()和notifyAll()方法都是Object类的final方法,无法被重写。

(2)wait()方法,调用某个对象的wait()方法能够让当前线程阻塞,前提是当前线程拥有这个对象的锁,此时线程会释放对象锁,待线程被notify()方法或者notifyAll()方法唤醒并重新获得对象锁后,线程会从阻塞的地方向后继续执行程序。wait()方法里还有个timeout的参数,意思是超过了这个时间(秒级),如果没有其他线程获取了对象锁,调用wait(timeout)方法的线程会重新获取对象锁,执行wait(timeout)方法后面的程序。

(3)notify()方法,调用某个对象的notify()方法能够唤醒一个正在等待这个对象锁的线程,前提是调用notify()方法的线程获取了对象锁。注意:调用完notify()方法后,被唤醒的线程不是立刻就获取对象锁执行任务,而是要等待执行notify()方法的线程执行完剩余任务释放了对象锁后,才能有机会获取到对象锁执行wait()后面的程序,这里有机会是说:线程执行完notify并且执行完同步块里的代码后,该线程处于就绪状态,会和notify唤醒后的线程共同竞争,有可能线程执行完一次notify后,又执行了notify,而没让被唤醒的线程获得锁执行。

(4)notifyAll()方法,调用某个对象的notifyAll()方法可以唤醒正在等待这个对象锁的所有线程,前提是调用notifyAll方法的线程获取了对象锁。

1.2 举例

起两个异步线程,一个是下载线程,用来下载图片和附件,另一个是图片展示线程,用来展示图片,执行顺序是:先下载图片,图片下载完毕后展示图片,图片展示完毕后下载附件。

package Notify;

/*** @ClassName NotifyDemo* @Description TODO* @Auther Jerry* @Date 2020/3/11 - 0:45* @Version 1.0*/

/*** wait()&¬ify()方法* 这两个方法是在Object中定义的,用于协调线程同步,比join更加灵活*/

public class NotifyDemo {

public static void main(String[] args) {

Object obj=new Object();

// 下载线程,包括下载图片和下载附件 Thread download=new Thread(){

public void run() {

System.out.println("开始下载图片");

for (int i &#61; 0; i <101; i&#43;&#61;10) {

System.out.println("down"&#43;i&#43;"%");

try {

Thread.sleep(50);

} catch (InterruptedException e) {

e.printStackTrace();

}

}

System.out.println("图片下载成功");

synchronized (obj) {

//唤醒展示图片的线程 obj.notify();

// 阻塞当前线程下载附件&#xff0c;待图片展示线程展示图片完毕后再唤醒下载线程下载附件 try {

obj.wait();

} catch (InterruptedException e)

{

e.printStackTrace();

}

}

System.out.println("开始下载附件");

for (int i &#61; 0; i <101; i&#43;&#61;10) {

System.out.println("附件下载"&#43;i&#43;"%");

try {

Thread.sleep(50);

} catch (InterruptedException e) {

e.printStackTrace();

}

}

System.out.println("附件下载成功");

}

};

// 图片展示线程 Thread show&#61;new Thread(){

public void run(){

synchronized (obj) {

try {

// 开始图片还未下载完成&#xff0c;阻塞图片展示线程 obj.wait();

} catch (InterruptedException e) {

e.printStackTrace();

}

System.out.println("show:开始展示图片");

try {

Thread.sleep(5000);

} catch (InterruptedException e) {

e.printStackTrace();

}

System.out.println("图片展示完毕");

// 图片展示线程展示完图片后&#xff0c;再通知下载线程下载附件 obj.notify();

}

}

};

// 启动两个线程 download.start();

show.start();

}

}

结果如下&#xff1a;

开始下载图片

down0%

down10%

down20%

down30%

down40%

down50%

down60%

down70%

down80%

down90%

down100%

图片下载成功

show:开始展示图片

图片展示完毕

开始下载附件

附件下载0%

附件下载10%

附件下载20%

附件下载30%

附件下载40%

附件下载50%

附件下载60%

附件下载70%

附件下载80%

附件下载90%

附件下载100%

附件下载成功

1.3 面试会问的点

(1)wait()、notify()和notifyAll()方法为什么是Object类的而不是Thread类的&#xff1f;

只有同一个锁上的被等待线程&#xff0c;才可以被同一个锁上执行notify/notifyAll的线程唤醒&#xff0c;之间沟通的桥梁是这个对象锁&#xff0c;因此方法是Object类而不是Thread类的。

(2)wait()、sleep()、join()、yield()的区别&#xff1f;wait()方法是Object类的实例方法&#xff0c;调用wait()方法会让当前线程阻塞&#xff0c;释放锁&#xff1b;

sleep()方法是Thread类的静态方法&#xff0c;会有入参sleep(timeout)&#xff0c;单位是毫秒&#xff0c;调用sleep()会让线程进入阻塞状态&#xff0c;但不会释放锁&#xff0c;待阻塞时间超过timeout时&#xff0c;线程会由阻塞状态变为就绪状态&#xff0c;获得CPU时间片后变为运行状态继续执行任务&#xff1b;

yiled方法是Thread类的静态方法&#xff0c;没有入参&#xff0c;调用yield方法会使线程退出CPU时间片由运行状态变为就绪状态&#xff0c;让其他同优先级或者更高优先级的线程获取CPU时间片进入运行状态&#xff0c;yiled方法不会释放锁&#xff1b;

join()方法是Thread类的实例方法&#xff0c;没有入参&#xff0c;线程执行t.join()方法&#xff0c;线程会等待t线程结束再继续执行&#xff0c;调用线程本身也会阻塞。

2、Condition接口

Condition是一个接口&#xff0c;是java1.5引进的&#xff0c;就是用来替代传统的wait()、notify()和notifyAll()来实现线程间协作通信的&#xff0c;相比之下Condition更加灵活高效&#xff0c;一个Lock实例里可以创建多个Condition对象&#xff0c;完成多路通信(不同线程对应不通的Condition)。相比之下&#xff0c;synchronized块搭配wait()、notify()、notifyAll()&#xff0c;相当于仅有1个Condition&#xff0c;所有线程的调度通信都是由这个Condition完成的&#xff0c;不够灵活。

2.1 API说明

package java.util.concurrent.locks;

import java.util.Date;

import java.util.concurrent.TimeUnit;

public interface Condition {

void await() throws InterruptedException;

void signal();

void signalAll();

}

上面仅列举了Condition接口的三个方法。

(1)Condition是个接口&#xff0c;await方法对应Object类的wait方法&#xff0c;signal方法对应Object类的notify方法&#xff0c;singnalAll方法对应Object类的notifyAll方法&#xff1b;

(2)Condition对象的创建依赖Lock对象&#xff0c;具体创建为&#xff1a;

Lock lock &#61; new ReentrantLock();

Condition condition &#61; lock.newCondition();

(3)Condition对象的await方法 、signal方法和signalAll方法都必须在lock保护之内&#xff0c;就是说必须在lock.lock()和lock.unlock之间才可以使用。

2.2 举例

初始化两个Condition对象&#xff0c;在main方法里起两个异步线程&#xff0c;分别调用两个Condition对象的await方法阻塞两个异步线程&#xff0c;然后在主线程中先调用ConditionA的signal方法唤醒线程A&#xff0c;过2秒后在主线程中再调用ConditionB的signal方法唤醒线程B&#xff0c;如下&#xff1a;

package Lock;

import java.util.concurrent.locks.Condition;

import java.util.concurrent.locks.Lock;

import java.util.concurrent.locks.ReentrantLock;

/*** &#64;ClassName ConditionDemo* &#64;Description TODO* &#64;Auther Jerry* &#64;Date 2020/3/18 - 22:31* &#64;Version 1.0*/

public class ConditionDemo {

Lock lock &#61; new ReentrantLock();

private Condition conditionA &#61; lock.newCondition();

private Condition conditionB &#61; lock.newCondition();

public static void main(String[] args) {

// 初始化实例 ConditionDemo conditionDemo &#61; new ConditionDemo();

// 启动两个线程&#xff0c;分别调用各自的Condition的await方法令其等待 new Thread(conditionDemo::awaitA).start();

new Thread(conditionDemo::awaitB).start();

// 主线程休眠2s后&#xff0c;先唤醒conditionA对应的线程 try {

Thread.sleep(2000);

} catch (InterruptedException e)

{

e.printStackTrace();

}

conditionDemo.signalA();

// 唤醒完conditionA对应的线程后&#xff0c;主线程再休眠2s&#xff0c;再唤醒conditionB对应的线程 try {

Thread.sleep(2000);

} catch (InterruptedException e)

{

e.printStackTrace();

}

// conditionDemo.signalB();

}

private void signalA()

{

lock.lock();

try {

System.out.println("唤醒ConditionA对应的线程");

this.conditionA.signal();

}

finally {

lock.unlock();

}

}

private void signalB()

{

lock.lock();

try {

System.out.println("唤醒ConditionB对应的线程");

this.conditionB.signal();

}

finally {

lock.unlock();

}

}

private void awaitA()

{

lock.lock();

try {

System.out.println("ConditionA对应的线程等待...");

try {

conditionA.await();

} catch (InterruptedException e)

{

e.printStackTrace();

}

}

finally {

lock.unlock();

}

}

private void awaitB()

{

lock.lock();

try {

System.out.println("ConditionB对应的线程等待...");

try {

conditionB.await();

} catch (InterruptedException e)

{

e.printStackTrace();

}

}

finally {

lock.unlock();

}

}

}

运行结果如下 &#xff1a;

ConditionA对应的线程等待...

ConditionB对应的线程等待...

唤醒ConditionA对应的线程

唤醒ConditionB对应的线程

从结果可以看出&#xff0c;主线程可以通过ConditionA和ConditionB分别控制线程A和线程B的休眠和唤醒&#xff0c;这也是Condition相比Object类的wait等方法的一个突出优势&#xff1a;多路通信协调线程。

3、上述两种方式实现生产者-消费者模型

(1)Object类的wait、notify、notifyAll

package Notify;

import java.awt.*;

import java.util.PriorityQueue;

import java.util.concurrent.locks.Lock;

import java.util.concurrent.locks.ReentrantLock;

/*** &#64;ClassName DemoObject* &#64;Description TODO* &#64;Auther Jerry* &#64;Date 2020/3/18 - 23:34* &#64;Version 1.0*/

public class DemoObject {

// producer线程和consumer线程共同访问的临界资源 private int queueSize &#61; 10;

private PriorityQueue queue &#61; new PriorityQueue(queueSize);

public static void main(String[] args) {

DemoObject demoObject &#61; new DemoObject();

Producer producer &#61; demoObject.new Producer();

Consumer consumer &#61; demoObject.new Consumer();

producer.start();

consumer.start();

}

class Producer extends Thread{

&#64;Override

public void run() {

produce();

}

private void produce()

{

while (true)

{

synchronized (queue)

{

while (queue.size() &#61;&#61; queueSize)

{

try {

System.out.println("队列已满&#xff0c;生产者线程需阻塞");

queue.wait();

} catch (InterruptedException e)

{

e.printStackTrace();

}

}

System.out.println("队列未满&#xff0c;生产者线程可以生产一个商品");

// 模仿生产一个商品需要耗时1秒 try{

Thread.sleep(2000);

} catch (InterruptedException e)

{

e.printStackTrace();

}

queue.offer(1);

System.out.println("生产者线程生产了一个商品");

// 通知消费者线程可以消费商品了 queue.notifyAll();

}

// 线程让步&#xff0c;使下一次操作可能是生产者生产商品&#xff0c;也可能是消费者消费商品 Thread.yield();

}

}

}

class Consumer extends Thread{

&#64;Override

public void run() {

consume();

}

private void consume()

{

while (true)

{

synchronized (queue)

{

while (queue.isEmpty())

{

try {

System.out.println("队列为空&#xff0c;消费者线程需阻塞");

queue.wait();

} catch (InterruptedException e)

{

e.printStackTrace();

}

}

System.out.println("队列非空&#xff0c;消费者线程可以消费一个商品");

// 模仿消费一个商品需要耗时1秒 try{

Thread.sleep(2000);

} catch (InterruptedException e)

{

e.printStackTrace();

}

queue.poll();

System.out.println("消费者线程消费了一个商品");

// 通知生产者线程可以生产商品了 queue.notifyAll();

// 线程让步&#xff0c;使下一次操作可能是生产者生产商品&#xff0c;也可能是消费者消费商品 Thread.yield();

}

}

}

}

}

结果不是唯一的&#xff0c;每次运行都可能得到不同的结果&#xff0c;取任意一次结果展示&#xff1a;

队列未满&#xff0c;生产者线程可以生产一个商品

生产者线程生产了一个商品

队列非空&#xff0c;消费者线程可以消费一个商品

消费者线程消费了一个商品

队列为空&#xff0c;消费者线程需阻塞

队列未满&#xff0c;生产者线程可以生产一个商品

生产者线程生产了一个商品

队列非空&#xff0c;消费者线程可以消费一个商品

消费者线程消费了一个商品

队列为空&#xff0c;消费者线程需阻塞

队列未满&#xff0c;生产者线程可以生产一个商品

生产者线程生产了一个商品

队列未满&#xff0c;生产者线程可以生产一个商品

生产者线程生产了一个商品

队列非空&#xff0c;消费者线程可以消费一个商品

消费者线程消费了一个商品

队列非空&#xff0c;消费者线程可以消费一个商品

消费者线程消费了一个商品

队列为空&#xff0c;消费者线程需阻塞

队列未满&#xff0c;生产者线程可以生产一个商品

生产者线程生产了一个商品

队列未满&#xff0c;生产者线程可以生产一个商品

生产者线程生产了一个商品

队列未满&#xff0c;生产者线程可以生产一个商品

...

(2)Condition

package Notify;

import ConcurrentOne.ClassTest;

import java.util.PriorityQueue;

import java.util.concurrent.locks.Condition;

import java.util.concurrent.locks.Lock;

import java.util.concurrent.locks.ReentrantLock;

/*** &#64;ClassName DemoCondition* &#64;Description TODO* &#64;Auther Jerry* &#64;Date 2020/3/19 - 0:29* &#64;Version 1.0*/

public class DemoCondition {

// producer线程和consumer线程共同访问的临界资源 private int queueSize &#61; 10;

private PriorityQueue queue &#61; new PriorityQueue(queueSize);

// 获取Condition对象 Lock lock &#61; new ReentrantLock();

Condition conditionProducer &#61; lock.newCondition();

Condition conditionConsumer &#61; lock.newCondition();

public static void main(String[] args) {

DemoCondition demoCondition &#61; new DemoCondition();

Producer producer &#61; demoCondition.new Producer();

Consumer consumer &#61; demoCondition.new Consumer();

producer.start();

consumer.start();

}

class Producer extends Thread{

&#64;Override

public void run() {

produce();

}

private void produce()

{

while (true)

{

lock.lock();

try

{

while (queue.size() &#61;&#61; queueSize)

{

try {

System.out.println("队列已满&#xff0c;生产者线程需阻塞");

conditionProducer.await();

} catch (InterruptedException e)

{

e.printStackTrace();

}

}

System.out.println("队列未满&#xff0c;生产者线程可以生产一个商品");

// 模仿生产一个商品需要耗时1秒 try{

Thread.sleep(2000);

} catch (InterruptedException e)

{

e.printStackTrace();

}

queue.offer(1);

System.out.println("生产者线程生产了一个商品");

// 通知消费者线程可以消费商品了 conditionConsumer.signalAll();

}

finally {

lock.unlock();

}

// 线程让步&#xff0c;使下一次操作可能是生产者生产商品&#xff0c;也可能是消费者消费商品 Thread.yield();

}

}

}

class Consumer extends Thread{

&#64;Override

public void run() {

consume();

}

private void consume()

{

while (true)

{

lock.lock();

try

{

while (queue.isEmpty())

{

try {

System.out.println("队列为空&#xff0c;消费者线程需阻塞");

conditionConsumer.await();

} catch (InterruptedException e)

{

e.printStackTrace();

}

}

System.out.println("队列非空&#xff0c;消费者线程可以消费一个商品");

// 模仿消费一个商品需要耗时1秒 try{

Thread.sleep(2000);

} catch (InterruptedException e)

{

e.printStackTrace();

}

queue.poll();

System.out.println("消费者线程消费了一个商品");

// 通知生产者线程可以生产商品了 conditionProducer.signalAll();

}

finally {

lock.unlock();

}

// 线程让步&#xff0c;使下一次操作可能是生产者生产商品&#xff0c;也可能是消费者消费商品 Thread.yield();

}

}

}

}

结果如下&#xff1a;

队列未满&#xff0c;生产者线程可以生产一个商品

生产者线程生产了一个商品

队列未满&#xff0c;生产者线程可以生产一个商品

生产者线程生产了一个商品

队列未满&#xff0c;生产者线程可以生产一个商品

生产者线程生产了一个商品

队列未满&#xff0c;生产者线程可以生产一个商品

生产者线程生产了一个商品

队列未满&#xff0c;生产者线程可以生产一个商品

生产者线程生产了一个商品

队列未满&#xff0c;生产者线程可以生产一个商品

生产者线程生产了一个商品

队列未满&#xff0c;生产者线程可以生产一个商品

生产者线程生产了一个商品

队列未满&#xff0c;生产者线程可以生产一个商品

生产者线程生产了一个商品

队列未满&#xff0c;生产者线程可以生产一个商品

生产者线程生产了一个商品

队列未满&#xff0c;生产者线程可以生产一个商品

生产者线程生产了一个商品

队列已满&#xff0c;生产者线程需阻塞

队列非空&#xff0c;消费者线程可以消费一个商品

消费者线程消费了一个商品

队列非空&#xff0c;消费者线程可以消费一个商品

消费者线程消费了一个商品

队列非空&#xff0c;消费者线程可以消费一个商品

消费者线程消费了一个商品

队列非空&#xff0c;消费者线程可以消费一个商品

消费者线程消费了一个商品

队列非空&#xff0c;消费者线程可以消费一个商品

消费者线程消费了一个商品

队列非空&#xff0c;消费者线程可以消费一个商品

消费者线程消费了一个商品

队列非空&#xff0c;消费者线程可以消费一个商品

消费者线程消费了一个商品

队列非空&#xff0c;消费者线程可以消费一个商品

消费者线程消费了一个商品

队列非空&#xff0c;消费者线程可以消费一个商品

消费者线程消费了一个商品

队列非空&#xff0c;消费者线程可以消费一个商品

消费者线程消费了一个商品

队列为空&#xff0c;消费者线程需阻塞

队列未满&#xff0c;生产者线程可以生产一个商品

生产者线程生产了一个商品

队列未满&#xff0c;生产者线程可以生产一个商品

生产者线程生产了一个商品

队列未满&#xff0c;生产者线程可以生产一个商品

生产者线程生产了一个商品

队列未满&#xff0c;生产者线程可以生产一个商品

生产者线程生产了一个商品

队列未满&#xff0c;生产者线程可以生产一个商品

生产者线程生产了一个商品

队列未满&#xff0c;生产者线程可以生产一个商品

生产者线程生产了一个商品

队列未满&#xff0c;生产者线程可以生产一个商品

生产者线程生产了一个商品

队列未满&#xff0c;生产者线程可以生产一个商品

生产者线程生产了一个商品

...

实际运行时发现结果比较固定&#xff0c;都是生产者线程将队列塞满后&#xff0c;消费者线程再消费&#xff0c;且不将队列消费为空生产者线程不会生产&#xff0c;可以将生产者或者消费者线程里的Thread.yiled()换成Thread.sleep()&#xff0c;强行让另一个线程获得锁。

参考&#xff1a;Java并发编程&#xff1a;线程间协作的两种方式&#xff1a;wait、notify、notifyAll和Condition - Matrix海子 - 博客园​www.cnblogs.comCSDN-专业IT技术社区-登录​blog.csdn.netJava多线程之ReentrantLock与Condition - 平凡希 - 博客园​www.cnblogs.com3423cfd42cad5ef7bd4b381edbea217f.png



推荐阅读
  • 预备知识可参考我整理的博客Windows编程之线程:https:www.cnblogs.comZhuSenlinp16662075.htmlWindows编程之线程同步:https ... [详细]
  • Java学习笔记之面向对象编程(OOP)
    本文介绍了Java学习笔记中的面向对象编程(OOP)内容,包括OOP的三大特性(封装、继承、多态)和五大原则(单一职责原则、开放封闭原则、里式替换原则、依赖倒置原则)。通过学习OOP,可以提高代码复用性、拓展性和安全性。 ... [详细]
  • MySQL数据库锁机制及其应用(数据库锁的概念)
    本文介绍了MySQL数据库锁机制及其应用。数据库锁是计算机协调多个进程或线程并发访问某一资源的机制,在数据库中,数据是一种供许多用户共享的资源,如何保证数据并发访问的一致性和有效性是数据库必须解决的问题。MySQL的锁机制相对简单,不同的存储引擎支持不同的锁机制,主要包括表级锁、行级锁和页面锁。本文详细介绍了MySQL表级锁的锁模式和特点,以及行级锁和页面锁的特点和应用场景。同时还讨论了锁冲突对数据库并发访问性能的影响。 ... [详细]
  • 生成式对抗网络模型综述摘要生成式对抗网络模型(GAN)是基于深度学习的一种强大的生成模型,可以应用于计算机视觉、自然语言处理、半监督学习等重要领域。生成式对抗网络 ... [详细]
  • CSS3选择器的使用方法详解,提高Web开发效率和精准度
    本文详细介绍了CSS3新增的选择器方法,包括属性选择器的使用。通过CSS3选择器,可以提高Web开发的效率和精准度,使得查找元素更加方便和快捷。同时,本文还对属性选择器的各种用法进行了详细解释,并给出了相应的代码示例。通过学习本文,读者可以更好地掌握CSS3选择器的使用方法,提升自己的Web开发能力。 ... [详细]
  • 本文介绍了九度OnlineJudge中的1002题目“Grading”的解决方法。该题目要求设计一个公平的评分过程,将每个考题分配给3个独立的专家,如果他们的评分不一致,则需要请一位裁判做出最终决定。文章详细描述了评分规则,并给出了解决该问题的程序。 ... [详细]
  • Android Studio Bumblebee | 2021.1.1(大黄蜂版本使用介绍)
    本文介绍了Android Studio Bumblebee | 2021.1.1(大黄蜂版本)的使用方法和相关知识,包括Gradle的介绍、设备管理器的配置、无线调试、新版本问题等内容。同时还提供了更新版本的下载地址和启动页面截图。 ... [详细]
  • 本文介绍了解决二叉树层序创建问题的方法。通过使用队列结构体和二叉树结构体,实现了入队和出队操作,并提供了判断队列是否为空的函数。详细介绍了解决该问题的步骤和流程。 ... [详细]
  • 在Xamarin XAML语言中如何在页面级别构建ControlTemplate控件模板
    本文介绍了在Xamarin XAML语言中如何在页面级别构建ControlTemplate控件模板的方法和步骤,包括将ResourceDictionary添加到页面中以及在ResourceDictionary中实现模板的构建。通过本文的阅读,读者可以了解到在Xamarin XAML语言中构建控件模板的具体操作步骤和语法形式。 ... [详细]
  • 深入理解Kafka服务端请求队列中请求的处理
    本文深入分析了Kafka服务端请求队列中请求的处理过程,详细介绍了请求的封装和放入请求队列的过程,以及处理请求的线程池的创建和容量设置。通过场景分析、图示说明和源码分析,帮助读者更好地理解Kafka服务端的工作原理。 ... [详细]
  • 李逍遥寻找仙药的迷阵之旅
    本文讲述了少年李逍遥为了救治婶婶的病情,前往仙灵岛寻找仙药的故事。他需要穿越一个由M×N个方格组成的迷阵,有些方格内有怪物,有些方格是安全的。李逍遥需要避开有怪物的方格,并经过最少的方格,找到仙药。在寻找的过程中,他还会遇到神秘人物。本文提供了一个迷阵样例及李逍遥找到仙药的路线。 ... [详细]
  • IjustinheritedsomewebpageswhichusesMooTools.IneverusedMooTools.NowIneedtoaddsomef ... [详细]
  • 本文介绍了操作系统的定义和功能,包括操作系统的本质、用户界面以及系统调用的分类。同时还介绍了进程和线程的区别,包括进程和线程的定义和作用。 ... [详细]
  • 深入解析Linux下的I/O多路转接epoll技术
    本文深入解析了Linux下的I/O多路转接epoll技术,介绍了select和poll函数的问题,以及epoll函数的设计和优点。同时讲解了epoll函数的使用方法,包括epoll_create和epoll_ctl两个系统调用。 ... [详细]
  • 重入锁(ReentrantLock)学习及实现原理
    本文介绍了重入锁(ReentrantLock)的学习及实现原理。在学习synchronized的基础上,重入锁提供了更多的灵活性和功能。文章详细介绍了重入锁的特性、使用方法和实现原理,并提供了类图和测试代码供读者参考。重入锁支持重入和公平与非公平两种实现方式,通过对比和分析,读者可以更好地理解和应用重入锁。 ... [详细]
author-avatar
mobiledu2502858703
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有