Java基础视频教程第12天_多线程二之线程间通信

来源:互联网 发布:java排班做二休一源码 编辑:程序博客网 时间:2024/06/07 01:18

一、多线程——线程间通信——示例代码

class Resource{String name;String sex;}class Input implements Runnable {private Resource r;Input(Resource r){this.r = r;}public void run(){int x = 0;while(true){if(x==0){r.name = "mike";r.sex = "man";}else{r.name = "丽丽";r.sex = "女";}x = (x+1)%2;}}}class Output implements Runnable {private Resource r;Output(Resource r){this.r = r;}public void run(){while(true){System.out.println(r.name+"..."+r.sex);}}}class InputOutputDemo{public static void main(String[] args) {Resource r = new Resource();Input in = new Input(r);Output out = new Output(r);Thread t1 = new Thread(in);Thread t2 = new Thread(out);t1.start();t2.start();}}

此程序存在安全问题:运行结果name和sex有可能不对应。

二、多线程——线程间通信——解决安全问题

解决安全问题:加锁
因为是两个线程,必须使用同一个锁;锁是一个对象,可选择同一个对象r作为锁

代码如下:
class Resource{String name;String sex;}class Input implements Runnable {private Resource r;Input(Resource r){this.r = r;}public void run(){int x = 0;while(true){if(x==0){synchronized(r)//r对象是唯一的{r.name = "mike";r.sex = "man";}}else{synchronized(r){r.name = "丽丽";r.sex = "女女女";}}x = (x+1)%2;}}}class Output implements Runnable {private Resource r;Output(Resource r){this.r = r;}public void run(){while(true){synchronized(r){System.out.println(r.name+"..."+r.sex);}}}}class InputOutputDemo{public static void main(String[] args) {Resource r = new Resource();Input in = new Input(r);Output out = new Output(r);Thread t1 = new Thread(in);Thread t2 = new Thread(out);t1.start();t2.start();}}
这段代码解决了姓名和性别不对应的问题,但是引发的新问题是:存入一个资源时,这个资源被重复取走多次;或者存入很多个资源却只取走了一个资源。
那么,如何实现存入一个资源就取走一个资源呢?请看下一节。

三、线程间通信——等待唤醒机制

1、 线程运行时,内存中会建立一个线程池;等待线程都存在线程池当中。
2、 当notify()时,唤醒的都是线程池中的线程。
3、 如果线程池中有很多等待的线程,那么notify()时,唤醒的是第一个等待的线程。
4、 notifyAll();唤醒线程池中所有等待的线程;
5、 wait; notify(); notifyAll(); 都使用在同步中,因为要对持有监视器(锁)的线程操作。所以要使用在同步中,因为只有同步才具有锁。
同时,wait; notify(); notifyAll();这些方法在操作同步中线程时,都必须要标识它们所操作线程持有的锁,只有同一个锁上的被等待线程,才可以被同一个锁上notify唤醒。不可以对不同锁中的线程进行唤醒。
也就是说,等待和唤醒必须是同一个锁。

6、为什么这些操作线程的方法要定义 Object 类中呢?
锁可以是任意对象,所以可以被任意对象调用的方法定义 Object 类中。

示例代码:
class Resource{String name;String sex;boolean flag = false;}class Input implements Runnable {private Resource r;Input(Resource 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();}}}}class Output implements Runnable {private Resource r;Output(Resource 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;r.notify();}}}}class InputOutputDemo{public static void main(String[] args) {Resource r = new Resource();Input in = new Input(r);Output out = new Output(r);Thread t1 = new Thread(in);Thread t2 = new Thread(out);t1.start();t2.start();}}

四、多线程间通信——代码优化 

class Resource{private String name;private String sex;private boolean flag = false;public synchronized void set(String name, String sex){if(flag)try{this.wait();}catch(Exception e){} //此处this可以省略this.name = name;this.sex = sex;flag = true;this.notify(); //此处this可以省略}public synchronized void out(){if(!flag)try{this.wait();}catch(Exception e){} //此处this可以省略System.out.println(name+".."+sex);flag = false;this.notify(); //此处this可以省略}}class Input implements Runnable {private Resource r;Input(Resource r){this.r = r;}public void run(){int x = 0;while(true){if(x==0)r.set("mike","man");elser.set("丽丽","女女女");x = (x+1)%2;}}}class Output implements Runnable {private Resource r;Output(Resource r){this.r = r;}public void run(){while(true){r.out();}}}class InputOutputDemo{public static void main(String[] args) {Resource r = new Resource();new Thread(new Input(r)).start();new Thread(new Output(r)).start();/*Input in = new Input(r);Output out = new Output(r);Thread t1 = new Thread(in);Thread t2 = new Thread(out);t1.start();t2.start();*/}}

五、线程间通信——生产者消费者

class ProducerConsumerDemo{public static void main(String[] args) {Resource r = new Resource();Producer pro = new Producer(r);Consumer con = new Consumer(r);Thread t1 = new Thread(pro);Thread t2 = new Thread(con);Thread t3 = new Thread(pro);Thread t4 = new Thread(con);t1.start();t2.start();t3.start();t4.start();}}class Resource{private String name;private int count = 1;private boolean flag = false;public synchronized void set(String name){while(flag)try{this.wait();}catch(Exception e){}this.name = name+"--"+count++;System.out.println(Thread.currentThread().getName()+"..生产者.."+this.name);flag = true;this.notifyAll();}public synchronized void out(){while(!flag)try{this.wait();}catch(Exception e){}System.out.println(Thread.currentThread().getName()+"...消费者......"+this.name);flag = false;this.notifyAll();}}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 res;Consumer(Resource res){this.res = res;}public void run(){while(true){res.out();}}}

结论:
当出现多个生产者消费者的线程时,需要用 while 和 notifyAll() 。
因为 if 只判断一次; while 每次都需要判断,只有 while(false)时,才向下执行。
同时,如果用 notify() 容易出现只唤醒本方线程的情况,有可能导致所有线程都处于等待状态。

六、线程间通信——生产者消费者JDK5.0升级版

java.util.concurrent.locks.Lock 
-ReentrantLock 

public interface Lock 

public class ReentrantLock extends Object implements Lock,Serializable

java.util.concurrent.locks.Condition 

public interface Condition 

JDK1.5 中提供了多线程升级解决方案。
将同步 synchronized 替换成显示的 Lock 操作。
将 Object 中的 wait,notify notifyAll,替换成了 Condition 对象的await(), signal(), signalAll() 。
该对象可以由 Lock 锁的newCondition()方法获取。

Lock: 替代了 synchronized
lock 
unlock
newCondition()

Condition: 替代了Object wait notify notifyAll
await();
signal();
signalAll();

该示例中,实现了本方只唤醒对方操作。
示例代码:
import java.util.concurrent.locks.*;class ProducerConsumerDemo2{public static void main(String[] args) {Resource r = new Resource();Producer pro = new Producer(r);Consumer con = new Consumer(r);Thread t1 = new Thread(pro);Thread t2 = new Thread(con);Thread t3 = new Thread(pro);Thread t4 = new Thread(con);t1.start();t2.start();t3.start();t4.start();}}class Resource{private String name;private int count = 1;private boolean flag = false;private final Lock lock = new ReentrantLock();private final Condition condition_pro = lock.newCondition();private final 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 res;Consumer(Resource res){this.res = res;}public void run(){while(true){try{res.out();}catch (InterruptedException e){}}}}

七、多线程——停止线程

如何停止线程?
只有一种,run方法结束。

1、定义循环结束标记
因为线程运行代码一般都是循环,只要控制了循环即可。

2、使用 interrupt (中断)方法。
该方法是结束线程的冻结状态,使线程回到运行状态中来。
注: stop 方法已经过时,不再使用。

特殊情况:
当线程处于了冻结状态。就不会读取到标记。那么线程就不会结束。

当没有指定的方式让冻结的线程恢复到运行状态时,这时需要对冻结进行清除。
强制让线程恢复到运行状态中来。这样就可以操作标记让线程结束。
Thread类提供该方法 interrupt();

代码:
class StopThread implements Runnable{private boolean flag = true;public synchronized void run(){int x = 0;while (flag){try{wait();}catch (InterruptedException e){System.out.println(Thread.currentThread().getName()+"...Exception: "+x);flag = false; //当中断状态清除后,改变标记标记}System.out.println(Thread.currentThread().getName()+"...run: "+x);x++;}}public void changeFlag(){flag = false;}}class StopThreadDemo {public static void main(String[] args) {StopThread st = new StopThread();Thread t1 = new Thread(st);Thread t2 = new Thread(st);t1.start();t2.start();int num = 0;while(true){if (num++ == 60){//st.changeFlag();t1.interrupt();//清除线程的中断状态t2.interrupt();//清除线程的中断状态break;}System.out.println(Thread.currentThread().getName()+"..."+num);}System.out.println("over");}}//IllegalMonitorStateException

八、多线程——守护线程

void setDaemon(boolean on)
将该线程标记为守护线程或用户线程。当正在运行的线程都是守护线程时,Java 虚拟机退出。该方法必须在启动线程前调用。

======主线程是守护线程。

======守护线程,可以理解为后台线程。

代码:
class StopThread implements Runnable{public void run(){int x = 0;while (true){System.out.println(Thread.currentThread().getName()+"...run: "+x);x++;}}}class StopThreadDemo2{public static void main(String[] args) {StopThread st = new StopThread();Thread t1 = new Thread(st);Thread t2 = new Thread(st);t1.setDaemon(true); //标记为守护线程t2.setDaemon(true); //标记为守护线程t1.start();t2.start();int num = 0;while(true){if (num++ == 60){break;}System.out.println(Thread.currentThread().getName()+"..."+num);}System.out.println("over");}}

九、多线程—— join 方法

void join()
等待该线程终止。

join:
当A线程执行到了B线程的.join()方法时,A就会等待。等B线程都执行完,A才会执行。
join可以用来临时加入线程执行。

代码:
class Demo implements Runnable {public void run(){for (int x=0 ;x<30 ;x++ ){System.out.println(Thread.currentThread().getName()+": "+x);}}}class JoinDemo{public static void main(String[] args) throws Exception  {Demo d = new Demo();Thread t1 = new Thread(d);Thread t2 = new Thread(d);t1.start();t1.join();t2.start();t2.join();for (int x=0 ;x<30 ;x++ ){System.out.println(Thread.currentThread().getName()+": "+x);}System.out.println("over");}}

十、多线程——优先级 & yield 方法

String toString(); 返回该线程的字符串表示形式,包括线程名称、优先级和线程组。

线程组:
谁开启该线程,该线程就属于哪个组。
例如:主线程main开启线程0,那么线程0就属于main组。

优先级:
优先级一共有10级,1——10,数字越大,优先级越高,获取执行权的概率就越大;默认为5 。
改变优先级: 
t1.setPriority(Thread.MAX_PRIORITY);

MAX_PRIORITY:10 
NORM_PRIORITY:5 
MIN_PRIORITY:1 

yield:
暂停当前正在执行的线程对象,并执行其他线程。
暂缓线程的运行,可以达到线程交替运行的效果。
调用方法:
Thread.yield(); 

优先级示例代码:
class Demo implements Runnable {public void run(){for (int x=0 ;x<30 ;x++ ){System.out.println(Thread.currentThread().toString()+": "+x); //toString()替换getName();}}}class PriorityDemo{public static void main(String[] args) throws Exception  {Demo d = new Demo();Thread t1 = new Thread(d);Thread t2 = new Thread(d);t1.start();t1.setPriority(Thread.MAX_PRIORITY); //设置优先级;t2.start();for (int x=0 ;x<30 ;x++ ){System.out.println(Thread.currentThread().getName()+": "+x);}System.out.println("over");}}

yield()方法代码:
class Demo implements Runnable {public void run(){for (int x=0 ;x<30 ;x++ ){System.out.println(Thread.currentThread().toString()+": "+x);Thread.yield();}}}class YieldDemo{public static void main(String[] args) throws Exception  {Demo d = new Demo();Thread t1 = new Thread(d);Thread t2 = new Thread(d);t1.start();t2.start();System.out.println("over");}}

开发中什么时候使用多线程?
当某些代码需要同时被执行时,就用单独的线程进行封装。
示例代码(匿名内部类方式): 
class ThreadTest{public static void main(String[] args) {//线程0运行代码new Thread(){public void run(){for (int x=0 ;x<30 ;x++ ){System.out.println(Thread.currentThread().getName()+": "+x);}}}.start();//线程1运行代码Runnable r = new Runnable(){public void run(){for (int y=0 ;y<30 ;y++ ){System.out.println(Thread.currentThread().getName()+": "+y);}}};new Thread(r).start();//主线程运行代码for (int z=0 ;z<30 ;z++ ){System.out.println(Thread.currentThread().getName()+": "+z);}}}
0 0
原创粉丝点击