(转)生产者/消费者问题的多种Java实现方式 (待整理)
背景:生产者消费者的问题真的是绕不开,面试时候很可能让手写此代码,需要深入总结下。
实质上,很多后台服务程序并发控制的基本原理都可以归纳为生产者/消费者模式,而这是恰恰是在本科操作系统课堂上老师反复讲解,而我们却视而不见不以为然的。在博文《一种面向作业流(工作流)的轻量级可复用的异步流水开发框架的设计与实现》中将介绍一种生产者/消费者模式的具体应用。
生产者消费者问题是研究多线程程序时绕不开的经典问题之一,它描述是有一块缓冲区作为仓库,生产者可以将产品放入仓库,消费者则可以从仓库中取走产品。
解决生产者/消费者问题的方法可分为两类:
(1)采用某种机制保护生产者和消费者之间的同步;
(2)在生产者和消费者之间建立一个管道。
第一种方式有较高的效率,并且易于实现,代码的可控制性较好,属于常用的模式。第二种管道缓冲区不易控制,被传输数据对象不易于封装等,实用性不强。
因此本文只介绍同步机制实现的生产者/消费者问题。
同步问题核心在于:如何保证同一资源被多个线程并发访问时的完整性。常用的同步方法是采用信号或加锁机制,保证资源在任意时刻至多被一个线程访问。Java语言在多线程编程上实现了完全对象化,提供了对同步机制的良好支持。
在Java中一共有四种方法支持同步,其中前三个是同步方法,一个是管道方法。
(1)wait() / notify()方法
(2)await() / signal()方法
(3)BlockingQueue阻塞队列方法
(4)PipedInputStream / PipedOutputStream
本文只介绍最常用的前三种,第四种暂不做讨论,有兴趣的读者可以自己去网上找答案。
wait()和notify()方法的实现
wait() / nofity()方法是基类Object的两个方法,也就意味着所有Java类都会拥有这两个方法,这样,我们就可以为任何对象实现同步机制。
调用 wait() 使得线程等待某个条件满足,线程在等待时会被挂起,当其他线程的运行使得这个条件满足时,其它线程会调用 notify() 或者 notifyAll() 来唤醒挂起的线程。
它们都属于 Object 的一部分,而不属于 Thread。
只能用在同步方法或者同步控制块中使用,否则会在运行时抛出 IllegalMonitorStateException。
使用 wait() 挂起期间,线程会释放锁。这是因为,如果没有释放锁,那么其它线程就无法进入对象的同步方法或者同步控制块中,那么就无法执行 notify() 或者 notifyAll() 来唤醒挂起的线程,造成死锁。
- /**
- * Project Name:basic
- * File Name:ProducerAndConsumerWaitNotifyAll.java
- * Package Name:com.forwork.com.basic.thread0411
- * Date:2019年4月11日上午6:45:33
- * Copyright (c) 2019, 深圳金融电子结算中心 All Rights Reserved.
- *
- */
- package com.forwork.com.basic.thread0411;
- /**
- * ClassName:ProducerAndConsumerWaitNotifyAll <br/>
- * Function: TODO <br/>
- * Date: 2019年4月11日 上午6:45:33 <br/>
- * @author Administrator
- * @version 1.0
- * @since JDK 1.7
- * @see
- */
- public class ProducerAndConsumerWaitNotifyAll {
- private static int count = 0;
- private static int FULL = 3; //等待条件
- private static int EMPTY = 0;
- private static String LOCK = "lock";
- private static class Producer implements Runnable {
- public void run() {
- for (int i = 0; i < 3; i++) {
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- synchronized (LOCK) {
- if (count == FULL) {
- System.out.println(Thread.currentThread().getName() + "producelock:" + count);
- try {
- LOCK.wait();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- count++;
- System.out.println(Thread.currentThread().getName() + "produce:" + count);
- LOCK.notifyAll();
- }
- }
- }
- }
- private static class Consumer implements Runnable {
- public void run() {
- for (int i = 0; i < 3; i++) {
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- synchronized (LOCK) {
- if (count == EMPTY) {
- try {
- System.out.println(Thread.currentThread().getName() + "consumerlock:" + count);
- LOCK.wait();
- } catch (Exception e) {
- e.printStackTrace();
- }
- }// (count == EMPTY)
- count--;
- System.out.println(Thread.currentThread().getName() + "consumer:" + count);
- LOCK.notifyAll();
- }
- }
- }
- }
- public static void main(String[] args) {
- for (int i = 0; i < 5; i++) {
- Producer producer = new Producer();
- new Thread(producer).start();
- }
- for (int i = 0; i < 5; i++) {
- Consumer consumer = new Consumer();
- new Thread(consumer).start();
- }
- }
- }
结果:
- Thread-1produce:1
- Thread-6consumer:0
- Thread-5consumerlock:0
- Thread-8consumerlock:0
- Thread-9consumerlock:0
- Thread-7consumerlock:0
- Thread-4produce:1
- Thread-0produce:2
- Thread-3produce:3
- Thread-2producelock:3
- Thread-7consumer:2
- Thread-9consumer:1
- Thread-8consumer:0
- Thread-5consumer:-1
- Thread-2produce:0
- Thread-1produce:1
- Thread-6consumer:0
- Thread-0produce:1
- Thread-3produce:2
- Thread-4produce:3
- Thread-9consumer:2
- Thread-7consumer:1
- Thread-2produce:2
- Thread-8consumer:1
- Thread-5consumer:0
- Thread-1produce:1
- Thread-6consumer:0
- Thread-0produce:1
- Thread-4produce:2
- Thread-3produce:3
- Thread-2producelock:3
- Thread-9consumer:2
- Thread-8consumer:1
- Thread-7consumer:0
- Thread-5consumerlock:0
- Thread-2produce:1
- Thread-5consumer:0
- 生产者在缓冲区full后wait,等待消费者调用notifyAll()唤醒后继续生产;
消费者在缓冲区empty后wait,等待生产者调用notifyAll()唤醒后继续消费。
wait() 和 sleep() 的区别
- wait() 是 Object 的方法,而 sleep() 是 Thread 的静态方法;
- wait() 会释放锁,sleep() 不会。
可重入锁ReentrantLock的实现
java.util.concurrent.lock 中的 Lock 框架是锁定的一个抽象,通过对lock的lock()方法和unlock()方法实现了对锁的显示控制,而synchronize()则是对锁的隐性控制。
可重入锁,也叫做递归锁,指的是同一线程 外层函数获得锁之后 ,内层递归函数仍然有获取该锁的代码,但不受影响,简单来说,该锁维护这一个与获取锁相关的计数器,如果拥有锁的某个线程再次得到锁,那么获取计数器就加1,函数调用结束计数器就减1,然后锁需要被释放两次才能获得真正释放。已经获取锁的线程进入其他需要相同锁的同步代码块不会被阻塞。
java.util.concurrent 类库中提供了 Condition 类来实现线程之间的协调,可以在 Condition 上调用 await() 方法使线程等待,其它线程调用 signal() 或 signalAll() 方法唤醒等待的线程。
相比于 wait() 这种等待方式,await() 可以指定等待的条件,因此更加灵活。
使用 Lock 来获取一个 Condition 对象。
- package com.forwork.com.basic.thread0411;
- import java.util.concurrent.locks.Condition;
- import java.util.concurrent.locks.Lock;
- import java.util.concurrent.locks.ReentrantLock;
- /**
- * ClassName:ProduceAndConsumerReenTrantLock <br/>
- * Function: ReenTrantLock实现
- * Date: 2019年4月11日 上午7:55:20 <br/>
- * @author Administrator
- * @version 1.0
- * @since JDK 1.7
- * @see
- */
- public class ProduceAndConsumerReenTrantLock {
- private static int count = 0;
- private static int FULL = 3; //等待条件
- private static int EMPTY = 0;
- private static Lock clock = new ReentrantLock();
- private static Condition empty = clock.newCondition();
- private static Condition full = clock.newCondition();
- private static class Producer implements Runnable {
- public void run() {
- for (int i = 0; i < 3; i++) {
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- clock.lock();
- try {
- if (count == FULL) {
- System.out.println(Thread.currentThread().getName() + " producelock:" + count);
- try {
- full.await();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- count++;
- System.out.println(Thread.currentThread().getName() + " produce:" + count);
- empty.signalAll(); //唤醒消费者
- } finally {
- clock.unlock();
- }
- }
- }
- }
- private static class Consumer implements Runnable {
- public void run() {
- for (int i = 0; i < 3; i++) {
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- clock.lock();
- try {
- if (count == EMPTY) {
- try {
- System.out.println(Thread.currentThread().getName() + " consumerlock:" + count);
- empty.await();
- } catch (Exception e) {
- e.printStackTrace();
- }
- }// (count == EMPTY)
- count--;
- System.out.println(Thread.currentThread().getName() + " consumer:" + count);
- full.signalAll(); //唤醒生产者
- } finally {
- clock.unlock();
- }
- }
- }
- }
- public static void main(String[] args) {
- for (int i = 0; i < 5; i++) {
- Producer producer = new Producer();
- new Thread(producer).start();
- }
- for (int i = 0; i < 5; i++) {
- Consumer consumer = new Consumer();
- new Thread(consumer).start();
- }
- }
- }
结果:
- Thread-1 produce:1
- Thread-4 produce:2
- Thread-0 produce:3
- Thread-2 producelock:3
- Thread-6 consumer:2
- Thread-5 consumer:1
- Thread-7 consumer:0
- Thread-3 produce:1
- Thread-9 consumer:0
- Thread-8 consumerlock:0
- Thread-2 produce:1
- Thread-8 consumer:0
- Thread-0 produce:1
- Thread-1 produce:2
- Thread-4 produce:3
- Thread-5 consumer:2
- Thread-6 consumer:1
- Thread-9 consumer:0
- Thread-7 consumerlock:0
- Thread-3 produce:1
- Thread-7 consumer:0
- Thread-2 produce:1
- Thread-8 consumer:0
- Thread-4 produce:1
- Thread-0 produce:2
- Thread-1 produce:3
- Thread-6 consumer:2
- Thread-5 consumer:1
- Thread-3 produce:2
- Thread-7 consumer:1
- Thread-9 consumer:0
- Thread-2 produce:1
- Thread-8 consumer:0
通过clock来newCondition()。
在try finally块中释放lock锁。
Condition 类上通过await()和signal() signalAll()实现线程的协同
三、BlockingQueue阻塞队列方法
BlockingQueue是JDK5.0的新增内容,它是一个已经在内部实现了同步的队列,实现方式采用的是我们第2种await() / signal()方法。它可以在生成对象时指定容量大小。它用于阻塞操作的是put()和take()方法。
put()方法:类似于我们上面的生产者线程,容量达到最大时,自动阻塞。
take()方法:类似于我们上面的消费者线程,容量为0时,自动阻塞。
- public class ProducerConsumer {
- private static BlockingQueue<String> queue = new ArrayBlockingQueue<>(5);
- private static class Producer extends Thread {
- @Override
- public void run() {
- try {
- queue.put("product");
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- System.out.print("produce..");
- }
- }
- private static class Consumer extends Thread {
- @Override
- public void run() {
- try {
- String product = queue.take();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- System.out.print("consume..");
- }
- }
- }
- public static void main(String[] args) {
- for (int i = 0; i < 2; i++) {
- Producer producer = new Producer();
- producer.start();
- }
- for (int i = 0; i < 5; i++) {
- Consumer consumer = new Consumer();
- consumer.start();
- }
- for (int i = 0; i < 3; i++) {
- Producer producer = new Producer();
- producer.start();
- }
- }
- produce..produce..consume..consume..produce..consume..produce..consume..produce..consume..
BlockingQueue即阻塞队列,从阻塞这个词可以看出,在某些情况下对阻塞队列的访问可能会造成阻塞。被阻塞的情况主要有如下两种:
- 当队列满了的时候进行入队列操作
- 当队列空了的时候进行出队列操作
因此,当一个线程对已经满了的阻塞队列进行入队操作时会阻塞,除非有另外一个线程进行了出队操作,当一个线程对一个空的阻塞队列进行出队操作时也会阻塞,除非有另外一个线程进行了入队操作。
从上可知,阻塞队列是线程安全的。
下面是BlockingQueue接口的一些方法:
其实阻塞队列实现阻塞同步的方式很简单,使用的就是是lock锁的多条件(condition)阻塞控制。使用BlockingQueue封装了根据条件阻塞线程的过程,而我们就不用关心繁琐的await/signal操作了。
- package com.forwork.com.basic.thread0411;
- import java.util.concurrent.ArrayBlockingQueue;
- import java.util.concurrent.BlockingQueue;
- /**
- * ClassName:ProduceAndConsumerBlockQueue <br/>
- * Function: TODO <br/>
- * Date: 2019年4月12日 上午6:50:14 <br/>
- * @author Administrator
- * @version 1.0
- * @since JDK 1.7
- * @see
- */
- public class ProduceAndConsumerBlockQueue {
- private static BlockingQueue<Integer> queue = new ArrayBlockingQueue<Integer>(3);
- private static class Producer implements Runnable {
- public void run() {
- for (int i = 0; i < 3; i++) {
- try {
- Thread.sleep(1000);
- queue.put(i); //入队
- System.out.println(Thread.currentThread().getName() + " produce:" + queue.size());
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }
- }
- private static class Consumer implements Runnable {
- public void run() {
- for (int i = 0; i < 3; i++) {
- try {
- Thread.sleep(1000);
- queue.take(); //出队
- System.out.println(Thread.currentThread().getName() + " consumer:" + queue.size());
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }
- }
- public static void main(String[] args) {
- for (int i = 0; i < 3; i++) {
- Producer producer = new Producer();
- new Thread(producer).start();
- }
- for (int i = 0; i < 3; i++) {
- Consumer consumer = new Consumer();
- new Thread(consumer).start();
- }
- }
- }
- Thread-0 produce:3
- Thread-2 produce:0
- Thread-1 produce:0
- Thread-4 consumer:0
- Thread-5 consumer:1
- Thread-3 consumer:2
- Thread-0 produce:1
- Thread-3 consumer:0
- Thread-5 consumer:0
- Thread-4 consumer:0
- Thread-1 produce:1
- Thread-2 produce:2
- Thread-0 produce:1
- Thread-3 consumer:0
- Thread-1 produce:1
- Thread-2 produce:2
- Thread-4 consumer:0
- Thread-5 consumer:1
put和take采用阻塞的方式插入和取出元素。
当队列为空或者满的时候,线程会挂起,直到有元素放入或者取出时候才会继续执行。
Java并发编程-阻塞队列(BlockingQueue)的实现原理
信号量Semaphore的实现
Semaphore(信号量)是用来控制同时访问特定资源的线程数量,它通过协调各个线程,以保证合理的使用公共资源,在操作系统中是一个非常重要的问题,可以用来解决哲学家就餐问题。Java中的Semaphore维护了一个许可集,一开始先设定这个许可集的数量,可以使用acquire()方法获得一个许可,当许可不足时会被阻塞,release()添加一个许可。在下列代码中,还加入了另外一个mutex信号量,维护生产者消费者之间的同步关系,保证生产者和消费者之间的交替进行
- /**
- * Project Name:basic
- * File Name:ProduceAndConsumerSemaphore.java
- * Package Name:com.forwork.com.basic.thread0411
- * Date:2019年4月12日上午8:05:07
- * Copyright (c) 2019, 深圳金融电子结算中心 All Rights Reserved.
- *
- */
- package com.forwork.com.basic.thread0411;
- import java.util.concurrent.Semaphore;
- /**
- * ClassName:ProduceAndConsumerSemaphore <br/>
- * Function: TODO <br/>
- * Date: 2019年4月12日 上午8:05:07 <br/>
- * @author Administrator
- * @version 1.0
- * @since JDK 1.7
- * @see
- */
- public class ProduceAndConsumerSemaphore {
- private static Semaphore sp = new Semaphore(3);
- private static class Producer implements Runnable {
- public void run() {
- for (int i = 0; i < 3; i++) {
- try {
- Thread.sleep(1000);
- sp.acquire(); //入队
- System.out.println(Thread.currentThread().getName() + " produce:" + sp.availablePermits());
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }
- }
- private static class Consumer implements Runnable {
- public void run() {
- for (int i = 0; i < 3; i++) {
- try {
- Thread.sleep(1000);
- sp.release(); //出队
- System.out.println(Thread.currentThread().getName() + " consumer:" + sp.availablePermits());
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }
- }
- public static void main(String[] args) {
- for (int i = 0; i < 3; i++) {
- Producer producer = new Producer();
- new Thread(producer).start();
- }
- for (int i = 0; i < 3; i++) {
- Consumer consumer = new Consumer();
- new Thread(consumer).start();
- }
- }
- }
结果:
- Thread-0 produce:0
- Thread-2 produce:0
- Thread-1 produce:0
- Thread-3 consumer:1
- Thread-4 consumer:3
- Thread-5 consumer:3
- Thread-2 produce:0
- Thread-1 produce:0
- Thread-0 produce:0
- Thread-3 consumer:1
- Thread-4 consumer:3
- Thread-5 consumer:3
- Thread-0 produce:1
- Thread-2 produce:1
- Thread-1 produce:0
- Thread-3 consumer:1
- Thread-4 consumer:3
- Thread-5 consumer:3
- /**
- * Project Name:basic
- * File Name:SemaphoreTest.java
- * Package Name:com.forwork.com.basic.thread0411
- * Date:2019年4月12日上午8:07:48
- * Copyright (c) 2019, 深圳金融电子结算中心 All Rights Reserved.
- *
- */
- package com.forwork.com.basic.thread0411;
- import java.util.concurrent.Semaphore;
- /**
- * ClassName:SemaphoreTest <br/>
- * Function: Semaphore相当于一个队列,队列中可用的信号量为初始化分配的数量n。
- * 每次release就多分配一个,acquire就消耗一个 <br/>
- * Date: 2019年4月12日 上午8:07:48 <br/>
- * @author Administrator
- * @version 1.0
- * @since JDK 1.7
- * @see
- */
- public class SemaphoreTest {
- private static Semaphore sp = new Semaphore(0);
- public static void main(String[] args) {
- try {
- for (int i = 0; i < 3; i++) {
- System.out.println(sp.availablePermits() + ":one");
- sp.release();
- sp.release();
- System.out.println(sp.availablePermits() + ":two");
- sp.acquire();
- System.out.println(sp.availablePermits() + ":three");
- sp.acquire();
- System.out.println(sp.availablePermits() + ":four");
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }
结果:
- 0:one
- 2:two
- 1:three
- 0:four
- 0:one
- 2:two
- 1:three
- 0:four
- 0:one
- 2:two
- 1:three
- 0:four
如何取得可用数量集的个数:sp.availablePermits()
每次release可用数量集会增加?是的,相当于BlockingQueue中的put操作
管道输入输出流PipedInputStream和PipedOutputStream实现
ps:了解
Java里的管道输入流 PipedInputStream与管道输出流 PipedOutputStream
感觉不是很好用~
在java的io包下,PipedOutputStream和PipedInputStream分别是管道输出流和管道输入流。
它们的作用是让多线程可以通过管道进行线程间的通讯。在使用管道通信时,必须将PipedOutputStream和PipedInputStream配套使用。
使用方法:先创建一个管道输入流和管道输出流,然后将输入流和输出流进行连接,用生产者线程往管道输出流中写入数据,消费者在管道输入流中读取数据,这样就可以实现了不同线程间的相互通讯,但是这种方式在生产者和生产者、消费者和消费者之间不能保证同步,也就是说在一个生产者和一个消费者的情况下是可以生产者和消费者之间交替运行的,多个生成者和多个消费者者之间则不行
- /**
- * 使用管道实现生产者消费者模型
- * @author ZGJ
- * @date 2017年6月30日
- */
- public class Test5 {
- final PipedInputStream pis = new PipedInputStream();
- final PipedOutputStream pos = new PipedOutputStream();
- {
- try {
- pis.connect(pos);
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- class Producer implements Runnable {
- @Override
- public void run() {
- try {
- while(true) {
- Thread.sleep(1000);
- int num = (int) (Math.random() * 255);
- System.out.println(Thread.currentThread().getName() + "生产者生产了一个数字,该数字为: " + num);
- pos.write(num);
- pos.flush();
- }
- } catch (Exception e) {
- e.printStackTrace();
- } finally {
- try {
- pos.close();
- pis.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
- }
- class Consumer implements Runnable {
- @Override
- public void run() {
- try {
- while(true) {
- Thread.sleep(1000);
- int num = pis.read();
- System.out.println("消费者消费了一个数字,该数字为:" + num);
- }
- } catch (Exception e) {
- e.printStackTrace();
- } finally {
- try {
- pos.close();
- pis.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
- }
- public static void main(String[] args) {
- Test5 test5 = new Test5();
- new Thread(test5.new Producer()).start();
- new Thread(test5.new Consumer()).start();
- }
- }
(转)生产者/消费者问题的多种Java实现方式 (待整理)的更多相关文章
- (转)生产者/消费者问题的多种Java实现方式
参考来源:http://blog.csdn.net/monkey_d_meng/article/details/6251879/ 生产者/消费者问题的多种Java实现方式 实质上,很多后台服务程序并发 ...
- 生产者/消费者问题的多种Java实现方式--转
实质上,很多后台服务程序并发控制的基本原理都可以归纳为生产者/消费者模式,而这是恰恰是在本科操作系统课堂上老师反复讲解,而我们却视而不见不以为然的.在博文<一种面向作业流(工作流)的轻量级可复用 ...
- 生产者/消费者问题的多种Java实现方式
实质上,很多后台服务程序并发控制的基本原理都可以归纳为生产者/消费者模式,而这是恰恰是在本科操作系统课堂上老师反复讲解,而我们却视而不见不以为然的.在博文<一种面向作业流(工作流)的轻量级可复用 ...
- 通过生产者消费者模式例子讲解Java基类方法wait、notify、notifyAll
wait(),notify()和notifyAll()都是Java基类java.lang.Object的方法. 通俗解释wait():在当前线程等待其它线程唤醒.notify(): 唤醒一个线程正在等 ...
- java23种设计模式专攻:生产者-消费者模式的三种实现方式
公司的架构用到了dubbo.带我那小哥也是个半吊子,顺便就考我生产者消费者模式,顺便还考我23种java设计模式,
- 生产者消费者模型Java实现
生产者消费者模型 生产者消费者模型可以描述为: ①生产者持续生产,直到仓库放满产品,则停止生产进入等待状态:仓库不满后继续生产: ②消费者持续消费,直到仓库空,则停止消费进入等待状态:仓库不空后,继续 ...
- 【1】【JUC】Condition和生产者消费者模型
本篇文章将介绍Condition的实现原理和基本使用方法,基本过程如下: 1.Condition提供了await()方法将当前线程阻塞,并提供signal()方法支持另外一个线程将已经阻塞的线程唤醒. ...
- 线程高级篇-Lock锁实现生产者-消费者模型
Lock锁介绍: 在java中可以使用 synchronized 来实现多线程下对象的同步访问,为了获得更加灵活使用场景.高效的性能,java还提供了Lock接口及其实现类ReentrantLock和 ...
- java多线程面试题整理及答案(2018年)
1) 什么是线程? 线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位.程序员可以通过它进行多处理器编程,你可以使用多线程对 运算密集型任务提速.比如,如果一个线程完 ...
随机推荐
- wdcp php5.3添加pdo_mysql模块
先查看探针: pdo没有支持mysql.导致了PHpwind以及thinkphp框架的一些运用了pdo进行mysql操作的程序无法运行. php5.3默认是封装了pdo_mysq的.那么就没必要单独下 ...
- C语言和go语言之间的交互
一.go代码中使用C代码 go代码中使用C代码,在go语言的函数块中,以注释的方式写入C代码,然后紧跟import "C" 即可在go代码中使用C函数 代码示例: go代码:tes ...
- 【论文:麦克风阵列增强】Microphone Array Post-Filtering For Non-Stationary Noise Suppression
作者:桂. 时间:2017-06-08 08:01:41 链接:http://www.cnblogs.com/xingshansi/p/6957027.html 原文链接:http://pan.ba ...
- 实现容器的底层技术 - 每天5分钟玩转 Docker 容器技术(30)
为了更好地理解容器的特性,本节我们将讨论容器的底层实现技术.cgroup 和 namespace 是最重要的两种技术.cgroup 实现资源限额, namespace 实现资源隔离. cgroup c ...
- ios在Xcode里关于图片的权限设置
<key>NSPhotoLibraryUsageDescription</key> <string>This app requires access to the ...
- 团体队列UVA540 Team Queue(队列简单用法)
题目背景 队列和优先级队列是大多数计算机科学家都知道的数据结构.但是团队队列却不被人熟知,尽管在生活中经常出现.比如,午餐时间的食堂门口的队列就是一个团队队列.在一个团队队列中,每个元素属于一个团队. ...
- 常用JS图片滚动(无缝、平滑、上下左右滚动)代码大全
innerHTML: 设置或获取位于对象起始和结束标签内的 HTML scrollHeight: 获取对象的滚动高度. scrollLeft: 设置或获取位于对象左边界和窗口中目前可见内容的 ...
- 关于对WEB标准以及W3C的理解与认识问题
web标准简单来说可以分为结构.表现和行为.其中结构主要是有HTML标签组成.或许通俗点说,在页面body里面我们写入的标签都是为了页面的结构.表现即指css样式表,通过css可以是页面的结构标签更具 ...
- SICP-1.7-递归函数
递归函数 函数内部直接或间接的调用函数自身 将复杂问题简单化 例子程序 def sum_digits(n): """Return the sum of the digit ...
- C# 短信发送 邮件发送
兴趣是最好的老师. --爱因斯坦 一.实现短信发送 1.使用短信mao的方式进行短信发送,前提要购买硬件设备,这里就不考虑展示了: 2.使用中国网建提供的短信平台,但是用几次后要收费: 我们这里主要介 ...