黑马程序员__java基础之线程间通信
来源:互联网 发布:ubuntu 安装类型 编辑:程序博客网 时间:2024/05/01 13:58
------- android培训、java培训、期待与您交流! ----------
线程间通信:
学习思路:
要实现线程间通信,首先要明确的明白通信之间如何连接。也就是说线程之间需要有共享的资源
那如何实现数据共享呢?
实现共享可以有3种方式:
(1)静态;(2)单例设计模式;(3)通过参数传递,这里通过构造函数实现
因为静态存在的时间过长,所以不建议。单例设计模式是允许的,这里的话,我们通过第三种方式进行讲解。
举例如下:
- class ThreadTest
- {
- public staticvoid main(String[] args)
- {
- Res r = new Res();
- new Thread(new Input(r)).start();
- new Thread(new Output(r)).start();
- }
- }
- class Res
- {
class ThreadTest{ public static void main(String[] args) { Res r = new Res(); new Thread(new Input(r)).start(); new Thread(new Output(r)).start(); }}class Res{
- /*
- 优化前:
- String name = name;
- String sex = sex;
- boolean flag =flag;
- */
- //优化后
- private String name;
- private String sex;
- private boolean flag =false;
- public synchronizedvoid set(String name,String sex)
- {
- if(this.flag)
- try{this.wait();}catch(Exception e){}
- this.name = name;
- this.sex = sex;
- this.flag = true;
- try{this.notify();}catch(Exception e){}
- }
- public synchronizedvoid out()
- {
- if(!this.flag)
- try{this.wait();}catch(Exception e){}
- System.out.println(name+"…………"+sex);
- this.flag = false;
- try{this.notify();}catch(Exception e){}
- }
- }
- class Input implements Runnable
- {
- private Res r;
- Input(Res r)
- {
- this.r = r;
- }
- public void run()
- {
- int x = 0;
- while(true)//为了实现连续多次打印,可以开始的用循环的形式进行输出
- {
- //给变量赋值
- /*
- 优化前的:
- synchronized(r)
- {
- if(r.flag)
- try{r.wait();}catch(Exception e){}
- if(x == 0)
- {
- r.name = "mike";
- r.sex = "man";
- }
- else
- {
- r.name = "丽丽";
- r.sex = "女女女女女";
- }
- x = (x+1)%2;
- r.flag = true;
- r.notify();
- }
- */
- //优化后
- if(x==0)
- r.set("mike","man");
- else
- r.set("丽丽","女女女女女");
- x = (x+1)%2;
- }
- }
- }
- class Output implements Runnable
- {
- private Res r;
- Output(Res r)
- {
- this.r = r;
- }
- public void run()
- {
- while(true)
- {
- //输出结果
- /*优化前
- synchronized(r)
- {
- if(!r.flag)
- try{r.wait();}catch(Exception e){}
- System.out.println(r.name+"…………"+r.sex);
- r.flag = false;
- try{r.notify();}catch(Exception e){}
- }
- */
- //优化后
- r.out();
- }
- }
- }
- /*
/* 优化前: String name = name; String sex = sex; boolean flag =flag; */ //优化后 private String name; private String sex; private boolean flag = false; public synchronized void set(String name,String sex) { if(this.flag) try{this.wait();}catch(Exception e){} this.name = name; this.sex = sex; this.flag = true; try{this.notify();}catch(Exception e){} } public synchronized void out() { if(!this.flag) try{this.wait();}catch(Exception e){} System.out.println(name+"…………"+sex); this.flag = false; try{this.notify();}catch(Exception e){} }}class Input implements Runnable{ private Res r; Input(Res r) { this.r = r; } public void run() { int x = 0; while(true) //为了实现连续多次打印,可以开始的用循环的形式进行输出 { //给变量赋值 /* 优化前的: synchronized(r) { if(r.flag) try{r.wait();}catch(Exception e){} if(x == 0) { r.name = "mike"; r.sex = "man"; } else { r.name = "丽丽"; r.sex = "女女女女女"; } x = (x+1)%2; r.flag = true; r.notify(); } */ //优化后 if(x==0) r.set("mike","man"); else r.set("丽丽","女女女女女"); x = (x+1)%2; } }}class Output implements Runnable{ private Res r; Output(Res r) { this.r = r; } public void run() { while(true) { //输出结果 /*优化前 synchronized(r) { if(!r.flag) try{r.wait();}catch(Exception e){} System.out.println(r.name+"…………"+r.sex); r.flag = false; try{r.notify();}catch(Exception e){} } */ //优化后 r.out(); } }}/*
按上面的输出结果出现"mike 女女女女""丽丽 man"的情况,出现安全问题;
当出现安全问题的时候,首先要分析为什么会这样输出,什么原因呢?显然,这是由于输入输出不同步造成的
即输出的时候还进行到一半的时候,设置的值已经改变。因此,要解决这个问题,我们要做的,就是使
输入输出同步!注意,是输入输出都要同步,而不是其中之一同步。这点要注意。
同步时要注意,为了保证使用同一把锁,要把同步代码块的锁,使用同一对象。这里可以使用r。
完成以上步骤后,就实现了同步。但是有一个问题就是输入输出时总是连续的输出相同的,而我们想要的是:
输入一个,输出一个。为了达到这个目的,我们可以通过使用标记的方式。
开始的时候,我们可以初始化标记为false,如果开始的时候为真,则等待。假的话,就设置值。
当设置完后,标记的flag的真假要改变。顺便说一下,等待多线程放在线程池里,唤醒的时候(notify),
一定要指明唤醒的对象,最先唤醒的,是最近要执行的那一个!任意对象只能被同一对象的锁唤醒。
代码:
因为notify,wai方法哪个都可以调用,所以为Object类型。因为在run方法中,不能抛,只能解决
对程序优化后(见上面优化后代码)
这个线程是输入输出只有一个的情况,而现实中多是多个线程同时运行,这就是生产者消费者问题:
- class ProductConsumerDemo
- {
- public staticvoid main(String[] args)
- {
- Resource r = new Resource();
- new Thread(new Producer(r)).start();
- new Thread(new Producer(r)).start();
- new Thread(new Consumer(r)).start();
- new Thread(new Consumer(r)).start();
- //创建并启动线程
- }
- }
- class Resource
- {
- private String name;
- private int count =1;
- private boolean flag;
- //生产商品
- public synchronizedvoid set(String name)
- {
- /*
- 优化前:
- if(flag)
- */
- //优化后
- while(flag)
- try{this.wait();}catch(Exception e){}
- this.name = name+"--"+count++;
- System.out.println(Thread.currentThread().getName()+"生产者-----"+this.name);
- flag = true;
- /*
- 优化前:
- try{this.notify();}catch(Exception e){}
- */
- //优化后:
- try{this.notifyAll();}catch(Exception e){}
- }
- public synchronizedvoid out()
- {
- /*
- 优化前:
- if(!flag)
- */
- //优化后
- while(!flag)
- try{this.wait();}catch(Exception e){}
- System.out.println(Thread.currentThread().getName()+"消费者____________"+this.name);
- flag = false;
- try{this.notify();}catch(Exception e){}
- }
- }
- //生产者
- class Producer implements Runnable
- {
- private Resource res;
- Producer(Resource res)
- {
- this.res = res;
- }
- public void run()
- {
- while(true)
- {
- res.set("商品");
- }
- }
- }
- //消费者
- class Consumer implements Runnable
- {
- private Resource con;
- Consumer(Resource con)
- {
- this.con = con;
- }
- public void run()
- {
- while(true)
- con.out();
- }
- }
class ProductConsumerDemo{public static void main(String[] args) {Resource r = new Resource();new Thread(new Producer(r)).start();new Thread(new Producer(r)).start();new Thread(new Consumer(r)).start();new Thread(new Consumer(r)).start();//创建并启动线程}}class Resource{private String name;private int count = 1;private boolean flag;//生产商品public synchronized void set(String name){/*优化前:if(flag)*///优化后while(flag)try{this.wait();}catch(Exception e){}this.name = name+"--"+count++;System.out.println(Thread.currentThread().getName()+"生产者-----"+this.name);flag = true;/*优化前:try{this.notify();}catch(Exception e){}*///优化后:try{this.notifyAll();}catch(Exception e){}}public synchronized void out(){/*优化前:if(!flag)*///优化后while(!flag)try{this.wait();}catch(Exception e){}System.out.println(Thread.currentThread().getName()+"消费者____________"+this.name);flag = false;try{this.notify();}catch(Exception e){}}}//生产者class Producer implements Runnable{private Resource res;Producer(Resource res){this.res = res;}public void run(){while(true){res.set("商品");}}}//消费者class Consumer implements Runnable{private Resource con;Consumer(Resource con){this.con = con;}public void run(){while(true)con.out();}}
但是按上面的代码操作时,会出现安全问题。这是因为当多个线程同时运行时,原来的等待唤醒机制出现局限性
因为notify在唤醒的过程中,只唤醒线程池中当前最近要执行的那个线程,如生产者t1,t2,当某一次刚好执行完t1
时,线程t2也处于唤醒状态,而t1在判断完后,刚好处于wait状态,而t2运行后,则不再判断flag的真假性,
这个是引起问题原因之一,如果把if改为while之后,因为notify就出现局限性,导致所有的线程进入等待状态。
要使程序继续执行,则需要用notifyAll方法,改动部分,如下代码:
- //生产商品
- public synchronizedvoid set(String name)
- {
- //优化前:
- //if(flag)
- //优化后
- while(flag)
- try{this.wait();}catch(Exception e){}
- this.name = name+"--"+count++;
- System.out.println(Thread.currentThread().getName()+"生产者-----"+this.name);
- flag = true;
- //优化前:
- //try{this.notify();}catch(Exception e){}
- //优化后:
- try{this.notifyAll();}catch(Exception e){}
- }
- public synchronizedvoid out()
- {
- //优化前:
- //if(!flag)
- //优化后
- while(!flag)
- try{this.wait();}catch(Exception e){}
- System.out.println(Thread.currentThread().getName()+"消费者____________"+this.name);
- flag = false;
- try{this.notify();}catch(Exception e){}
- }
//生产商品 public synchronized void set(String name) { //优化前: //if(flag) //优化后 while(flag) try{this.wait();}catch(Exception e){} this.name = name+"--"+count++; System.out.println(Thread.currentThread().getName()+"生产者-----"+this.name); flag = true; //优化前: //try{this.notify();}catch(Exception e){} //优化后: try{this.notifyAll();}catch(Exception e){} } public synchronized void out() { //优化前: //if(!flag) //优化后 while(!flag) try{this.wait();}catch(Exception e){} System.out.println(Thread.currentThread().getName()+"消费者____________"+this.name); flag = false; try{this.notify();}catch(Exception e){} }
在这个程序中,notifyAll会同时唤醒本方和对象的线程,那是否可以直接唤醒对方的线程呢?
jdk 5.0之后,对这个问题进行了解决,出现了新的加锁解锁机制。
加锁--解锁:lock---unlock;
对象唤醒机制
condition.await();condition.signal();
将上上述代码转化为升级后的代码为:
- //Jdk 1.5升级后,生产消费者问题
- import java.util.concurrent.locks.*;
- class ProductConsumerDemo2
- {
- public static void main(String[] args)
- {
- Resource r = new Resource();
- //创建并启动线程
- new Thread(new Producer(r)).start();
- new Thread(new Producer(r)).start();
- new Thread(new Consumer(r)).start();
- new Thread(new Consumer(r)).start();
- }
- }
- class Resource
- {
- private String name;
- private int count =1;
- private boolean flag;
- //创建使用锁对象
- private Lock lock = new ReentrantLock();
- private Condition condition_pro = lock.newCondition();
- private Condition condition_con = lock.newCondition();
- //生产商品
- public void set(String name)throws InterruptedException
- {
- //加锁
- lock.lock();
- try
- {
- while(flag)
- condition_pro.await();
- this.name = name+"--"+count++;
- System.out.println(Thread.currentThread().getName()+"生产者---"+this.name);
- flag = true;
- condition_con.signal();
- }
- finally
- {
- lock.unlock(); //解锁
- }
- }
- public void out()throws InterruptedException
- {
- lock.lock();
- try
- {
- while(!flag)
- condition_con.await();
- System.out.println(Thread.currentThread().getName()+"消费者-------------"+this.name);
- flag = false;
- condition_pro.signal();
- }
- finally
- {
- lock.unlock(); //解锁
- }
- }
- }
- //生产者
- class Producer implements Runnable
- {
- private Resource res;
- Producer(Resource res)
- {
- this.res = res;
- }
- public void run()
- {
- while(true)
- {
- try
- {
- res.set("商品");
- }
- catch (InterruptedException e)
- {
- }
- }
- }
- }
- //消费者
- class Consumer implements Runnable
- {
- private Resource con;
- Consumer(Resource con)
- {
- this.con = con;
- }
- public void run()
- {
- while(true)
- {
- try
- {
- con.out();
- }
- catch (InterruptedException e)
- {
- }
- }
- }
- }
//Jdk 1.5升级后,生产消费者问题import java.util.concurrent.locks.*;class ProductConsumerDemo2{ public static void main(String[] args) { Resource r = new Resource(); //创建并启动线程 new Thread(new Producer(r)).start(); new Thread(new Producer(r)).start(); new Thread(new Consumer(r)).start(); new Thread(new Consumer(r)).start(); }}class Resource{ private String name; private int count = 1; private boolean flag; //创建使用锁对象 private Lock lock = new ReentrantLock(); private Condition condition_pro = lock.newCondition(); private Condition condition_con = lock.newCondition(); //生产商品 public void set(String name)throws InterruptedException { //加锁 lock.lock(); try { while(flag) condition_pro.await(); this.name = name+"--"+count++; System.out.println(Thread.currentThread().getName()+"生产者---"+this.name); flag = true; condition_con.signal(); } finally { lock.unlock(); //解锁 } } public void out()throws InterruptedException { lock.lock(); try { while(!flag) condition_con.await(); System.out.println(Thread.currentThread().getName()+"消费者-------------"+this.name); flag = false; condition_pro.signal(); } finally { lock.unlock(); //解锁 } } }//生产者class Producer implements Runnable{ private Resource res; Producer(Resource res) { this.res = res; } public void run() { while(true) { try { res.set("商品"); } catch (InterruptedException e) { } } }}//消费者class Consumer implements Runnable{ private Resource con; Consumer(Resource con) { this.con = con; } public void run() { while(true) { try { con.out(); } catch (InterruptedException e) { } } }}
jdk1.5版本以后,对锁机制的改正在于使用lock与unlock方法,使加锁解锁更直观;
同时,使用Condition接口子类的方式,在lock接口的子类中可以使用多个Condition接口子类对象,
可以使本方对象唤醒对方。而原来一个同步只能一个对象,多个对象嵌套的话,容易造成死锁。改正后就
避免了这个问题。
在上面对线程操作后,我们都是通过直接在控制台上使用ctrl+c强制结束,那到底该怎么结束运行的线程呢?
在java中只有一种方式,那就是使run中的方法运行结束。结束的关键就是控制循环条件。
如下所示:
- class StopThreadDemo
- {
- public staticvoid main(String[] args)
- {
- StopThread t = new StopThread();
- Thread t1 = new Thread(t);
- Thread t2 = new Thread(t);
- t1.start();
- t2.start();
- int num = 0;
- while(true)
- {
- if(num++ == 60)
- {
- t.changeFlag();
- break;
- }
- System.out.println(Thread.currentThread().getName()+"………………"+num);
- }
- }
- }
- class StopThread implements Runnable
- {
- private boolean flag =true;
- public void run()
- {
- while(flag)
- System.out.println(Thread.currentThread().getName()+"………………Thread run");
- }
- public void changeFlag()
- {
- flag = false;
- }
- }
class StopThreadDemo { public static void main(String[] args) { StopThread t = new StopThread(); Thread t1 = new Thread(t); Thread t2 = new Thread(t); t1.start(); t2.start(); int num = 0; while(true) { if(num++ == 60) { t.changeFlag(); break; } System.out.println(Thread.currentThread().getName()+"………………"+num); } }}class StopThread implements Runnable{ private boolean flag = true; public void run() { while(flag) System.out.println(Thread.currentThread().getName()+"………………Thread run"); } public void changeFlag() { flag = false; }}
但是,还有一种情况,就是当多个运行时,如果都处于等待状态,可以使用Thread类的interrupt方法停止
等待状态,使程序继续运行。只是都会抛出异常(注意抛出异常是wait出现异常,而不是中断位置出现
异常),代码如下:
- class StopThreadDemo
- {
- public staticvoid main(String[] args)
- {
- StopThread t = new StopThread();
- Thread t1 = new Thread(t);
- Thread t2 = new Thread(t);
- t1.start();
- t2.start();
- int num = 0;
- while(true)
- {
- if(num++ == 60)
- {
- //t.changeFlag();
- t1.interrupt();
- t2.interrupt();
- break;
- }
- System.out.println(Thread.currentThread().getName()+"………………"+num);
- }
- }
- }
- class StopThread implements Runnable
- {
- private boolean flag =true;
- public synchronizedvoid run()
- {
- while(flag)
- {
- try
- {
- wait();
- }
- catch (InterruptedException e)
- {
- System.out.println(Thread.currentThread().getName()+"………………Exception");
- flag = false;
- }
- System.out.println(Thread.currentThread().getName()+"………………Thread run");
- }
- }
- public void changeFlag()
- {
- flag = false;
- }
- }
class StopThreadDemo { public static void main(String[] args) { StopThread t = new StopThread(); Thread t1 = new Thread(t); Thread t2 = new Thread(t); t1.start(); t2.start(); int num = 0; while(true) { if(num++ == 60) { //t.changeFlag(); t1.interrupt(); t2.interrupt(); break; } System.out.println(Thread.currentThread().getName()+"………………"+num); } }}class StopThread implements Runnable{ private boolean flag = true; public synchronized void run() { while(flag) { try { wait(); } catch (InterruptedException e) { System.out.println(Thread.currentThread().getName()+"………………Exception"); flag = false; } System.out.println(Thread.currentThread().getName()+"………………Thread run"); } } public void changeFlag() { flag = false; }}
另外,在多线程中还有这样的方法setDaemo(),守护线程,也可以理解为后台线程。这个线程特点是:
守护的对象结束,这些守护线程也就结束了。如我们可以把上面的interrupt或者同时也把wait取消,
只要把t1,t2,设置为主线程的守护线程,主线程结束,守护线程也就自动停止。
多线程中还有一个join方法,该方法的特点是:直接向cpu索要执行权,如果join在主线程中,那么主线程要
等待该调用join的线程结束,主线程才会执行。
toString在多线程输出有所不同,格式是线程名称,所属线程。setPriority()方法可以设置线程的优先级。
Thread还有yield方法停止当前线程,运行其他线程。
多线程总结(最优选择):
在多线程安全问题控制方面,选择的是lock接口与Condition接口的子类对象,这样可以实现同一把锁,使用
多把锁,因此在唤醒的时候可以直接唤醒对方的线程,在一定程度上也避免了死锁等情况的发生频率。
线程的结束的话,一种方法是通过控制循环条件,让线程自己运行完结束。或者通过"线程名.interrupt()"调用
结束。
此外,还有一种是通过守护线程的形式,当被守护的线程结束,守护线程也就结束了。
如果使用join方法的话,如果在主线程中执行,则必须等调用join的线程执行完,主线程才会执行。
toString方法和setPriority()以及yield方法也是需要注意的对象。
- 黑马程序员__java基础之线程间通信
- 黑马程序员__java基础9 包 线程1
- 黑马程序员__Java基础语法
- 黑马程序员__java基础__GUI
- 黑马程序员__java之反射
- 黑马程序员__java之集合
- 黑马程序员__java之多线程上
- 黑马程序员__java之多线程下
- 黑马程序员__java基础笔记1
- 黑马程序员__java基础视频day1、day2
- 黑马程序员__java基础视频day3、day4
- 黑马程序员__java基础视频day5
- 黑马程序员__java基础视频day7
- 黑马程序员__java基础视频day8
- 黑马程序员__java基础加强总结
- 黑马程序员__Java基础__多线程
- 黑马程序员__JAVA基础__语句
- 黑马程序员__JAVA基础__函数
- 贪心算法-Doing Homework again
- python 控制摄像头
- 黑马程序员-java基础增强_常用类
- Predicate Format String Syntax
- 黑马程序员_基础加强_Java线程通信和线程并发库
- 黑马程序员__java基础之线程间通信
- Android之Broadcast, BroadcastReceiver(广播)
- 找其中的韵母
- davinci DM365-DM368开发攻略—linux-2.6.32移植
- 电子书转换器calibre
- 联想进军美国智能手机市场
- [MySQL] 浅谈InnoDB存储引擎
- struts2.2.x入门应用(一)
- 多指尖检测的简单方法。