闭锁 CountDownLatch:可以自主设置一个设了多道锁的门,然后当锁全部打开的时候,门就会打开,门之后的代码得到执行。
主要是强调 门之后的代码必须在 在门完全打开之后才可以执行。
package test;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 闭锁
* @author callmehua
*
*/
public class test01 {
public static void main(String[] args) {
//闭锁
CountDownLatch cdl=new CountDownLatch(3);
//固定长度的线程池 当然你可以直接用Thread
ExecutorService service=Executors.newFixedThreadPool(3);
//开启线程
for(int i=0;i<3;i++) {
service.submit(new Runnable() {
@Override
public void run() {
System.out.println("--我的名字是--"+Thread.currentThread().getName()+"我是 sleep 之前的打印!");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//下面这三句 代码 每个线程互不干扰 即我干我的 你干你的 只不过每个线程开了一次锁
System.out.println("--我的名字是--"+Thread.currentThread().getName()+"我要开锁了!");
cdl.countDown();
//不管每个线程开多少锁 或者有的线程不开锁 只要让锁的计数器变为0 则调取 await()方法之后的代码就会执行
//cdl.countDown();
System.out.println("--我的名字是--"+Thread.currentThread().getName()+"我是 countdown 方法之后的打印!");
}
});
}
System.out.println("我正在等待");
try {
//其实await()方法就是设置了一个点 如果触发了 计数器为0的这个事件,才继续执行;
//如果不加awit()方法,则闭锁 就没有起到任何作用,我不管你开没开所,我都会执行
//进行监听计数器 一旦计数器为0 则立马执行 以后的语句
cdl.await();
/**
* 闭锁只影响这部分代码!!!!!!!!!! 其余代码 都是随机性的 所以就不要追究为什么其他代码会有前有后的执行了,这本就是线程的意义(希望你们明白我的意思)
*/
System.out.println("+++++++++++++++++++++++++++"+Thread.currentThread().getName()+"+++++++这才是被影响的地区-------开始"+System.currentTimeMillis());
System.out.println("结束了");
System.out.println("%%%%%%%%%%%%%%%%%%"+Thread.currentThread().getName()+"%%%%%%%%%%%%这才是被影响的地区-------结束"+System.currentTimeMillis());
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
service.shutdown();
}
}
}
执行结果:
我正在等待
--我的名字是--pool-1-thread-2我是 sleep 之前的打印!
--我的名字是--pool-1-thread-1我是 sleep 之前的打印!
--我的名字是--pool-1-thread-3我是 sleep 之前的打印!
--我的名字是--pool-1-thread-3我要开锁了!
--我的名字是--pool-1-thread-2我要开锁了!
--我的名字是--pool-1-thread-1我要开锁了!
--我的名字是--pool-1-thread-2我是 countdown 方法之后的打印!
--我的名字是--pool-1-thread-1我是 countdown 方法之后的打印!
--我的名字是--pool-1-thread-3我是 countdown 方法之后的打印!
+++++++++++++++++++++++++++main+++++++这才是被影响的地区-------开始1523679678024
结束了
%%%%%%%%%%%%%%main%%%%%%%%%%%%%这才是影响的响的地区-------结束1523679678024
栅栏 CyclicBarrier:其实 这个和闭锁有相同之处,用栅栏也可以完成以上代码,只不过是把关键方法的位置调整一下和修改同步 工具类的构造方法就行。
构造方法 CyclicBarrier(int parties, Runnable barrierAction),int值是必要的 设置栅栏个数。
barrierAction不是必要的,定义的是 冲破所有栅栏后所执行的代码,属于新的线程。
关键方法是 await()/await(int parties) 栅栏执行到await(),会先检测栅栏是否全部被冲破,如果没有被冲破,则等待栅栏全部被冲破,然后再执行await()方法之后的代码,这部分代码是属于各自线程的。注意 parties是每次执行await方法所冲破的栅栏个数。
package test;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 栅栏
* @author callmehua
*
*/
public class CyclicBarrierTest {
public static void main(String[] args) {
CyclicBarrier cb=new CyclicBarrier(3,new Runnable() {
//定义栅栏冲破之后的所做的事
@Override
public void run() {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//这里也可以被影响 因为只有冲破所有栅栏 这里才会执行 (属于新的线程了 )
System.out.println("全部冲破--------- 必须执行完此方法,才能继续-执行每个线程 await()方法之后的 语句");
}
});
//固定长度的线程池 当然你可以直接用Thread
ExecutorService service=Executors.newFixedThreadPool(3);
//开启线程
for(int i=0;i<3;i++) {
service.submit(new Runnable() {
@Override
public void run() {
//下面这三句 代码 每个线程互不干扰 即我干我的 你干你的 只不过每个线程开了一次锁
System.out.println("--我的名字是--"+Thread.currentThread().getName()+"我要冲破栅栏了!");
try {
System.out.println("--我的名字是--"+Thread.currentThread().getName()+"我是 sleep 之前的打印!");
Thread.sleep(2000);
//设置栅栏点 初始栅栏为3 如果全都冲破了 则证明可以向下执行 此时线程之间是互相等待的
cb.await();
System.out.println("+++++++++++++++++++++++++++"+Thread.currentThread().getName()+"+++++++这才是被影响的地区-------开始"+System.currentTimeMillis());
System.out.println("成功冲破");
System.out.println("+%%%%%%%%%%%%%%%%%%%%%"+Thread.currentThread().getName()+"%%%%%%%%%%%%%%%%这才是被影响的地区-------结束"+System.currentTimeMillis());
} catch (InterruptedException | BrokenBarrierException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("--我的名字是--"+Thread.currentThread().getName()+"我是 await() 方法之后的打印!");
}
});
}
System.out.println("不被影响");
try {
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
service.shutdown();
}
}
}
运行结果:
不被影响
--我的名字是--pool-1-thread-1我要冲破栅栏了!
--我的名字是--pool-1-thread-1我是 sleep 之前的打印!
--我的名字是--pool-1-thread-3我要冲破栅栏了!
--我的名字是--pool-1-thread-2我要冲破栅栏了!
--我的名字是--pool-1-thread-2我是 sleep 之前的打印!
--我的名字是--pool-1-thread-3我是 sleep 之前的打印!
全部冲破--------- 必须执行完此方法,才能继续-执行每个线程 await()方法之后的 语句
+++++++++++++++++++++++++++pool-1-thread-2+++++++这才是被影响的地区-------开始1523681082181
成功冲破
+++++++++++++++++++++++++++pool-1-thread-1+++++++这才是被影响的地区-------开始1523681082181
成功冲破
+++++++++++++++++++++++++++pool-1-thread-3+++++++这才是被影响的地区-------开始1523681082181
成功冲破
+%%%%%%%%%%%%%%%%%%%%%pool-1-thread-1%%%%%%%%%%%%%%%%这才是被影响的地区-------结束1523681082181
+%%%%%%%%%%%%%%%%%%%%%pool-1-thread-2%%%%%%%%%%%%%%%%这才是被影响的地区-------结束1523681082181
--我的名字是--pool-1-thread-2我是 await() 方法之后的打印!
--我的名字是--pool-1-thread-1我是 await() 方法之后的打印!
+%%%%%%%%%%%%%%%%%%%%%pool-1-thread-3%%%%%%%%%%%%%%%%这才是被影响的地区-------结束1523681082181
--我的名字是--pool-1-thread-3我是 await() 方法之后的打印!
注意:若要用栅栏实现闭锁的那个例子,只需要把 闭锁影响区域的代码放到 栅栏构造方法中第二参数中即可,并且调整栅栏中await()之后的代码。归根结底,栅栏更加灵活,因为可以对每个线程设置栅栏冲破后各自的实现,而闭锁做不到(我是这样理解的)。
信号量 Semaphore:和synchronized差不多,只不过synchronized更加严格一点,只能一个线程可以拿到锁!信号量比较温柔,你可以进来试着看看能不能拿到‘锁’,如果拿到就继续执行,不能拿到就等着。信号量的‘锁’其实就是资源。
package test;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
public class SemaphoreTest {
public static void main(String[] args) {
Semaphore sema=new Semaphore(3);
//定义线程数量
int count = 5;
//固定长度的线程池 当然你可以直接用Thread
ExecutorService service=Executors.newFixedThreadPool(count);
//开启线程
for(int i=0;i<count;i++) {
service.submit(new Runnable() {
@Override
public void run() {
try {
System.out.println("--我的名字是--"+Thread.currentThread().getName()+"我要**试试**拿到 '锁' !"+System.currentTimeMillis());
//获取信息量
sema.acquire();
System.out.println("+++++++++++++++++++++++++++"+Thread.currentThread().getName()+"+++++++这才是被锁的地区-------开始"+System.currentTimeMillis());
System.out.println("--我的名字是--"+Thread.currentThread().getName()+"我要>>>>>已经<<<<<拿到 '锁' !"+System.currentTimeMillis());
//注意 信息量锁住的是 acquire()方法之后的代码 acquire()是拿到信息量,如果拿到了,则向后执行,如果没拿到,则等待
Thread.sleep(2000);
//把信息量还回去
System.out.println("--我的名字是--"+Thread.currentThread().getName()+"我要======还回去====== '锁' !"+System.currentTimeMillis());
sema.release();
/**
* (在上面)切记 不要忘了重置信号量
*/
System.out.println("+%%%%%%%%%%%%%%%%%%%%%"+Thread.currentThread().getName()+"%%%%%%%%%%%%%%%%这才是被锁的地区-------结束"+System.currentTimeMillis());
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("--我的名字是--"+Thread.currentThread().getName()+"我是拿 '锁' 方法之后的打印!"+System.currentTimeMillis());
}
});
}
try {
System.out.println("无关紧要");
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
service.shutdown();
}
}
}
输出结果:
无关紧要
--我的名字是--pool-1-thread-4我要**试试**拿到 '锁' !1523681692681
--我的名字是--pool-1-thread-5我要**试试**拿到 '锁' !1523681692681
+++++++++++++++++++++++++++pool-1-thread-4+++++++这才是被锁的地区-------开始1523681692681
--我的名字是--pool-1-thread-3我要**试试**拿到 '锁' !1523681692681
--我的名字是--pool-1-thread-1我要**试试**拿到 '锁' !1523681692681
--我的名字是--pool-1-thread-2我要**试试**拿到 '锁' !1523681692681
+++++++++++++++++++++++++++pool-1-thread-3+++++++这才是被锁的地区-------开始1523681692681
--我的名字是--pool-1-thread-4我要>>>>>已经<<<<<拿到 '锁' !1523681692681
+++++++++++++++++++++++++++pool-1-thread-5+++++++这才是被锁的地区-------开始1523681692681
--我的名字是--pool-1-thread-5我要>>>>>已经<<<<<拿到 '锁' !1523681692681
--我的名字是--pool-1-thread-3我要>>>>>已经<<<<<拿到 '锁' !1523681692681
--我的名字是--pool-1-thread-3我要======还回去====== '锁' !1523681694682
--我的名字是--pool-1-thread-5我要======还回去====== '锁' !1523681694682
--我的名字是--pool-1-thread-4我要======还回去====== '锁' !1523681694682
+++++++++++++++++++++++++++pool-1-thread-2+++++++这才是被锁的地区-------开始1523681694682
--我的名字是--pool-1-thread-2我要>>>>>已经<<<<<拿到 '锁' !1523681694682
%%%%%%%%%%%%%%%pool-1-thread-5%%%%%%%%%%这才是被锁的地区-------结束1523681694682
--我的名字是--pool-1-thread-5我是拿 '锁' 方法之后的打印!1523681694682
+++++++++++++++++++++++++++pool-1-thread-1+++++++这才是被锁的地区-------开始1523681694682
--我的名字是--pool-1-thread-1我要>>>>>已经<<<<<拿到 '锁' !1523681694682
%%%%%%%%%%%%%%%pool-1-thread-4%%%%%%%%%%这才是被锁的地区-------结束1523681694682
--我的名字是--pool-1-thread-4我是拿 '锁' 方法之后的打印!1523681694682
%%%%%%%%%%%%%%%pool-1-thread-3%%%%%%%%%%这才是被锁的地区-------结束1523681694682
--我的名字是--pool-1-thread-3我是拿 '锁' 方法之后的打印!1523681694682
--我的名字是--pool-1-thread-2我要======还回去====== '锁' !1523681696682
%%%%%%%%%%%%%%%pool-1-thread-2%%%%%%%%%%这才是被锁的地区-------结束1523681696682
--我的名字是--pool-1-thread-2我是拿 '锁' 方法之后的打印!1523681696682
--我的名字是--pool-1-thread-1我要======还回去====== '锁' !1523681696682
%%%%%%%%%%%%%%%pool-1-thread-1%%%%%%%%%%这才是被锁的地区-------结束1523681696682
--我的名字是--pool-1-thread-1我是拿 '锁' 方法之后的打印!1523681696682
总结:注意看代码中的 影响范围,红底色代码。
闭锁,专注于多个线程执行到某个点,然后触发事件,再执行触发事件之后的代码。
栅栏:比闭锁更加灵活。增强版吧。专注于多个线程互相等待执行await()之后的代码,也可以定义一个消息机制,在所有栅栏冲破后,可以执行另一个线程。
信息量:和synchronized相差无几,拿资源,拿‘锁’。只不过可以拿多个而已(一个线程拿一个,多个线程;一个线程拿多个,多个线程)。
我只是个it小白,在工作之余学习学习,在百度相关知识的时候,几乎所有的帖子都是千篇一律。。。可能大家都比我聪明吧,反正我是有点迷惑,只好动手实践下,这样时间花费的时间多是是多了,可是更加明白了。
如有错误,请大家指正。我会认真的修改。