----------- 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对象。
Condition
将 Object
监视器方法(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...
发表评论
-
黑马程序员_Java String类
2012-12-15 15:23 1067----------- Android培训、Ja ... -
黑马程序员_Java多线程基础
2012-12-14 10:42 800----------- Android培训、Java培训、Ja ... -
黑马程序员_IO(Input/Output)流 学习笔记
2012-12-08 16:58 01.IO流用来处理设备之间的数据. 2.Java对数据的操作 ... -
黑马程序员_模拟文本转换服务器
2012-12-06 12:43 1016----------- Android培训、Java培训 ... -
黑马程序员_模拟文本转换服务器
2012-12-06 12:16 2----------- Android培训、Java培训、Ja ... -
黑马程序员_自定义字节流的缓冲区
2012-12-05 12:23 1681----------- Android培训、Java培训、J ... -
黑马程序员-Title
2012-12-14 15:49 0----------- Android培训、Java培训、Ja ...
相关推荐
黑马程序员_张孝祥_Java多线程与并发库,视频+代码+资料
黑马程序员_张孝祥_Java多线程与并发库,老师讲的非常仔细,老师很有耐心.欢迎大家下载学习.
day01_Object类、常用API day02_Collection、泛型 day03_List、Set、数据结构、Collections day04_Map,斗地主案例 day05_异常,线程 day06_线程、同步 day07_等待与唤醒案例、线程池、Lambda...Java基础小节练习题答案
传智播客_Java培训_毕向东_Java基础[05-多线程]系黑马程序员_毕向东_Java基础视频教程
1、 面向对象、跨平台性、健壮性、安全性、可移植性、多线程性、动态性等。 2、 JRE(Java Runtime Environment,Java 运行时环境),它相当于操作系统部分,提供了 Java 程序运 行时所需要的基本条件和许多 Java ...
NULL 博文链接:https://huangminwen.iteye.com/blog/1157983
Java多线程实例,是学习java多线程的好方法
详细介绍的JAVA的多线程程序设计
实现多线程TCP传输,同时分成发送方和接受方
多线程下载,随便做的,有一定的局限性,仅供参考
当一个进程中线程有多个时,是多线程。 为什么要用多线程 1,让计算机"同时"做多件事情,节约时间。 2,后台运行程序,提高程序的运行效率.。 3,多线程可以让程序"同时"处理多个事情。 4,...
Java中的多线程结构请大家观看不足的地方请指教,大家可以一语既出的观看和指导。
1.讲解了Java多线程的基础, 包括Thread类的核心API的使用。2.讲解了在多线程中对并发访问的控制, 主要就是synchronized的使用, 由于此关键字在使用上非常灵活, 所以书中用了很多案例来介绍此关键字的使用, 为...
java 多线程 其实就是每个线程都拥有自己的内存空间,多线程之间的通信,比例A线程修改了主内存(main方法的线程)变量,需要把A线程修改的结果同步到主线程中,这时B线程再从主线程获取该变量的值,这样就实现了...
java并发库thread使用,传统线程技术、定时器技术、线程互斥技术,同步通讯技术、多线程共享数据、并发库应用,线程锁技术,阻塞锁、阻塞队列,线程池等应用
java的打字程序,包含了多线程编写的练习和Applet的编程练习。
一个简单记录java多线程下变量共享问题,分析多线程内部运行
smoker_java多线程_源码.zip
实现了多线程电梯的调度算法(3部电梯统一进行调度)
服务器通信学习