黑马程序员_基础加强_Java线程通信和线程并发库

本文深入探讨Java中多种线程同步技术,包括Lock与Condition的使用、读写锁的实现原理及其应用场景、Semaphore计数信号灯的工作机制、CyclicBarrier和CountDownLatch的使用案例、Exchanger的数据交换方式以及可阻塞队列的特性。此外,还通过具体实例展示了如何利用这些技术解决实际编程中的线程同步问题。

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

java5的线程锁技术
Lock&Condition实现线程同步通信
Lock比传统的synchronized方式更加面向对象,两个线程执行的代码块要实现同步互斥,必须持有同一个Lock对象。
ReadWriteLock,多个读锁不互斥,读锁与写锁互斥。如果需要多线程同时读,但不能同时写,加读锁;如果代码修改数据,为改代码加写锁,写锁是线程独占的。
在等待 Condition 时,允许发生“虚假唤醒”,Condition 应该总是在一个循环中被等待,并测试正被等待的状态。
Condition condition = lock.newCondition();
使用读写锁的缓存功能
  1. class CachedData {
  2. Object data;
  3. volatile boolean cacheValid;
  4. ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
  5. void processCachedData() {
  6. rwl.readLock().lock();
  7. if (!cacheValid) {//如果缓存中没有data
  8. // 在使用写锁前必须释放读锁
  9. rwl.readLock().unlock();
  10. rwl.writeLock().lock();
  11. // 获取写锁后在检查
  12. if (!cacheValid) {
  13. data = ...//写入data
  14. cacheValid = true;
  15. }
  16. // 写入数据后,上读锁,在释放写锁
  17. rwl.readLock().lock();
  18. rwl.writeLock().unlock();
  19. }
  20. use(data);
  21. rwl.readLock().unlock();
  22. }
  23. }
class CachedData {
   Object data;
   volatile boolean cacheValid;
   ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();


   void processCachedData() {
     rwl.readLock().lock();
     if (!cacheValid) {//如果缓存中没有data
        // 在使用写锁前必须释放读锁
        rwl.readLock().unlock();
        rwl.writeLock().lock();
        //  获取写锁后在检查 
        if (!cacheValid) {
          data = ...//写入data
          cacheValid = true;
        }
        // 写入数据后,上读锁,在释放写锁
        rwl.readLock().lock();
        rwl.writeLock().unlock(); 
     }
     use(data);
     rwl.readLock().unlock();
   }
 }
Semaphore 计数信号灯
限制可以访问某些资源的线程数目。可进入同一段代码的线程数目。
Semaphore(int permits) permits允许的并发线程数.
acquire() 从此信号量获取一个许可.
void release() 释放一个许可。


单个信号量的Semaphore对象可以实现互斥锁的功能,并且可以是由一个线程获得了“锁”,再由另一个线程释放“锁”。
  1. public class SemaphoreTest {
  2. public static void main(String[] args) {
  3. ExecutorService service = Executors.newCachedThreadPool();
  4. final Semaphore sp = new Semaphore(3);//创建一个可以允许3个线程并发访问的信号灯
  5. for(int i=0;i<10;i++){
  6. Runnable runnable = new Runnable(){
  7. public void run(){
  8. try {
  9. sp.acquire();
  10. } catch (InterruptedException e1) {
  11. e1.printStackTrace();
  12. }
  13. System.out.println("线程" + Thread.currentThread().getName() +
  14. "进入,当前已有" + (3-sp.availablePermits()) + "个并发");
  15. try {
  16. Thread.sleep((long)(Math.random()*10000));
  17. } catch (InterruptedException e) {
  18. e.printStackTrace();
  19. }
  20. System.out.println("线程" + Thread.currentThread().getName() +
  21. "即将离开");
  22. sp.release();
  23. //下面代码有时候执行不准确,因为其没有和上面的代码合成原子单元
  24. System.out.println("线程" + Thread.currentThread().getName() +
  25. "已离开,当前已有" + (3-sp.availablePermits()) + "个并发");
  26. }
  27. };
  28. service.execute(runnable); //将任务交给线程池
  29. }
  30. }
  31. }
public class SemaphoreTest {
	public static void main(String[] args) {
		ExecutorService service = Executors.newCachedThreadPool();
		final  Semaphore sp = new Semaphore(3);//创建一个可以允许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);	//将任务交给线程池		
		}
	}
}
CyclicBarrier
一个同步辅助类,它允许一组线程互相等待,直到到达某个公共屏障点。
  1. public class CyclicBarrierTest {
  2. public static void main(String[] args) {
  3. ExecutorService service = Executors.newCachedThreadPool();//创建一个线程池
  4. final CyclicBarrier cb = new CyclicBarrier(3);//创建一个循环barrier,线程数目为3
  5. for(int i=0;i<3;i++){//新建3个线程,交给线程池执行
  6. Runnable runnable = new Runnable(){
  7. public void run(){
  8. try {
  9. Thread.sleep((long)(Math.random()*10000));
  10. System.out.println("线程" + Thread.currentThread().getName() +
  11. "即将到达集合地点1,当前已有" + (cb.getNumberWaiting()+1) + "个已经到达," + (cb.getNumberWaiting()==2?"都到齐了,继续走啊":"正在等候"));
  12. cb.await();//等待其他线程
  13. Thread.sleep((long)(Math.random()*10000));
  14. System.out.println("线程" + Thread.currentThread().getName() +
  15. "即将到达集合地点2,当前已有" + (cb.getNumberWaiting()+1) + "个已经到达," + (cb.getNumberWaiting()==2?"都到齐了,继续走啊":"正在等候"));
  16. cb.await();
  17. Thread.sleep((long)(Math.random()*10000));
  18. System.out.println("线程" + Thread.currentThread().getName() +
  19. "即将到达集合地点3,当前已有" + (cb.getNumberWaiting() + 1) + "个已经到达," + (cb.getNumberWaiting()==2?"都到齐了,继续走啊":"正在等候"));
  20. cb.await();
  21. } catch (Exception e) {
  22. e.printStackTrace();
  23. }
  24. }
  25. };
  26. service.execute(runnable);
  27. }
  28. service.shutdown();
  29. }
  30. }
public class CyclicBarrierTest {


	public static void main(String[] args) {
		ExecutorService service = Executors.newCachedThreadPool();//创建一个线程池
		final  CyclicBarrier cb = new CyclicBarrier(3);//创建一个循环barrier,线程数目为3
		for(int i=0;i<3;i++){//新建3个线程,交给线程池执行
			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();
	}
}
CountDownLatch
CountDownLatch(int count) 构造一个用给定计数初始化的 CountDownLatch。
调用countDown方法就将计数器减1,当计数到达0时,则所有等待者或单个等待者开始执行。
  1. public class CountdownLatchTest {
  2. public static void main(String[] args) {
  3. ExecutorService service = Executors.newCachedThreadPool();
  4. final CountDownLatch cdOrder = new CountDownLatch(1);
  5. final CountDownLatch cdAnswer = new CountDownLatch(3);
  6. for(int i=0;i<3;i++){
  7. Runnable runnable = new Runnable(){//创建3个线程
  8. public void run(){
  9. try {
  10. System.out.println("线程" + Thread.currentThread().getName() +
  11. "正准备接受命令");
  12. cdOrder.await();//等待主线程控制cdOrder开始任务
  13. System.out.println("线程" + Thread.currentThread().getName() +
  14. "已接受命令");
  15. Thread.sleep((long)(Math.random()*10000));
  16. System.out.println("线程" + Thread.currentThread().getName() +
  17. "回应命令处理结果");
  18. cdAnswer.countDown();//任务完成通知主线程
  19. } catch (Exception e) {
  20. e.printStackTrace();
  21. }
  22. }
  23. };
  24. service.execute(runnable);
  25. }
  26. try {
  27. Thread.sleep((long)(Math.random()*10000));
  28. System.out.println("线程" + Thread.currentThread().getName() +
  29. "即将发布命令");
  30. cdOrder.countDown();//将count归0,开始执行任务
  31. System.out.println("线程" + Thread.currentThread().getName() +
  32. "已发送命令,正在等待结果");
  33. cdAnswer.await();//等待子线程完成
  34. System.out.println("线程" + Thread.currentThread().getName() +
  35. "已收到所有响应结果");
  36. } catch (Exception e) {
  37. e.printStackTrace();
  38. }
  39. service.shutdown();
  40. }
  41. }
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(){//创建3个线程
					public void run(){
					try {
						System.out.println("线程" + Thread.currentThread().getName() + 
								"正准备接受命令");						
						cdOrder.await();//等待主线程控制cdOrder开始任务
						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();//将count归0,开始执行任务
			System.out.println("线程" + Thread.currentThread().getName() + 
			"已发送命令,正在等待结果");	
			cdAnswer.await();//等待子线程完成
			System.out.println("线程" + Thread.currentThread().getName() + 
			"已收到所有响应结果");	
		} catch (Exception e) {
			e.printStackTrace();
		}				
		service.shutdown();
	}
}
Exchanger
实现持有同一Exchanger对象的两个线程间的数据交换。
V exchange(V x) 方法交换数据。

可阻塞队列
public class ArrayBlockingQueue<E>extends AbstractQueue<E>implements BlockingQueue<E>, Serializable
按 FIFO(先进先出)原则对元素进行排序。
生产者使用put方法放入数据,消费者使用take方法取出数据。
新元素插入到队列的尾部,队列获取操作则是从队列头部开始获得元素。
size() 返回此队列中元素的数量。
可以由两个只有一个元素的同步队列对象实现线程通信功能。


空中网题目:
Test类中的代码在不断地产生数据,然后交给TestDo.doSome()方法去处理,就好像生产者在不断地产生数据,消费者在不断消费数据。
程序有10个线程来消费生成者产生的数据,这些消费者都调用TestDo.doSome()方法去进行处理,故每个消费者都需要一秒才能处理完,程序应保证这些消费者线程依次有序地消费数据,只有上一个消费者消费完后,下一个消费者才能消费数据,下一个消费者是谁都可以,但要保证这些消费者线程拿到的数据是有顺序的。
  1. public class Test {
  2. public static void main(String[] args) {
  3. final Semaphore semaphore = new Semaphore(1);
  4. final SynchronousQueue<String> queue = new SynchronousQueue<String>();
  5. for(int i=0;i<10;i++){
  6. new Thread(new Runnable(){
  7. @Override
  8. public void run() {
  9. try {
  10. semaphore.acquire();
  11. String input = queue.take();//保证按顺序取出数据
  12. String output = TestDo.doSome(input);
  13. System.out.println(Thread.currentThread().getName()+ ":" + output);
  14. semaphore.release();
  15. } catch (InterruptedException e) {
  16. e.printStackTrace();
  17. }
  18. }
  19. }).start();
  20. }
  21. System.out.println("begin:"+(System.currentTimeMillis()/1000));
  22. for(int i=0;i<10;i++){ //这行不能改动
  23. String input = i+""; //这行不能改动
  24. try {
  25. queue.put(input);//主线程的循环产生数据,并将数据放入同步队列中,由子线程对队列进行操作
  26. } catch (InterruptedException e) {
  27. e.printStackTrace();
  28. }
  29. }
  30. }
  31. }
  32. //不能改动此TestDo类
  33. class TestDo {
  34. public static String doSome(String input){
  35. try {
  36. Thread.sleep(1000);
  37. } catch (InterruptedException e) {
  38. e.printStackTrace();
  39. }
  40. String output = input + ":"+ (System.currentTimeMillis() / 1000);
  41. return output;
  42. }
  43. }
public class Test {


	public static void main(String[] args) {
		final Semaphore semaphore = new Semaphore(1);
		final SynchronousQueue<String> queue = new SynchronousQueue<String>();
		for(int i=0;i<10;i++){
			new Thread(new Runnable(){
				@Override
				public void run() {	
					try {
						semaphore.acquire();
						String input = queue.take();//保证按顺序取出数据
						String output = TestDo.doSome(input);
						System.out.println(Thread.currentThread().getName()+ ":" + output);
						semaphore.release();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}	
				}
			}).start();
		}
		
		System.out.println("begin:"+(System.currentTimeMillis()/1000));
		for(int i=0;i<10;i++){  //这行不能改动
			String input = i+"";  //这行不能改动
			try {
				queue.put(input);//主线程的循环产生数据,并将数据放入同步队列中,由子线程对队列进行操作
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}


//不能改动此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;
	}
}
同步集合
传统方式下的Collection在迭代集合时,不允许对集合进行修改。
同步集合类:ConcurrentHashMap、ConcurrentSkipListMap、ConcurrentSkipListSet、CopyOnWriteArrayList 和 CopyOnWriteArraySet。当期望许多线程访问一个给定 collection 时,ConcurrentHashMap 通常优于同步的 HashMap,ConcurrentSkipListMap 通常优于同步的 TreeMap。当期望的读数和遍历远远大于列表的更新数时,CopyOnWriteArrayList 优于同步的 ArrayList。


空中网面试题:
如果有几个线程调用TestDo.doSome(key, value)方法时,传递进去的key相等(equals比较为true),则这几个线程应互斥排队输出结果,即当有两个线程的key都是"1"时,它们中的一个要比另外其他线程晚1秒输出结果,如下所示:
4:4:1258199615
1:1:1258199615
3:3:1258199615
1:2:1258199616
  1. import java.util.Iterator;
  2. import java.util.concurrent.CopyOnWriteArrayList;
  3. //不能改动此Test类
  4. public class Test extends Thread{
  5. private TestDo testDo;
  6. private String key;
  7. private String value;
  8. public Test(String key,String key2,String value){
  9. this.testDo = TestDo.getInstance();
  10. /*常量"1"和"1"是同一个对象,下面这行代码就是要用"1"+""的方式产生新的对象,
  11. 以实现内容没有改变,仍然相等(都还为"1"),但对象却不再是同一个的效果*/
  12. this.key = key+key2;
  13. this.value = value;
  14. }
  15. public static void main(String[] args) throws InterruptedException{
  16. Test a = new Test("1","","1");
  17. Test b = new Test("1","","2");
  18. Test c = new Test("3","","3");
  19. Test d = new Test("4","","4");
  20. System.out.println("begin:"+(System.currentTimeMillis()/1000));
  21. a.start();
  22. b.start();
  23. c.start();
  24. d.start();
  25. }
  26. public void run(){
  27. testDo.doSome(key, value);
  28. }
  29. }
  30. class TestDo {
  31. private TestDo() {}
  32. private static TestDo _instance = new TestDo();
  33. public static TestDo getInstance() {
  34. return _instance;
  35. }
  36. //因为在执行doSome时,需要对key进行判断和添加,需要使用同步集合
  37. private CopyOnWriteArrayList keys = new CopyOnWriteArrayList();
  38. public void doSome(Object key, String value) {
  39. Object o = key;//以key做同步互斥锁对象
  40. if(!keys.contains(o)){
  41. keys.add(o);
  42. }else{
  43. for(Iterator iter=keys.iterator();iter.hasNext();){
  44. try {
  45. Thread.sleep(20);//测试代码,让对集合操作的动作多执行一会
  46. } catch (InterruptedException e) {
  47. e.printStackTrace();
  48. }
  49. Object oo = iter.next();
  50. if(oo.equals(o)){
  51. o = oo;
  52. break;
  53. }
  54. }
  55. }
  56. synchronized(o)
  57. // 以大括号内的是需要局部同步的代码,不能改动!
  58. {
  59. try {
  60. Thread.sleep(1000);
  61. System.out.println(key+":"+value + ":"
  62. + (System.currentTimeMillis() / 1000));
  63. } catch (InterruptedException e) {
  64. e.printStackTrace();
  65. }
  66. }
  67. }
  68. }
import java.util.Iterator;
import java.util.concurrent.CopyOnWriteArrayList;


//不能改动此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;
	}
	//因为在执行doSome时,需要对key进行判断和添加,需要使用同步集合
	private CopyOnWriteArrayList keys = new CopyOnWriteArrayList();
	public void doSome(Object key, String value) {
		Object o = key;//以key做同步互斥锁对象
		if(!keys.contains(o)){
			keys.add(o);
		}else{
			for(Iterator iter=keys.iterator();iter.hasNext();){
				try {
					Thread.sleep(20);//测试代码,让对集合操作的动作多执行一会
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				Object oo = iter.next();
				if(oo.equals(o)){
					o = oo;
					break;
				}
			}
		}
		synchronized(o)
		// 以大括号内的是需要局部同步的代码,不能改动!
		{
			try {
				Thread.sleep(1000);
				System.out.println(key+":"+value + ":"
						+ (System.currentTimeMillis() / 1000));
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}

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

基于TROPOMI高光谱遥感仪器获取的大气成分观测资料,本研究聚焦于大气污染物一氧化氮(NO₂)的空间分布与浓度定量反演问题。NO₂作为影响空气质量的关键指标,其精确监测对环境保护与大气科学研究具有显著价值。当前,利用卫星遥感数据结合先进算法实现NO₂浓度的高精度反演已成为该领域的重要研究方向。 本研究构建了一套以深度学习为核心的技术框架,整合了来自TROPOMI仪器的光谱辐射信息、观测几何参数以及辅助气象数据,形成多维度特征数据集。该数据集充分融合了不同来源的观测信息,为深入解析大气中NO₂的时空变化规律提供了数据基础,有助于提升反演模型的准确性与环境预测的可靠性。 在模型架构方面,项目设计了一种多分支神经网络,用于分别处理光谱特征与气象特征等多模态数据。各分支通过独立学习提取代表性特征,并在深层网络中进行特征融合,从而综合利用不同数据的互补信息,显著提高了NO₂浓度反演的整体精度。这种多源信息融合策略有效增强了模型对复杂大气环境的表征能力。 研究过程涵盖了系统的数据处理流程。前期预处理包括辐射定标、噪声抑制及数据标准化等步骤,以保障输入特征的质量与一致性;后期处理则涉及模型输出的物理量转换与结果验证,确保反演结果符合实际大气浓度范围,提升数据的实用价值。 此外,本研究进一步对不同功能区域(如城市建成区、工业带、郊区及自然背景区)的NO₂浓度分布进行了对比分析,揭示了人类活动与污染物空间格局的关联性。相关结论可为区域环境规划、污染管控政策的制定提供科学依据,助力大气环境治理与公共健康保护。 综上所述,本研究通过融合TROPOMI高光谱数据与多模态特征深度学习技术,发展了一套高效、准确的大气NO₂浓度遥感反演方法,不仅提升了卫星大气监测的技术水平,也为环境管理与决策支持提供了重要的技术工具。 资源来源于网络分享,仅用于学习交流使用,请勿用于商业,如有侵权请联系我删除!
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值