并发编程(三):从AQS到CountDownLatch与ReentrantLock
一、目录
二、AQS简要分析
- 为什么会产生ArrayList、LinkedList、HashMap这些容器?它们底层实现无非都是对数组、链表、树的操作,至于它们的产生,就是因为对编程人员对于数组、链表、树的增删改查操作非常繁琐而提出的解决方案。
- 那为什么会产生AQS呢?谈到同步,大家最容易想到的就是在多线程中如何确保安全的资源共享。那同步队列就是为了解决资源共享的同步容器。像上述容器一样,在顶层就设计好,编程人员只需要调用接口就能轻易实现复杂的资源共享问题。
- 调用自定义同步器的tryAcquire()尝试直接去获取资源,如果成功就返回。
- 没成功,则addWaiter()将线程加入等待队列的尾部,并标记为独享模式。
- acquireQueued()使线程在等待队列中休息,有机会时会去尝试获得资源。获得资源后返回。如果整个过程有中断过返回true,否则返回false。
- 如果线程在等待过程中中断过,它是不响应的。只是获得资源后才再进行自我中断selfInterrupt(),将中断补上。


- tryAcquireShared()尝试获取资源,成功则直接返回。
- 失败则通过 doAcquireShared()进入等待队列,直到被唤醒或者中断并且成功获取资源才返回。
- 不同:独占式是只唤醒后继节点。共享式是唤醒后继,后继还会去唤醒它的后继,从而实现共享。
以上是核心的关于CountDownLatch、ReentrantLock的分析。由于博主研究程度有限,想更深层次研究,请参考:Java并发AQS详解
三、浅谈CountDownLatch
/**
* CountDownLatch相当于指令枪或者门闩,所有线程都awit()阻塞在起跑线,只有countDown到state为0,其他线程才能往下运行。
* @author qiuyongAaron
*/
public class CountDownLatchDemo {
private static final int PLAYER_NUM=5;</span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">static</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> main(String[] args) { CountDownLatch start</span>=<span style="color: #0000ff;">new</span> CountDownLatch(1<span style="color: #000000;">);
CountDownLatch end </span>=<span style="color: #0000ff;">new</span><span style="color: #000000;"> CountDownLatch(PLAYER_NUM);
Player [] players</span>=<span style="color: #0000ff;">new</span><span style="color: #000000;"> Player[PLAYER_NUM]; </span><span style="color: #0000ff;">for</span>(<span style="color: #0000ff;">int</span> i=0;i<PLAYER_NUM;i++<span style="color: #000000;">)
players[i]</span>=<span style="color: #0000ff;">new</span><span style="color: #000000;"> Player(start, end, i);
</span><span style="color: #008000;">//</span><span style="color: #008000;">指定线程个数的线程池!</span>
ExecutorService exe=<span style="color: #000000;">Executors.newFixedThreadPool(PLAYER_NUM);
</span><span style="color: #0000ff;">for</span><span style="color: #000000;">(Player player:players)
exe.execute(player); System.out.println(</span>"比赛开始!"<span style="color: #000000;">);
</span><span style="color: #008000;">//</span><span style="color: #008000;">比赛开始!</span>
start.countDown();
</span><span style="color: #0000ff;">try</span><span style="color: #000000;"> {
end.await();
} </span><span style="color: #0000ff;">catch</span><span style="color: #000000;"> (InterruptedException e) {
e.printStackTrace();
}</span><span style="color: #0000ff;">finally</span><span style="color: #000000;">{
System.out.println(</span>"比赛结束!"<span style="color: #000000;">);
exe.shutdown();
}
}
}
class Player implements Runnable{
private CountDownLatch start;
private CountDownLatch end;
private int id;Random random</span>=<span style="color: #0000ff;">new</span><span style="color: #000000;"> Random();
</span><span style="color: #0000ff;">public</span> Player(CountDownLatch start,CountDownLatch end,<span style="color: #0000ff;">int</span><span style="color: #000000;"> id) {
</span><span style="color: #0000ff;">this</span>.start=<span style="color: #000000;">start;
</span><span style="color: #0000ff;">this</span>.end=<span style="color: #000000;">end;
</span><span style="color: #0000ff;">this</span>.id=<span style="color: #000000;">id;
} @Override
</span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> run() {
</span><span style="color: #0000ff;">try</span><span style="color: #000000;"> {
</span><span style="color: #008000;">//</span><span style="color: #008000;">等待比赛开始。</span>
start.await();
TimeUnit.SECONDS.sleep(random.nextInt(10));
System.out.println("Player-"+id+":arrived");
} catch (InterruptedException e) {
e.printStackTrace();
}finally{
//选手-id到达终点,end计数为0结束比赛!
end.countDown();
}
}
}//运行结果:
比赛开始!
Player-3:arrived
Player-4:arrived
Player-0:arrived
Player-1:arrived
Player-2:arrived
比赛结束!
三、谈ReentrantLock
/**
* 示例一:同步锁的使用
* reentrantlock用于替代synchronized
* 本例中由于m1锁定this,只有m1执行完毕的时候,m2才能执行
* @author qiuyongAaron
*/
public class ReentrantLockOne {
public synchronized void m1(){
for(int i=0;i<10;i++){
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(i);
}
}</span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">synchronized</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> m2(){
System.out.println(</span>"hello m2!"<span style="color: #000000;">);
} </span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">static</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> main(String[] args) {
ReentrantLockOne lock</span>=<span style="color: #0000ff;">new</span><span style="color: #000000;"> ReentrantLockOne(); </span><span style="color: #0000ff;">new</span> Thread(()->lock.m1(),"t1"<span style="color: #000000;">).start(); </span><span style="color: #0000ff;">try</span><span style="color: #000000;"> {
TimeUnit.SECONDS.sleep(</span>2<span style="color: #000000;">);
} </span><span style="color: #0000ff;">catch</span><span style="color: #000000;"> (InterruptedException e) {
e.printStackTrace();
} </span><span style="color: #0000ff;">new</span> Thread(()->lock.m2(),"t2"<span style="color: #000000;">).start();
}
}
Synchronized实现线程同步
2.2 ReentrantLock实现线程同步-与synchronized作用一致!
/**
* 示例二:等价于同步锁
* 使用reentrantlock可以完成同样的功能
* 需要注意的是,必须要必须要必须要手动释放锁(重要的事情说三遍)
* 使用syn锁定的话如果遇到异常,jvm会自动释放锁,但是lock必须手动释放锁,因此经常在finally中进行锁的释放
* @author qiuyongAaron
*/
public class ReentrantLockTwo {
ReentrantLock lock =new ReentrantLock();
public void m1(){
try {
lock.lock();
for(int i=0;i<10;i++){
TimeUnit.SECONDS.sleep(1);
System.out.println(i);
}
} catch (InterruptedException e) {
e.printStackTrace();
}finally{
lock.unlock();
}
}</span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">synchronized</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> m2(){
lock.lock();
System.out.println(</span>"hello m2!"<span style="color: #000000;">);
lock.unlock();
} </span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">static</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> main(String[] args) {
ReentrantLockTwo lock</span>=<span style="color: #0000ff;">new</span><span style="color: #000000;"> ReentrantLockTwo(); </span><span style="color: #0000ff;">new</span> Thread(()->lock.m1(),"t1"<span style="color: #000000;">).start(); </span><span style="color: #0000ff;">try</span><span style="color: #000000;"> {
TimeUnit.SECONDS.sleep(</span>2<span style="color: #000000;">);
} </span><span style="color: #0000ff;">catch</span><span style="color: #000000;"> (InterruptedException e) {
e.printStackTrace();
} </span><span style="color: #0000ff;">new</span> Thread(()->lock.m2(),"t2"<span style="color: #000000;">).start();
}
}
ReentrantLock同步互斥
/**
* 示例三:tryLock
* 使用reentrantlock可以进行“尝试锁定”tryLock,这样无法锁定,或者在指定时间内无法锁定,线程可以决定是否继续等待
* @author qiuyongAaron
*/
public class ReentrantLockThree {
ReentrantLock lock=new ReentrantLock();</span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> m1(){
</span><span style="color: #0000ff;">try</span><span style="color: #000000;"> {
lock.lock();
</span><span style="color: #0000ff;">for</span>(<span style="color: #0000ff;">int</span> i=0;i<10;i++<span style="color: #000000;">){
TimeUnit.SECONDS.sleep(</span>1<span style="color: #000000;">);
System.out.println(i);
}
} </span><span style="color: #0000ff;">catch</span><span style="color: #000000;"> (Exception e) {
e.printStackTrace();
}</span><span style="color: #0000ff;">finally</span><span style="color: #000000;">{
lock.unlock();
}
} </span><span style="color: #0000ff;">boolean</span> locked=<span style="color: #0000ff;">false</span><span style="color: #000000;">;
</span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> m2(){
</span><span style="color: #0000ff;">try</span><span style="color: #000000;"> {
lock.tryLock(</span>5<span style="color: #000000;">,TimeUnit.SECONDS);
System.out.println(</span>"m2:"+<span style="color: #000000;">locked);
} </span><span style="color: #0000ff;">catch</span><span style="color: #000000;"> (Exception e) {
e.printStackTrace();
}</span><span style="color: #0000ff;">finally</span><span style="color: #000000;">{
</span><span style="color: #0000ff;">if</span><span style="color: #000000;">(locked) lock.unlock();
}
} </span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">static</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> main(String[] args) {
ReentrantLockThree lock</span>=<span style="color: #0000ff;">new</span><span style="color: #000000;"> ReentrantLockThree(); </span><span style="color: #0000ff;">new</span> Thread(()->lock.m1(),"t1"<span style="color: #000000;">).start(); </span><span style="color: #0000ff;">try</span><span style="color: #000000;"> {
TimeUnit.SECONDS.sleep(</span>1<span style="color: #000000;">);
} </span><span style="color: #0000ff;">catch</span><span style="color: #000000;"> (InterruptedException e) {
e.printStackTrace();
} </span><span style="color: #0000ff;">new</span> Thread(()->lock.m2(),"t2"<span style="color: #000000;">).start();
}
}
ReentrantLock尝试获取锁
2.4 指定公平锁或者抢占式锁
/**
* ReentrantLock还可以指定为公平锁
* @author qiuyongAaron
*/
public class ReentrantLockFive extends Thread{</span><span style="color: #008000;">//</span><span style="color: #008000;">默认false:为非公平锁 true:公平锁</span>
ReentrantLock lock=<span style="color: #0000ff;">new</span><span style="color: #000000;"> ReentrantLock(); @Override
</span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> run() {
</span><span style="color: #0000ff;">for</span>(<span style="color: #0000ff;">int</span> i=0;i<100;i++<span style="color: #000000;">){
lock.lock();
</span><span style="color: #0000ff;">try</span><span style="color: #000000;"> {
TimeUnit.SECONDS.sleep(</span>1<span style="color: #000000;">);
System.out.println(Thread.currentThread().getName()</span>+"获得锁"+"-"+<span style="color: #000000;">i);
} </span><span style="color: #0000ff;">catch</span><span style="color: #000000;"> (InterruptedException e) {
e.printStackTrace();
}</span><span style="color: #0000ff;">finally</span><span style="color: #000000;">{
lock.unlock();
}
} } </span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">static</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> main(String[] args) {
ReentrantLockFive lock</span>=<span style="color: #0000ff;">new</span><span style="color: #000000;"> ReentrantLockFive();
</span><span style="color: #0000ff;">new</span> Thread(lock,"t1"<span style="color: #000000;">).start();
</span><span style="color: #0000ff;">new</span> Thread(lock,"t2"<span style="color: #000000;">).start();
}
}
运行结果:
//非公平锁
t2获得锁-0 t2获得锁-1 t1获得锁-0 t1获得锁-1 t1获得锁-2 t2获得锁-2
//公平锁
t1获得锁-0 t2获得锁-0 t1获得锁-1 t2获得锁-1 t1获得锁-2 t2获得锁-2
ReentrantLock公平锁
/**
* 模拟生产者消费者模式-线程之间通信 synchronized-notifyAll/wait
* @author qiuyongAaron
*/
public class MyContainerOne {
LinkedList<Integer> list=new LinkedList<Integer>();
static final int MAX=10;
int count=0;</span><span style="color: #008000;">//</span><span style="color: #008000;">生产者线程</span>
<span style="color: #0000ff;">public</span> <span style="color: #0000ff;">synchronized</span> <span style="color: #0000ff;">void</span> put(<span style="color: #0000ff;">int</span><span style="color: #000000;"> i){
</span><span style="color: #0000ff;">while</span>(list.size()==<span style="color: #000000;">MAX){
</span><span style="color: #0000ff;">try</span><span style="color: #000000;"> {
</span><span style="color: #0000ff;">this</span><span style="color: #000000;">.wait();
} </span><span style="color: #0000ff;">catch</span><span style="color: #000000;"> (InterruptedException e) {
e.printStackTrace();
}
}
list.add(i);
</span>++<span style="color: #000000;">count;
</span><span style="color: #0000ff;">this</span>.notifyAll();<span style="color: #008000;">//</span><span style="color: #008000;">通知消费者来消费</span>
}
</span><span style="color: #008000;">//</span><span style="color: #008000;">消费者线程</span>
<span style="color: #0000ff;">public</span> <span style="color: #0000ff;">synchronized</span> <span style="color: #0000ff;">int</span><span style="color: #000000;"> get(){
</span><span style="color: #0000ff;">while</span>(list.size()==0<span style="color: #000000;">){
</span><span style="color: #0000ff;">try</span><span style="color: #000000;"> {
</span><span style="color: #0000ff;">this</span><span style="color: #000000;">.wait();
} </span><span style="color: #0000ff;">catch</span><span style="color: #000000;"> (InterruptedException e) {
e.printStackTrace();
}
}
</span><span style="color: #0000ff;">int</span> num=<span style="color: #000000;">list.removeFirst();
count</span>--<span style="color: #000000;">;
</span><span style="color: #0000ff;">this</span>.notifyAll();<span style="color: #008000;">//</span><span style="color: #008000;">通知生产者生产</span>
<span style="color: #0000ff;">return</span><span style="color: #000000;"> num;
} </span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">static</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> main(String[] args) {
MyContainerOne container</span>=<span style="color: #0000ff;">new</span><span style="color: #000000;"> MyContainerOne(); </span><span style="color: #008000;">//</span><span style="color: #008000;">制造10个消费者</span>
<span style="color: #0000ff;">for</span>(<span style="color: #0000ff;">int</span> i=0;i<10;i++<span style="color: #000000;">){
</span><span style="color: #0000ff;">new</span> Thread(()-><span style="color: #000000;">{
</span><span style="color: #0000ff;">for</span>(<span style="color: #0000ff;">int</span> j=0;j<5;j++<span style="color: #000000;">) System.out.println(container.get());
},
</span>"c"+<span style="color: #000000;">i).start();
} </span><span style="color: #0000ff;">try</span><span style="color: #000000;"> {
TimeUnit.SECONDS.sleep(</span>2<span style="color: #000000;">);
} </span><span style="color: #0000ff;">catch</span><span style="color: #000000;"> (InterruptedException e) {
e.printStackTrace();
} </span><span style="color: #008000;">//</span><span style="color: #008000;">制造2个生产者</span>
<span style="color: #0000ff;">for</span>(<span style="color: #0000ff;">int</span> i=0;i<2;i++<span style="color: #000000;">){
</span><span style="color: #0000ff;">new</span> Thread(()-><span style="color: #000000;">{
</span><span style="color: #0000ff;">for</span>(<span style="color: #0000ff;">int</span> j=0;j<25;j++<span style="color: #000000;">) container.put(j);
},
</span>"p"+<span style="color: #000000;">i).start();
}
}
}
/**
* 模拟生产者消费者模式-reentrantLock-awit/signAll
* @author qiuyongAaron
*/
public class MyContainerTwo {LinkedList</span><Integer> list=<span style="color: #0000ff;">new</span> LinkedList<Integer><span style="color: #000000;">();
</span><span style="color: #0000ff;">static</span> <span style="color: #0000ff;">final</span> <span style="color: #0000ff;">int</span> MAX=10<span style="color: #000000;">;
</span><span style="color: #0000ff;">int</span> count=0<span style="color: #000000;">; ReentrantLock lock</span>=<span style="color: #0000ff;">new</span><span style="color: #000000;"> ReentrantLock();
Condition producer</span>=<span style="color: #000000;">lock.newCondition();
Condition consumer</span>=<span style="color: #000000;">lock.newCondition(); </span><span style="color: #008000;">//</span><span style="color: #008000;">生产者线程</span>
<span style="color: #0000ff;">public</span> <span style="color: #0000ff;">void</span> put(<span style="color: #0000ff;">int</span><span style="color: #000000;"> i){
</span><span style="color: #0000ff;">try</span><span style="color: #000000;"> {
lock.lock();
</span><span style="color: #0000ff;">while</span>(list.size()==<span style="color: #000000;">MAX){
producer.await();
}
list.add(i);
</span>++<span style="color: #000000;">count;
consumer.signalAll();</span><span style="color: #008000;">//</span><span style="color: #008000;">通知消费者来消费</span>
} <span style="color: #0000ff;">catch</span><span style="color: #000000;"> (InterruptedException e){
e.printStackTrace();
}</span><span style="color: #0000ff;">finally</span><span style="color: #000000;">{
lock.unlock();
}
} </span><span style="color: #008000;">//</span><span style="color: #008000;">消费者线程</span>
<span style="color: #0000ff;">public</span> <span style="color: #0000ff;">int</span><span style="color: #000000;"> get(){
</span><span style="color: #0000ff;">try</span><span style="color: #000000;">{
lock.lock();
</span><span style="color: #0000ff;">while</span>(list.size()==0<span style="color: #000000;">){
consumer.await();
}
</span><span style="color: #0000ff;">int</span> num=<span style="color: #000000;">list.removeFirst();
count</span>--<span style="color: #000000;">;
producer.signalAll();</span><span style="color: #008000;">//</span><span style="color: #008000;">通知生产者生产</span>
<span style="color: #0000ff;">return</span><span style="color: #000000;"> num;
}</span><span style="color: #0000ff;">catch</span><span style="color: #000000;">(Exception e){
e.printStackTrace();
}</span><span style="color: #0000ff;">finally</span><span style="color: #000000;">{
lock.unlock();
}
</span><span style="color: #0000ff;">return</span> 0<span style="color: #000000;">;
} </span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">static</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> main(String[] args) {
MyContainerTwo container</span>=<span style="color: #0000ff;">new</span><span style="color: #000000;"> MyContainerTwo(); </span><span style="color: #008000;">//</span><span style="color: #008000;">制造10个消费者</span>
<span style="color: #0000ff;">for</span>(<span style="color: #0000ff;">int</span> i=0;i<10;i++<span style="color: #000000;">){
</span><span style="color: #0000ff;">new</span> Thread(()-><span style="color: #000000;">{
</span><span style="color: #0000ff;">for</span>(<span style="color: #0000ff;">int</span> j=0;j<5;j++<span style="color: #000000;">) System.out.println(container.get());
},
</span>"c"+<span style="color: #000000;">i).start();
} </span><span style="color: #0000ff;">try</span><span style="color: #000000;"> {
TimeUnit.SECONDS.sleep(</span>2<span style="color: #000000;">);
} </span><span style="color: #0000ff;">catch</span><span style="color: #000000;"> (InterruptedException e) {
e.printStackTrace();
} </span><span style="color: #008000;">//</span><span style="color: #008000;">制造2个生产者</span>
<span style="color: #0000ff;">for</span>(<span style="color: #0000ff;">int</span> i=0;i<2;i++<span style="color: #000000;">){
</span><span style="color: #0000ff;">new</span> Thread(()-><span style="color: #000000;">{
</span><span style="color: #0000ff;">for</span>(<span style="color: #0000ff;">int</span> j=0;j<25;j++<span style="color: #000000;">) container.put(j);
},
</span>"p"+<span style="color: #000000;">i).start();
}
}
}
四、版权声明
作者:邱勇Aaron
出处:http://www.cnblogs.com/qiuyong/
您的支持是对博主深入思考总结的最大鼓励。
本文版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,尊重作者的劳动成果。
参考:马士兵并发编程、并发编程实践
AQS详解:http://www.cnblogs.com/waterystone/p/4920797.html
并发编程(三):从AQS到CountDownLatch与ReentrantLock的更多相关文章
- 并发编程JUC系列AQS(CountDownLatch、CyclicBarrier、Semaphore)
一.CountDownLatch package com.jonychen.test; import java.util.concurrent.CountDownLatch; import java. ...
- 【Java并发编程实战】----- AQS(三):阻塞、唤醒:LockSupport
在上篇博客([Java并发编程实战]----- AQS(二):获取锁.释放锁)中提到,当一个线程加入到CLH队列中时,如果不是头节点是需要判断该节点是否需要挂起:在释放锁后,需要唤醒该线程的继任节点 ...
- Java高并发编程基础三大利器之CountDownLatch
引言 上一篇文章我们介绍了AQS的信号量Semaphore<Java高并发编程基础三大利器之Semaphore>,接下来应该轮到CountDownLatch了. 什么是CountDownL ...
- 【Java并发编程实战】----- AQS(二):获取锁、释放锁
上篇博客稍微介绍了一下AQS,下面我们来关注下AQS的所获取和锁释放. AQS锁获取 AQS包含如下几个方法: acquire(int arg):以独占模式获取对象,忽略中断. acquireInte ...
- Java并发编程三个性质:原子性、可见性、有序性
并发编程 并发程序要正确地执行,必须要保证其具备原子性.可见性以及有序性:只要有一个没有被保证,就有可能会导致程序运行不正确 线程不安全在编译.测试甚至上线使用时,并不一定能发现,因为受到当时的 ...
- 【Java并发编程实战】-----“J.U.C”:ReentrantLock之三unlock方法分析
前篇博客LZ已经分析了ReentrantLock的lock()实现过程,我们了解到lock实现机制有公平锁和非公平锁,两者的主要区别在于公平锁要按照CLH队列等待获取锁,而非公平锁无视CLH队列直接获 ...
- 【Java并发编程实战】-----“J.U.C”:ReentrantLock之一简介
注:由于要介绍ReentrantLock的东西太多了,免得各位客官看累,所以分三篇博客来阐述.本篇博客介绍ReentrantLock基本内容,后两篇博客从源码级别分别阐述ReentrantLock的l ...
- Java并发编程-看懂AQS的前世今生
在具备了volatile.CAS和模板方法设计模式的知识之后,我们可以来深入学习下AbstractQueuedSynchronizer(AQS),本文主要想从AQS的产生背景.设计和结构.源代码实现及 ...
- Java并发编程:用AQS写一把可重入锁
Java并发编程:自己动手写一把可重入锁详述了如何用synchronized同步的方式来实现一把可重入锁,今天我们来效仿ReentrantLock类用AQS来改写一下这把锁.要想使用AQS为我们服务, ...
随机推荐
- OpenCV探索之路(三):滤波操作
滤波处理分为两大类:线性滤波和非线性滤波.OpenCV里有这些滤波的函数,使用起来非常方便,现在简单介绍其使用方法. 线性滤波:方框滤波.均值滤波.高斯滤波 方框滤波 #include<open ...
- OAuth及第三方登录
现在的生活中运用互联网的有好多地方,我们既要申请微博,申请博客,申请邮箱等等:哪怕登录一个小网址看点东西都要注册登录,不过现在好多了:有了第三方登录,再也不用担心这不够用的脑子整天记忆账号和密码了,只 ...
- 使用r.js优化静态资源
r.js主要功能:优化项目的静态资源.可以简化压缩代码,减少体积.指定模块将多个组件合并为一个文件,减少HTTP请求数量.具体使用步骤如下: 先把 r.js 文件放到项目根目录,再于项目根目录内新建一 ...
- Greys学习笔记(未完待续)
Greys介绍 greys-anatomy是一个Java线上诊断工具,取名来自美剧<实习医生格雷>,由菜鸟-杜琨同学开发维护.比我们常用的脚本工具btrace提供更多的功能,greys采用 ...
- RabbitMQ_安装配置与管理
RabbitMQ 安装配置与管理 安装 安装erlang虚拟机 Rabbitmq基于erlang语言开发,所有需要安装erlang虚拟机 #wget http://www.erlang.org/do ...
- iOS开发之 Lottie -- 炫酷的动效
动效在软件开发中非常常见,炫酷的动画能提升应用的B格,然而由设计师的设计转化成程序猿GG的代码是个非常"痛苦"的过程.对于复杂动画,可能要花费很多时间去研究和实现.Lottie 的 ...
- (数字IC)低功耗设计入门(六)——门级电路低功耗设计优化
三.门级电路低功耗设计优化 (1)门级电路的功耗优化综述 门级电路的功耗优化(Gate Level Power Optimization,简称GLPO)是从已经映射的门级网表开始,对设计进行功耗的优化 ...
- 在容器中利用Nginx-proxy实现多域名的自动反向代理、免费SSL证书
在个人的小项目或者测试环境中,配置反向代理显得十分繁琐,而借助 Nginx-proxy 的镜像,即使是小白,也能快速实现域名转发. 1.域名.IP自动转发 在开始之前,首先黑进了自家的路由器,将某个域 ...
- VR上天了!全景商业化落地了!——VR全景智慧城市
几年前,VR创业公司SpaceVR就启动了旨在将宇航员视觉体验带给普通人的虚拟现实(VR)项目.SpaceVR计划将VR相机卫星送入太空,并将相机拍摄到的太空视频发送回地球,从而让VR用户身临其境地看 ...
- Scrapy的debug方式
Scrapy不方便调试,但是为了深入学习框架内部的一些原理,有时候仅仅依靠日志是不够的.下面提供一种scrapy的debug方式 demo直接用来自官方例子来演示:https://github.com ...