黑马程序员_多线程(5) 同步工具类&空中网面试题

本文通过具体实例介绍了Java并发编程中的核心概念,包括Semaphore信号灯、CyclicBarrier循环栅栏、CountDownLatch倒计时锁存器、Exchanger交换器以及阻塞队列的应用,帮助读者深入理解并发控制机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

-------  android培训java培训、期待与您交流! ----------


一.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();
				}
			}
		}

	}







------- android培训java培训、期待与您交流! ----------
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值