一.Semaphore 实现信号灯
可以维护当前访问自身的线程个数,并提供的线程机制,使用Semaphore可以控制同时访问资源的线程个数
eg:
package com.it;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
public class SemphoreDemo{
public static void main(String[] args) {
final ExecutorService threadPool = Executors.newCachedThreadPool();
final Semaphore semaphore = new Semaphore(3);
for(int i=0;i<10;i++){
Runnable runnable = new Runnable(){
@Override
public void run() {
try {
semaphore.acquire();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(" 线程 "+Thread.currentThread().getName()+" 进入,当前已有 "+(3-semaphore.availablePermits()));
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(" 线程 "+Thread.currentThread().getName()+" 即将离开 ");
semaphore.release();
System.out.println("线程"+Thread.currentThread().getName()+" 已离开,当前已有 "+(3-semaphore.availablePermits())+"并发");
}
};
threadPool.execute(runnable);
}
}
}
二.CyclicBarrier
eg:
package com.it;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class CyclicBarrierDemo {
/**
* @param args
*/
public static void main(String[] args) {
ExecutorService service = Executors.newCachedThreadPool();
final CyclicBarrier cyclicbarrier = new CyclicBarrier(3);
for(int i=1;i<=3;i++){
Runnable runnable = new Runnable(){
@Override
public void run() {
try {
Thread.sleep((long)Math.random()*10000);
System.out.println("线程"+Thread.currentThread().getName()+
"即将到达集合地点A,当前已有 "+(cyclicbarrier.getNumberWaiting()+1)+" 个线程已经到达"+
(cyclicbarrier.getNumberWaiting()==2?",全到达了,继续走":",请等候"));
cyclicbarrier.await();
Thread.sleep((long)Math.random()*10000);
System.out.println("线程"+Thread.currentThread().getName()+
"即将到达集合地点B,当前已有 "+(cyclicbarrier.getNumberWaiting()+1)+" 个线程已经到达"+
(cyclicbarrier.getNumberWaiting()==2?",全到达了,继续走":",请等候"));
cyclicbarrier.await();
Thread.sleep((long)Math.random()*10000);
System.out.println("线程"+Thread.currentThread().getName()+
"即将到达集合地点C,当前已有 "+(cyclicbarrier.getNumberWaiting()+1)+" 个线程已经到达"+
(cyclicbarrier.getNumberWaiting()==2?",全到达了,继续走":",请等候"));
cyclicbarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}
};
service.execute(runnable);
}
service.shutdown();
}
}
三.CountDownLatch
package com.it;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class CountDownLatchDemo {
/**
* @param args
*/
public static void main(String[] args) {
ExecutorService threadPool = Executors.newCachedThreadPool();
final CountDownLatch latchOrder = new CountDownLatch(1);
final CountDownLatch latchAnswer = new CountDownLatch(3);
for(int i=1;i<=3;i++){
Runnable runnable = new Runnable(){
@Override
public void run() {
try{
System.out.println("线程 "+Thread.currentThread().getName()+" 正准备命令");
latchOrder.await();
System.out.println("线程 "+Thread.currentThread().getName()+" 已接受命令");
Thread.sleep((long)Math.random()*10000);
System.out.println("线程"+Thread.currentThread().getName()+"回应命令处理结束");
latchAnswer.countDown();
}catch(Exception e){
System.err.println(e.toString());
}
}
};
threadPool.execute(runnable);
}
try{
Thread.sleep((long)Math.random()*10000);
System.out.println("线程 "+Thread.currentThread().getName()+" 正准备发出命令");
latchOrder.countDown();
System.out.println("线程 "+Thread.currentThread().getName()+" 已发送命令,正在等待结束");
latchAnswer.await();
System.out.println("线程"+Thread.currentThread().getName()+" 已收到所有响应结果");
}catch(Exception e){
System.err.println(e.toString());
}
threadPool.shutdown();
}
}
四.Exchanger
package com.it;
import java.util.concurrent.Exchanger;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ExchangerDemo {
public static void main(String[] args) {
ExecutorService threadPool = Executors.newCachedThreadPool();
final Exchanger exchanger = new Exchanger();
threadPool.execute(new Runnable(){
@Override
public void run() {
try {
String A_threadData = "A";
System.out.println("线程 "+Thread.currentThread().getName()+"正在把数据"+A_threadData+"换出去");
Thread.sleep((long)Math.random()*10000);
String b_threadData = (String) exchanger.exchange(A_threadData);
System.out.println("线程 "+Thread.currentThread().getName()+"换回的数据 "+b_threadData);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
threadPool.execute(new Runnable(){
@Override
public void run() {
try {
String B_threadData = "B";
System.out.println("线程 "+Thread.currentThread().getName()+"正在把数据"+B_threadData+"换出去");
Thread.sleep((long)Math.random()*10000);
String A_threadData = (String) exchanger.exchange(B_threadData);
System.out.println("线程 "+Thread.currentThread().getName()+"换回的数据 "+A_threadData);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
}
五.Collection 的 并发 与 并发中阻塞队列
eg:
package com.it;
import java.util.Random;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
public class BlockingQueueDemo {
public static void main(String[] args) {
final BlockingQueue<Object> queue = new ArrayBlockingQueue<Object>(3);
new Thread(){
@Override
public void run(){
for(int i=1;i<=3;i++){
//while(true){
try{
Thread.sleep((long)Math.random()*10000);
System.out.println(Thread.currentThread().getName()+"准备填充数据");
queue.put(new Random().nextInt(100)+1);
System.out.println(Thread.currentThread().getName()+"已经填充数据,队列目前有 "+queue.size()+" 个数据");
}catch(InterruptedException e){
System.err.println(e.toString());
}
}
}
}.start();
new Thread(new Runnable(){
@Override
public void run() {
//while(true){
try{
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName()+"准备截取数据");
queue.take();
System.out.println(Thread.currentThread().getName()+"已经截取数据,队列目前有 "+queue.size()+" 个数据");
}catch(InterruptedException e){
System.out.println(e.toString());
}
}
//}
}).start();
}
}
六.空中网3道面试题
第一题
package com.it;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
/**
* 第一题: 现有的程序代码模拟产生了16个日志对象,并且需要运行16秒才能运行打印完日志,请在程序中增加4个线程去调用
* parseLog() 方法来分头打印这16个日志对象,程序需要运行4秒即可打印完这些日志对象
* @author Administrator
*
*/
public class LogThreadDemo {
public static void main(String[] args) {
System.out.println("begin"+(System.currentTimeMillis()/1000));
/**
* 模拟处理16个日志,下面产生了16个日志对象,当前代码运行16秒才能打印完
* 修改程序代码,开四个线程让这16个对象在4秒钟完成
*/
final BlockingQueue<String> queue = new ArrayBlockingQueue<>(16);
for(int i=0;i<4;i++){
new Thread(new Runnable(){
@Override
public void run() {
while(true){
try {
String log = queue.take();
parseLog(log);
} catch (InterruptedException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
}
}
}).start();
}
for(int i=0;i<16;i++){ // 不能改动
final String log = ""+(i+1); // 不能改动
{
//LogThreadDemo.parseLog(log);
try {
queue.put(log);
} catch (InterruptedException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
}
}
}
// parseLog 方法内部的代码不能改动
public static void parseLog(String log){
System.out.println(log+":"+(System.currentTimeMillis()/1000));
try{
Thread.sleep(1000);
}catch(InterruptedException e){
e.printStackTrace();
}
}
}
第二题
/* 第二题:现成程序中的Test类中的代码在不断地产生数据,然后交给TestDo.doSome()方法去处理,就好像生产者在不断地产生数据,消费者在不断消费数据。请将程序改造成有10个线程来消费生成者产生的数据,这些消费者都调用TestDo.doSome()方法去进行处理,故每个消费者都需要一秒才能处理完,程序应保证这些消费者线程依次有序地消费数据,只有上一个消费者消费完后,下一个消费者才能消费数据,下一个消费者是谁都可以,但要保证这些消费者线程拿到的数据是有顺序的。原始代码如下:*/
package queue;
public class Test {
public static void main(String[] args) {
final Semaphore semaphore = new Semaphore(1);
final SynchronousQueue<String> queue = new SynchronousQueue<String>();
System.out.println("begin:"+(System.currentTimeMillis()/1000));
for(int i=0;i<10;i++){
new Thread(new Runnable(){
try{
semaphore.acquire();
String input = queue.task();
String output = TestDo.doSome(input);
System.out.println(Thread.currentThread().getName()+ ":" + output);
semaphore.release();
} catch(Exception e){
e.printStrack();
}
}).start();
}
for(int i=0;i<10;i++){ //这行不能改动
String input = i+""; //这行不能改动
try{
queue.put(input);
}catch(Exception e) {
e.printStack();
}
}
}
}
//不能改动此TestDo类
class TestDo {
public static String doSome(String input){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
String output = input + ":"+ (System.currentTimeMillis() / 1000);
return output;
}
}
/* 第三题:现有程序同时启动了4个线程去调用TestDo.doSome(key, value)方法,由于TestDo.doSome(key, value)方法内的代码是先暂停1秒,然后再输出以秒为单位的当前时间值,所以,会打印出4个相同的时间值,如下所示:
4:4:1258199615
1:1:1258199615
3:3:1258199615
1:2:1258199615
请修改代码,如果有几个线程调用TestDo.doSome(key, value)方法时,传递进去的key相等(equals比较为true),则这几个线程应互斥排队输出结果,即当有两个线程的key都是"1"时,它们中的一个要比另外其他线程晚1秒输出结果,如下所示:
4:4:1258199615
1:1:1258199615
3:3:1258199615
1:2:1258199616
总之,当每个线程中指定的key相等时,这些相等key的线程应每隔一秒依次输出时间值(要用互斥),如果key不同,则并行执行(相互之间不互斥)。原始代码如下: */
package syn;
//不能改动此Test类
public class Test extends Thread{
private TestDo testDo;
private String key;
private String value;
public Test(String key,String key2,String value){
this.testDo = TestDo.getInstance();
/*常量"1"和"1"是同一个对象,下面这行代码就是要用"1"+""的方式产生新的对象,
以实现内容没有改变,仍然相等(都还为"1"),但对象却不再是同一个的效果*/
this.key = key+key2;
this.value = value;
}
public static void main(String[] args) throws InterruptedException{
Test a = new Test("1","","1");
Test b = new Test("1","","2");
Test c = new Test("3","","3");
Test d = new Test("4","","4");
System.out.println("begin:"+(System.currentTimeMillis()/1000));
a.start();
b.start();
c.start();
d.start();
}
public void run(){
testDo.doSome(key, value);
}
}
class TestDo {
private TestDo() {}
private static TestDo _instance = new TestDo();
public static TestDo getInstance() {
return _instance;
}
private CopyOnWriteArraryList<Object> queue = new CopyOnWriteArrayList<Object>();
public void doSome(Object key, String value) {
Object obj = key;
if(!queue.contains(obj)){
queue.add(obj);
}else{
for(Iterator<Object> it = queue.iterator();it.hasNext();) {
Object foo = it.next();
if(foo.equals(obj)){
obj = foo;
}
}
}
synchronized(obj)
// 以大括号内的是需要局部同步的代码,不能改动!
{
try {
Thread.sleep(1000);
System.out.println(key+":"+value + ":"
+ (System.currentTimeMillis() / 1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}