【9】java几个常用线程类简介

1、Semaphere

    Semaphore可以维护当前访问自身的线程个数,并提供了同步机制。使用Semaphore可以控制同时访问资源的线程个数,例如,实现一个文件允许的并发访问数。
    Semaphore实现的功能就类似厕所有5个坑,假如有十个人要上厕所,那么同时能有多少个人去上厕所呢?同时只能有5个人能够占用,当5个人中的任何一个人让开后,其中在等待的另外5个人中又有一个可以占用了。另外等待的5个人中可以是随机获得优先机会,也可以是按照先来后到的顺序获得机会,这取决于构造Semaphore对象时传入的参数选项。
    单个信号量的Semaphore对象可以实现互斥锁的功能,并且可以是由一个线程获得了“锁”,再由另一个线程释放“锁”,这可应用于死锁恢复的一些场合。当然由一个线程获得“锁”,然后释放“锁”也行。

public class SemaphoreTest {
public static void main(String[] args) {
	ExecutorService service = Executors.newCachedThreadPool();
	final  Semaphore sp = new Semaphore(3);
	for(int i=0;i<10;i++){
		Runnable runnable = new Runnable(){
		public void run(){
		try {
				sp.acquire();
			} catch (InterruptedException e1) {
			e1.printStackTrace();
			}
System.out.println("线程" + Thread.currentThread().getName() + "进入,当前已有" + (3-sp.availablePermits()) + "个并发");
		try {
			Thread.sleep((long)(Math.random()*10000));
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
System.out.println("线程" + Thread.currentThread().getName() + "即将离开");					
			sp.release();
			//下面代码有时候执行不准确,因为其没有和上面的代码合成原子单元
System.out.println("线程" + Thread.currentThread().getName() + "已离开,当前已有" + (3-sp.availablePermits()) + "个并发");					
			}
			};
			service.execute(runnable);
		}
	}
}

示例:3个坑 10个人
厕所,有多少人都能装,线程数动态变化,来一个人产生一个线程
ExecutorService service = Exccutors.newCachedThreadPool();
final Semaphore sp = new Semaphore(3);厕所中坑的个数  指定只有3个
3个坑,来了5个人,有2个人要等,其中有一个办完事走了,等待的2个哪个先上呢?默认的构造方法不管,谁抢到了谁上。用new Semaphore(3, true)就可以保证先来的先上。
将坑的个数设置为1就可以达到互斥效果,每次只能有一个线程运行
for (int i=0; i<10; i++)来了10个人
{人的任务  抢坑
Runnable runnable = new Runnable()
{
public void run()
{
sp.acquire();抢坑了 会抛中断异常
}有人占住坑了,给出提示
SOP(currentThreadName+进入,当前已有(3-sp.availablePermits())个人了)
Thread.sleep(5000)蹲坑办事
办完事打声招呼
SOP(ThreadName即将离开)
释放坑的占有权
sp.release();
SOP(ThreadName已经走了,还有sp.availablePermits()个坑可用)
}
开始任务吧
service.execute(runnable)
}
传统互斥只能内部释放锁this.unlock(),进去this.lock()晕倒了别人就没法进去了;用信号灯可以外部释放,其他线程可以释放再获取sp.release()   sp.acquire()。

2、CyclicBarrier

讲解CyclicBarrier的功能时,通过辅助画图的方式说明,效果会更好。
\              /
  \     |    /
------------------------三个线程干完各自的任务,在不同的时刻到达集合点后,就可以接着忙各自的工作去了,再到达新的集合点,再去忙各自的工作,
到达集合点了用CyclicBarrier对象的await方法表示。
  /     |   \
/       |     \
-------------------

public class CyclicBarrierTest {

	public static void main(String[] args) {
		ExecutorService service = Executors.newCachedThreadPool();
		final  CyclicBarrier cb = new CyclicBarrier(3);
		for(int i=0;i<3;i++){
			Runnable runnable = new Runnable(){
			public void run(){
			try {
			Thread.sleep((long)(Math.random()*10000));	
System.out.println("线程" + Thread.currentThread().getName() + "即将到达集合地点1,当前已有" + (cb.getNumberWaiting()+1) + "个已经到达," + (cb.getNumberWaiting()==2?"都到齐了,继续走啊":"正在等候"));						
			cb.await();
						
		Thread.sleep((long)(Math.random()*10000));	
System.out.println("线程" + Thread.currentThread().getName() + "即将到达集合地点2,当前已有" + (cb.getNumberWaiting()+1) + "个已经到达," + (cb.getNumberWaiting()==2?"都到齐了,继续走啊":"正在等候"));
			cb.await();	
		Thread.sleep((long)(Math.random()*10000));	
System.out.println("线程" + Thread.currentThread().getName() + "即将到达集合地点3,当前已有" + (cb.getNumberWaiting() + 1) + "个已经到达," + (cb.getNumberWaiting()==2?"都到齐了,继续走啊":"正在等候"));						
			cb.await();						
			} catch (Exception e) {
			e.printStackTrace();
			}}};
			service.execute(runnable);
		}
		service.shutdown();
	}
}

3、CountDownLatch

    犹如倒计时计数器,调用CountDownLatch对象的countDown方法就将计数器减1,当计数到达0时,则所有等待者或单个等待者开始执行。这直接通过代码来说明CountDownLatch的作用,这样学员的理解效果更直接。

public class CountdownLatchTest {

	public static void main(String[] args) {
		ExecutorService service = Executors.newCachedThreadPool();
		final CountDownLatch cdOrder = new CountDownLatch(1);
		final CountDownLatch cdAnswer = new CountDownLatch(3);		
		for(int i=0;i<3;i++){
			Runnable runnable = new Runnable(){
					public void run(){
					try {
						System.out.println("线程" + Thread.currentThread().getName() + 
								"正准备接受命令");						
						cdOrder.await();
						System.out.println("线程" + Thread.currentThread().getName() + 
						"已接受命令");								
						Thread.sleep((long)(Math.random()*10000));	
						System.out.println("线程" + Thread.currentThread().getName() + 
								"回应命令处理结果");						
						cdAnswer.countDown();						
					} catch (Exception e) {
						e.printStackTrace();
					}				
				}
			};
			service.execute(runnable);
		}		
		try {
			Thread.sleep((long)(Math.random()*10000));
		
			System.out.println("线程" + Thread.currentThread().getName() + 
					"即将发布命令");						
			cdOrder.countDown();
			System.out.println("线程" + Thread.currentThread().getName() + 
			"已发送命令,正在等待结果");	
			cdAnswer.await();
			System.out.println("线程" + Thread.currentThread().getName() + 
			"已收到所有响应结果");	
		} catch (Exception e) {
			e.printStackTrace();
		}				
		service.shutdown();

	}
}

4、Exchanger

    用于实现两个人之间的数据交换,每个人在完成一定的事务后想与对方交换数据,第一个先拿出数据的人将一直等待第二个人拿着数据到来时,才能彼此交换数据。

public class ExchangerTest {

	public static void main(String[] args) {
		ExecutorService service = Executors.newCachedThreadPool();
		final Exchanger<String> exchanger = new Exchanger<String>();
		service.execute(new Runnable(){
			public void run() {
				try {				
					String data1 = "zxx";
					System.out.println("线程" + Thread.currentThread().getName() + 
					"正在把数据" + data1 +"换出去");
					Thread.sleep((long)(Math.random()*10000));
					String data2 = (String)exchanger.exchange(data1);
					System.out.println("线程" + Thread.currentThread().getName() + 
					"换回的数据为" + data2);
				}catch(Exception e){					
				}
			}	
		});
		service.execute(new Runnable(){
			public void run() {
				try {		
					String data1 = "lhm";
					System.out.println("线程" + Thread.currentThread().getName() + 
					"正在把数据" + data1 +"换出去");
					Thread.sleep((long)(Math.random()*10000));					
					String data2 = (String)exchanger.exchange(data1);
					System.out.println("线程" + Thread.currentThread().getName() + 
					"换回的数据为" + data2);
				}catch(Exception e){
					
				}				
			}	
		});	
		service.shutdown();
	}
}

5、BlockingQueue

public class BlockingQueueTest {
	public static void main(String[] args) {
		final BlockingQueue<Integer> queue = new ArrayBlockingQueue<Integer>(3);
		for(int i=0;i<2;i++){
			new Thread(){
				public void run(){
					while(true){
						try {
							Thread.sleep((long)(Math.random()*1000));
					System.out.println(Thread.currentThread().getName() + "准备放数据!");							
							queue.put(1);
					System.out.println(Thread.currentThread().getName() + "已经放了数据," + 							
					"队列目前有" + queue.size() + "个数据");
						} catch (InterruptedException e) {
							e.printStackTrace();
						}

					}
				}
				
			}.start();
		}
		
		new Thread(){
			public void run(){
				while(true){
					try {
						//将此处的睡眠时间分别改为100和1000,观察运行结果
						Thread.sleep(1000);
					System.out.println(Thread.currentThread().getName() + "准备取数据!");
						queue.take();
					System.out.println(Thread.currentThread().getName() + "已经取走数据," + 							
								"队列目前有" + queue.size() + "个数据");					
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
			
		}.start();			
	}
}

6、java5同步集合类的应用

       传统集合类在并发访问时是有问题的,也就是说是线程不安全的。传统方式下用Collections工具类提供的synchronizedCollection方法来获得同步集合,分析该方法的实现源码。 Collections.synchronizedMap(Map<K,V> m)返回的是一个SynchronizedMap。这个类是位于Collections中的内部类,其运用了代理模式,给底层的Map加上synchronized。传统方式下的Collection在迭代集合时,不允许对集合进行修改。下面这段代码将抛出ConcurrentModificationException异常:

public class CollectionModifyExceptionTest {
	public static void main(String[] args) {
		Collection users = new ArrayList();//new CopyOnWriteArrayList();
		users.add(new User("张三",28));	
		users.add(new User("李四",25));			
		users.add(new User("王五",31));	
		Iterator itrUsers = users.iterator();
		while(itrUsers.hasNext()){
			System.out.println("aaaa");
			User user = (User)itrUsers.next();
			if("张三".equals(user.getName())){
				users.remove(user);
			} else {
				System.out.println(user);				
			}
		}
	}
}

如果删除的是李四,则不会抛异常,但只会输出张三,王五没有输出。
如果删除的是王五,,输出张三,李四后,会抛异常。
所以传统集合中不能用Iterator一边遍历,一边对集合进行操作。
只要把集合从ArrayList改为CopyOnWriteArrayList就能解决这个问题。

 Java5中提供了如下一些同步集合类:
 通过看java.util.concurrent包下的介绍可以知道有哪些并发集合
 ConcurrentHashMap
 CopyOnWriteArrayList
 CopyOnWriteArraySet

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值