`
AisiniLe
  • 浏览: 9577 次
  • 性别: Icon_minigender_1
  • 来自: 武汉
最近访客 更多访客>>
社区版块
存档分类
最新评论

黑马程序员_Java多线程通信基础

阅读更多

----------- Android培训Java培训Java学习型技术博客、期待与您交流! ------------

 

线程之间的关系是平等的,彼此之间并不存在任何依赖,它们各自竞争CPU资源,互不相让,并且还无条件地阻止其他线程对共享资源的异步访问。然而,也有很多现实问题要求不仅要同步的访问同一共享资源,而且线程间还彼此牵制,通过相互通信来向前推进。那么,多个线程之间是如何进行通信的呢?

当线程在继续执行前需要等待一个条件方可继续执行时,仅有 synchronized 关键字是不够的。因为虽然synchronized关键字可以阻止并发更新同一个共享资源,实现了同步,但是它不能用来实现线程间的消息传递,也就是所谓的通信。而在处理此类问题的时候又必须遵循一种原则,即:对于生产者,在生产者没有生产之前,要通知消费者等待;在生产者生产之后,马上又通知消费者消费;对于消费者,在消费者消费之后,要通知生产者已经消费结束,需要继续生产新的产品以供消费。

一个简单的示例:

 

  public class ThreadRes
 {
 	public static void main(String[] args)
 	{
 		Res r = new Res();
 
 		Output out = new Output(r);
 		Input in = new Input(r);
 
 		Thread t1 = new Thread(in);
 		Thread t2 = new Thread(out);
 
 		t1.start();
 		t2.start();
 	}
 }
 
 class Res//自定义的资源
 {
 	String name;
 	String sex;
 }
 
 class Input implements Runnable//产生输入,扩展Runnable接口,实现run()方法
 {
 	private Res r;
 
 	Input(Res r)
 	{
 		this.r = r;
 	}
 
 	public void run()
 	{
 		int x = 0;
 		while (true)
 		{
 			synchronized (r)//同一个锁
 			{
 				if (x == 0)
 				{
 					r.name = "mike";
 					r.sex = "man";
 
 				} else
 				{
 
 					r.name = "丽丽";
 					r.sex = "女";
 
 				}
 			}
 
 			x = (x + 1) % 2;//控制输入语句,输入不同的资源
 		}
 	}
 }
 
 class Output implements Runnable//接收输出,扩展Runnable接口,实现run()方法
 {
 	private Res r;
 
 	Output(Res r)
 	{
 		this.r = r;
 	}
 
 	public void run()
 	{
 		while (true)
 		{
 			synchronized (r)
 			{
 				System.out.println(r.name + "..." + r.sex);
 			}
 			
 		}
 	}
 }
 
 

 

上面示例中,一个线程进行输入,另一个线程进行输出,需要注意的是,两者的操作的资源必须是一致的,如果分别在Input和Output中分别定义了Res res = new Res();两者之间实际并没有进行通信.有输入才有输出.

程序运行结果:

 

...................
 丽丽...女
 丽丽...女
 丽丽...女
 mike...man
 mike...man
 mike...man
 mike...man
 mike...man
..................

 

 在现实生活中,可能是输入一个,输出一个,两者交替的运行,这就需要使用Java中提供的方法.

其实,Java提供了3个非常重要的方法来巧妙地解决线程间的通信问题。这3个方法分别是:wait()、notify()和notifyAll()。它们都是Object类的最终方法,因此每一个类都默认拥有它们。

调用wait()方法可以使调用该方法的线程释放共享资源的锁,然后从运行态退出,进入等待队列,直到被再次唤醒。而调用notify()方法可以唤醒等待队列中第一个等待同一共享资源的线程,并使该线程退出等待队列,进入可运行态。调用notifyAll()方法可以使所有正在等待队列中等待同一共享资源的线程从等待状态退出,进入可运行状态,此时,优先级最高的那个线程最先执行。显然,利用这些方法就不必再循环检测共享资源的状态,而是在需要的时候直接唤醒等待队列中的线程就可以了。这样不但节省了宝贵的CPU资源,也提高了程序的效率。

由于wait()方法在声明的时候被声明为抛出InterruptedException异常,因此,在调用wait()方法时,需要将它放入try…catch代码块中。此外,使用该方法时还需要把它放到一个同步代码段中.

修改上述代码:

Res类和主类无需修改

Input类的修改:

 

class Input1 implements Runnable
 {
 	private Res1 r1;
 	Input1(Res1 r1)
 	{
 		this.r1 = r1;
 	}
 	public void run()
 	{
 		int x = 0;
 		while (true)
 		{
 			synchronized (r1)// 同一个锁
 			{
 				if (r1.flag)
 					try
 					{
 						r1.wait();// 在线程池中等待
 					} catch (InterruptedException e)
 					{
 						e.printStackTrace();
 					}
 				if (x == 0)
 				{
 					r1.name = "mike";
 					r1.sex = "man";
 
 				} else
 				{
 					r1.name = "丽丽";
 					r1.sex = "女";
 				}
 				x = (x + 1) % 2;
 				r1.flag = true;
 				r1.notify();
 			}
 		}
 	}
 }
 

 

Output类的修改:

 

class Output1 implements Runnable
 {
 	private Res1 r1; 
 	Output1(Res1 r1)
 	{
 		this.r1 = r1;
 	}
 	public void run()
 	{
 		while (true)
 		{
 			synchronized (r1)
 			{
 				if (!r1.flag)
 					try
 					{
 						r1.wait();
 					} catch (InterruptedException e)
 					{
 						e.printStackTrace();
 					}
 				System.out.println(r1.name + "..." + r1.sex);
 				r1.flag = false;
 				r1.notify();
 			}
 		}
 	}
 }
 

 

其实两者主要的修改就是在synchronized同步块中添加了wait()和noyify()语句

运行的结果:

 

 ......................
 丽丽...女
 mike...man
 丽丽...女
 mike...man
 丽丽...女
 mike...man
 丽丽...女
 mike...man
.....................
 

 

发现上述代码结构冗余,稍微进行代码优化:

 

   /*

  * 代码优化
  */
 
 public class ThreadRes2
 {
 	public static void main(String[] args)
 	{
 		Res2 r2 = new Res2();
 
 		new Thread(new Output2(r2)).start();
 		new Thread(new Input2(r2)).start();
 		
 	}
 }
 
 class Res2
 {
 	private String name;
 	private String sex;
 	boolean flag;
 
 	public synchronized void set(String name, String sex)
 	{
 		if (flag)
 			try
 			{
 				this.wait();
 			} catch (InterruptedException e)
 			{
 				
 			}
 			
 		this.name = name;
 		this.sex = sex;
 		
 		flag = true;
 		this.notify();
 	}
 
 	public synchronized void out()
 	{
 		if(!flag)
 			try
 			{
 				this.wait();
 			} catch (InterruptedException e)
 			{
 
 			}
 		System.out.println(name + "---" + sex);
 		
 		flag = false;
 		this.notify();
 	}
 }
 
 class Input2 implements Runnable
 {
 	private Res2 r2;
 
 	Input2(Res2 r2)
 	{
 		this.r2 = r2;
 	}
 
 	public void run()
 	{
 		int x = 0;
 		while (true)
 		{
 				if (x == 0)
 				{
 					r2.set("mike", "man");
 				} else
 				{
 					r2.set("丽丽", "女");
 				}
 				x = (x + 1) % 2;
 			
 		}
 	}
 }
 
 class Output2 implements Runnable
 {
 	private Res2 r2;
 
 	Output2(Res2 r2)
 	{
 		this.r2 = r2;
 	}
 
 	public void run()
 	{
 		while (true)
 		{
 			r2.out();
 		}
 	}
 }
 
 

主要的修改是将synchronized同步代码块移动到Res中实现.运行结果正常.

 

上述代码是基于Java中的等待唤醒机制.在JDK1.5后,提供了Lock接口和Condition接口.

Lock 实现提供了比使用 synchronized 方法和语句可获得的更广泛的锁定操作。此实现允许更灵活的结构,可以具有差别很大的属性,可以支持多个相关的Condition对象。

 

ConditionObject 监视器方法(wait,notify和notifyAll)分解成截然不同的对象,以便通过将这些对象与任意Lock实现组合使用,为每个对象提供多个等待 set(wait-set).

其中,Lock 替代了 synchronized 方法和语句的使用,Condition 替代了 Object 监视器方法的使用。  

 

以生产者和消费者的问题演示:

 

 /*
  * JDK1.5中提供了多线程升级解决方案
  * 将同步Synchronized替换为现实的lock操作
  * 将Object中的wait,notify和notifyAll替换为Condition对对象
  * 该对象可以在lock多个condition对象
  * 
  * 该实例中实现了本方只唤醒对方的操作
  */
 
 import java.util.concurrent.locks.Condition;
 import java.util.concurrent.locks.Lock;
 import java.util.concurrent.locks.ReentrantLock;
 
 public class ProducerConsumerDemo2
 {
 	public static void main(String[] args)
 	{
 		Resource res = new Resource();
 
 		Producer p = new Producer(res);
 		Consumer c = new Consumer(res);
 
 		Thread t1 = new Thread(p);
 		Thread t2 = new Thread(p);
 		Thread t3 = new Thread(c);
 		Thread t4 = new Thread(c);
 
 		t1.start();
 		t2.start();
 		t3.start();
 		t4.start();
 
 	}
 }
 
 class Resource
 {
 	private String name;
 	private int count = 1;
 	private boolean flag = false;
 
 	private Lock lock = new ReentrantLock();
 
 	//private Condition condition = lock.newCondition();
 	
 	private Condition condition_pro = lock.newCondition();
 	private Condition condition_con = lock.newCondition();
 
 	public void set(String name)
 	{
 		lock.lock();// 上锁
 
 		
 			try
 			{
 				while (flag)
 				condition_pro.await();
 				this.name = name + "--" + count++;
 
 				System.out.println(Thread.currentThread().getName()
 						+ "***--生产者--***" + this.name);
 
 				flag = true;
 				//condition.signal();//仍然会全部等待-->signalAll
 				//condition.signalAll();//仍然唤醒本方
 				
 				condition_con.signal();
 			} catch (InterruptedException e)
 			{
 				e.printStackTrace();
 			}
 	
 			finally
 			{
 				lock.unlock();// 解锁
 			}
 	}
 
 	public void out()
 	{
 		lock.lock();
 
 		try
 		{
 			while (!flag)
 				condition_con.await();
 			System.out.println(Thread.currentThread().getName() + "--消费者--"
 					+ this.name);
 			flag = false;
 			//condition.signal();//仍然会全部等待-->signalAll
 			
 			//condition.signalAll();//仍然唤醒本方
 			
 			condition_pro.signal();
 			
 		} catch (InterruptedException e)
 		{
 			e.printStackTrace();
 		} finally
 		{
 			lock.unlock();//释放锁的动作一定要执行
 		}
 
 	}
 }
 
 class Producer implements Runnable
 {
 	private Resource res;
 
 	public Producer(Resource res)
 	{
 		this.res = res;
 	}
 
 	public void run()
 	{
 		while (true)
 		{
 			res.set("++商品++");
 		}
 	}
 }
 
 class Consumer implements Runnable
 {
 	private Resource res;
 
 	public Consumer(Resource res)
 	{
 		this.res = res;
 	}
 
 	public void run()
 	{
 		while (true)
 		{
 			res.out();
 		}
 	}
 }
 

 

 注意:

 

 程序中一个线程进行生产,另一个线程进行消费,这个过程相当和谐,如果有多个进程生产,多个进程消费,此时就崩溃了...原因:等待的进程在唤醒的时候没有判断标记.直接执行...

 将if转换为while,但是这样会导致进程全部等待.....while-->notifyAll()      if -->notify()

 

 

 

  * 如何停止线程:

  * stop()已经过时,只有一种,run方法结束

  * 开启多线程运行,运行代码通常是循环结构

  * 只要控制循环结构,就可以让run方法结束,线程也结束

 

  * interrupt()强制将冻结的进程恢复到运行状态

  * 守护线程/用户线程 setDaemon(boolean)-->后台线程

  * 当正在运行的线程都是守护线程时,Java 虚拟机退出

  * 该方法必须在启动线程前调用。

 

 

  * join()抢夺cpu执行权,一直等待该线程的结束.

  * 主线程等待join的结束

  * 当A线程执行到B线程的join方法时,A线程就会等待,等B线程都执行完毕后,A才会执行.

 

 

 

 *优先级Priority:1~10 MIN_PRIORITY(1)  NORM_PRIORITY(5默认)  MAX_PRIORITY(10)

 *yield():暂停当前正在执行的线程对象,并执行其他线程。

 

Done...

1
0
分享到:
评论

相关推荐

    黑马程序员_张孝祥_Java多线程与并发库 视频+代码+资料

    ### Java多线程基础 1. **线程的概念**:在Java中,线程是程序执行流的基本单元。一个标准的Java应用程序至少有一个线程,即主(main)线程。通过创建多个线程,可以实现并发执行,提高程序的运行效率。 2. **创建...

    黑马程序员-java多线程技术01

    本教程将聚焦于Java中的多线程技术,以“黑马程序员-java多线程技术01”为学习起点,探讨如何在Java中实现并管理线程。 首先,我们来理解什么是线程。线程是操作系统分配CPU时间的基本单元,一个进程中可以有多个...

    黑马程序员_Java基础辅导班教程课件[第01期]第10天

    在"黑马程序员_Java基础辅导班教程课件[第01期]第10天"中,我们聚焦于Java编程语言的基础知识,这是一门面向初学者的课程,旨在帮助学员快速掌握Java的核心概念。通过这个阶段的学习,学员将能够理解并运用Java的...

    黑马程序员_毕向东最新经典Java基础视频

    根据提供的文件信息,我们可以推断出这是一套由知名IT教育机构“黑马程序员”出品、由讲师毕向东主讲的Java基础教学视频。由于实际视频内容无法直接获取,本篇将依据标题、描述以及部分标签内容,综合分析并展开相关...

    黑马程序员_从零开始征服Android之旅(第一季)源码和笔记

    这份资料涵盖了一系列关键知识点,包括但不限于Android基础、UI设计、数据存储、网络通信、多线程以及性能优化等方面。 首先,Android基础是入门的关键,它涉及到Android系统架构、Android Studio集成开发环境的...

    黑马程序员入学Java精华总结

    ### 黑马程序员入学Java精华总结 #### 一、Java概述与基础知识 1. **何为编程?** - 编程是指通过编写计算机能够理解的指令来解决问题或完成特定任务的过程。这些指令通常被组织成算法,并使用某种编程语言实现。...

    java并发库高级应用源码--张孝祥

    Java并发库是Java编程中非常重要的一个领域,它为开发者提供了高效、安全的多线程编程工具。在《java并发库高级应用源码--张孝祥》中,我们将会深入探讨Java中的线程管理和并发控制策略,这对于我们理解和优化多线程...

    多线程高新(黑马程序员)

    本文将深入探讨“多线程高新”这一主题,结合黑马程序员的课程内容,来阐述多线程的核心概念、优势、实现方式以及在实际应用中的注意事项。 首先,多线程是指在一个进程中同时执行多个线程,这些线程可以共享同一...

    黑马程序员入学Java知识——精华总结.doc

    Java内置了对多线程的支持,通过Thread类或实现Runnable接口可以创建并管理多个执行线程。 17. **网络编程**: Java提供了丰富的API进行网络通信,如Socket和ServerSocket类,用于客户端和服务器之间的连接。 ...

    黑马程序员入学Java知识(精华总结)

    ### 黑马程序员入学Java知识(精华总结) #### 一、Java概述与基础知识 ##### 1、何为编程? 编程是指使用计算机语言来编写指令,这些指令被计算机执行以完成特定任务的过程。通过编程,我们可以控制计算机的行为...

    黑马程序员java面试宝典 完整版PDF.rar

    《黑马程序员Java面试宝典》是一本专门为Java开发者准备的面试指南,包含了广泛而深入的Java技术知识,以及面试过程中可能会遇到的各种问题。这本书的完整版PDF提供了丰富的学习材料,帮助求职者提升自己的技术水平...

    黑马程序员入学面试题

    实现一个多线程的卖票程序,可以使用`synchronized`关键字或显式锁来确保线程安全。 #### 52. 用代码实现List和map存储、取出数据使用泛型 使用泛型可以确保类型安全性,避免ClassCastException等类型转换异常。 #...

    黑马程序员入学Java知识——精华总结

    "黑马程序员入学Java知识——精华总结"这份文档很可能包含了Java编程的基础到进阶的知识点,旨在帮助初学者快速掌握Java的核心概念和技能。 1. **Java基础知识**:Java是一种面向对象的语言,它的基础包括语法、...

    Java-IO流高级-例题 & 例题源码 & PPT教学文档(黑马程序员详细版).rar

    本资料包“Java-IO流高级-例题 & 例题源码 & PPT教学文档(黑马程序员详细版).rar”提供了一个深入学习Java IO流的全面资源,包含实例题目、源代码以及PPT教学材料,适合对Java IO有进阶需求的开发者。 1. **Java ...

    java拼图游戏源码.zip

    Java拼图游戏是一款基于...总的来说,这个Java拼图游戏项目不仅展示了Swing GUI设计、多线程编程和游戏逻辑实现,还体现了良好的代码组织和封装性,对于学习Java编程和游戏开发的初学者来说,是一个极好的实践案例。

    黑马程序员《Java自学宝典》源代码

    Java自学宝典是针对初学者和有一定基础的学习者设计的一套完整的Java学习资源,由知名的教育机构黑马程序员出品。这份源代码压缩包包含了书中各个章节的实例代码,旨在帮助学习者深入理解Java编程语言的核心概念和...

    黑马程序员入学Java知识

    ### 黑马程序员入学Java知识 #### Java概述与基础知识 1. **何为编程?** - 编程是通过特定的计算机语言来编写指令,让计算机能够执行一系列任务的过程。 2. **Java语言概述,历史、特点** - Java是一种广泛...

    Java基础案例教程(第2版)_PPT.zip

    15. **多线程**:Java内置对多线程的支持,通过Thread类或Runnable接口实现并发执行,提高程序性能。 16. **反射机制**:反射允许在运行时动态访问和修改类的信息,是Java的高级特性,常用于插件开发、元编程等场景...

Global site tag (gtag.js) - Google Analytics