java multithreading communication - yaokun123/php-wiki GitHub Wiki
JAVA多线程之间的通信
一、线程间通信-示例
多个线程在处理同一个资源,但是任务不同。
//资源
class Resource{
String name;
String sex;
}
//输入
class Input implement Runnable{
Resource r;
Input(Resource r){
this.r = r;
}
public void run(){
int x = 0;
while(true){
if(x%2==0){
r.name = "mike";
r.sex = "man";
}else{
r.name = "丽丽";
r.sex = "女";
}
x++;
}
}
}
//输出
class Output implement Runnable{
Resource r;
Output(Resource r){
this.r = r;
}
public void run(){
while(true){
System.out.println(r.name+"..."+r.sex);
}
}
}
class Demo{
public static void main(String[] args){
Resource r = new Resource();
Input i = new Input(r);
Output o = new Output(r);
Thread t1 = new Thread(i);
Thread t2 = new Thread(o);
t1.start();
t2.start();
}
}
由于读和写不同步,所以会造成数据混乱
修改如下:
//输入
class Input implement Runnable{
Resource r;
Input(Resource r){
this.r = r;
}
public void run(){
int x = 0;
while(true){
synchronized(this.r){
if(x%2==0){
r.name = "mike";
r.sex = "man";
}else{
r.name = "丽丽";
r.sex = "女";
}
x++;
}
}
}
}
//输出
class Output implement Runnable{
Resource r;
Output(Resource r){
this.r = r;
}
public void run(){
while(true){
synchronized(this.r){
System.out.println(r.name+"..."+r.sex);
}
}
}
}
二、线程间通信-等待唤醒机制-wait()-notify()
涉及的方法
1、wait():是让线程处于冻结状态,释放cpu的执行权和执行资格。被wait的线程会被存储到线程池中。
2、notify():唤醒线程池中一个线程(任意)。
3、notifyAll():唤醒线程池中的所有线程。
注意:这些方法都必须定义在同步中。因为这些方法是用于操作线程状态的方法。必须要明确到底操作的是哪一个锁上的线程。
为什么操作线程的方法定义在Object类中? 因为这些方法是监视器的方法,监视器其实就是锁,而锁就是任意的对象,所以任意的对象可以调用的方法必须定义在Object中。
//资源
class Resource{
String name;
String sex;
boolean flag = false;
}
//输入
class Input implement Runnable{
Resource r;
Input(Resource r){
this.r = r;
}
public void run(){
int x = 0;
while(true){
synchronized(this.r){
if(this.r.flag){
try{
this.r.wait();
}catch(InterruptedException){}
}
if(x%2==0){
this.r.name = "mike";
this.r.sex = "man";
}else{
this.r.name = "丽丽";
this.r.sex = "女";
}
this.r.flag = true;
this.r.notify();
x++;
}
}
}
}
//输出
class Output implement Runnable{
Resource r;
Output(Resource r){
this.r = r;
}
public void run(){
while(true){
synchronized(this.r){
if(!this.r.flag){
try{
this.r.wait();
}catch(InterruptedException){}
}
System.out.println(this.r.name+"..."+this.r.sex);
this.r.flag = false;
this.r.notify();
}
}
}
}
class Demo{
public static void main(String[] args){
Resource r = new Resource();
Input i = new Input(r);
Output o = new Output(r);
Thread t1 = new Thread(i);
Thread t2 = new Thread(o);
t1.start();
t2.start();
}
}
三、线程间通信-多生产者多消费者问题-wait()-notifyAll()
if判断标记,只有一次,会导致不该运行的线程运行了,出现了数据错误的情况
while判断标记,解决了线程获取执行权后是否要运行(回过头判断)
notify只能唤醒一个线程,如果奔放唤醒了本方就没有意义。而且while判断标记+notify会导致死锁
notifyAll解决了本方线程一定会唤醒对方线程
class Resource{
private String name;
private int count = 1;
private boolean flag = false;
public synchronized void setName(String name){
if(this.flag){//多生产者这里不能在用if,必须用while,进程唤醒之后再进行判断标记
try{this.wait();}catch(InterruptedException e){}
}
this.name = name + this.count;
this.count++;
System.out.println(Thread.currentThread().getName()+"...生产者..."+this.name);
this.flag = true;
this.notify();//多生产者这里必须使用notifyAll()唤醒所有线程,不然会导致所有线程都被休眠现象(死锁)
}
public synchronized void out(){
if(!this.flag){//多生产者这里不能在用if,必须用while,进程唤醒之后再进行判断标记
try{this.wait();}catch(InterruptedException e){}
}
System.out.println(Thread.currentThread().getName()+"......消费者......"+this.name);
this.flag = false;
this.notify();
}
}
class Producer implement runnable{
private Resource r;
Producer(Resource r){
this.r = r;
}
public void run(){
while(true){
this.r.setName("烤鸭");
}
}
}
class Consummer implement runnable{
private Resource r;
Consummer(Resource r){
this.r = r;
}
public void run(){
while(true){
this.r.out();
}
}
}
class ProducerConsumerDemo{
public static void main(String[] args){
Resource r = new Resource();
Producer p = new Producer(r);
Consummer c = new Consummer(r);
//多生产者
Thread t0 = new Thread(p);
Thread t1 = new Thread(p);
//多消费者
Thread t2 = new Thread(c);
Thread t3 = new Thread(c);
t1.start();
t2.start();
}
}
多生产者多消费者使用notify()是造成另一种死锁的原因。
四、线程间通信-多生产者多消费者问题-JDK1.5新特性-Lock
对于同步代码块,对于锁的操作是隐式的。
import java.util.concurrent.locks.*;
class Resource{
private String name;
private int count = 1;
private boolean flag = false;
Look look = new ReentrantLock();//jdk1.5新特性-创建一个锁对象
public void setName(String name){
lock.lock();//使用显式锁
try{
while(this.flag){//此处就不能再用this了,要用锁上的wait
try{this.wait();}catch(InterruptedException e){}
}
this.name = name + this.count;
this.count++;
System.out.println(Thread.currentThread().getName()+"...生产者..."+this.name);
this.flag = true;
this.notify();//此处就不能再用this了
}finally{
lock.unlock();
}
}
public void out(){
try{
if(!this.flag){//此处就不能再用this了
try{this.wait();}catch(InterruptedException e){}
}
System.out.println(Thread.currentThread().getName()+"......消费者......"+this.name);
this.flag = false;
this.notify();//此处就不能再用this了
}finally{
lock.unlock();
}
}
}
Lock接口:出现替代了同步代码块或者同步函数,将同步的隐式锁操作变为显式锁操作。同时更为灵活。可以在一个锁上加上多个监视器
五、线程间通信-多生产者多消费者问题-JDK1.5新特性-Condition
import java.util.concurrent.locks.*;
class Resource{
private String name;
private int count = 1;
private boolean flag = false;
Look look = new ReentrantLock();//jdk1.5新特性-创建一个锁对象
//通过已有的锁获取该锁上的监视器对象
Condition con1 = lock.newCondition();//两组监视器,一组监视生产者,一组监视消费者
Condition con2 = lock.newCondition();
public void setName(String name){
lock.lock();//使用显式锁
try{
while(this.flag){
try{con1.await();}catch(InterruptedException e){}//此处就不能再用this了,要用锁上的wait
}
this.name = name + this.count;
this.count++;
System.out.println(Thread.currentThread().getName()+"...生产者..."+this.name);
this.flag = true;
//this.notify();//此处就不能再用this了
con2.signalAll();/con2.signal();
}finally{
lock.unlock();
}
}
public void out(){
try{
if(!this.flag){//此处就不能再用this了
try{con2.await();}catch(InterruptedException e){}
}
System.out.println(Thread.currentThread().getName()+"......消费者......"+this.name);
this.flag = false;
//this.notify();//此处就不能再用this了
con1.signalAll();/con1.signal();
}finally{
lock.unlock();
}
}
}
Condition接口:出现替代了Object中的wait notify notifyAll方法。将这些监视器方法单独进行了封装,变成Condition监视器对象,可以任意锁进行组合(await signal signalAll)
六、wait和sleep的区别
1、wait可以指定时间也可以不指定。sleep必须指定时间。
2、在同步中,对cpu的执行权和锁的处理不同
wait:释放执行权,释放锁
sleep:释放执行权,不释放锁。
七、停止线程
- 1、stop()
已过时
- 2、run方法结束
任务中都会有循环结构,只要控制住循环就可以结束任务。
控制循环通常就用定义标记来完成。
但是如果线程处于冻结状态,无法读取标记,如何结束呢?
可以使用interrupt()方法将线程从冻结状态强制恢复到运行状态中来,让线程具备cpu的执行资格。
但是强制动作会发生InterruptedException,记得要处理
八、守护线程-setDaemon
将该线程标记为守护线程或用户线程。当正在运行的线程都是守护线程时,java虚拟机退出。
该方法必须在启动线程前调用。