黑马程序员——Java多线程2

来源:互联网 发布:电脑中文编程语言视频 编辑:程序博客网 时间:2024/05/22 05:28

------- android培训java培训、期待与您交流! ----------

线程间通信

 唤醒等待模式

用wait()、notify()、notifyAll()方法进行线程间通信。这三个方法全都定义在Object类中。只能用在同步线程中,被相应的锁调用。
例子如下:假设有两个变量作为临界资源,一个输入线程,一个输出线程,要求输入线程给两个变量赋值,输出线程打印两个变量,输入线程和输出线程轮流执行,并且两个变量必须同时赋值和同时打印。
class Resource{private String name;private String gender;public Boolean flag;//是否已写入变量public Resource(){this.flag=false;}public synchronized void setValue(String name,String gender){if (flag) {try{this.wait();}catch(Exception e){}}this.name=name;this.gender=gender;this.flag=true;this.notify();}public synchronized void out(){if (!flag) {try{this.wait();}catch(Exception e){}}System.out.println(this.name+">>>>"+this.gender);this.flag=false;this.notify();}}class Input implements Runnable{Resource r;public Input(Resource r){this.r=r;}public void run(){int x=0;while(true){if (x==0) {r.setValue("小兔","女");}else{r.setValue("小强","男");}x=(x+1)%2;}}}class Output implements Runnable{Resource r;public Output(Resource r){this.r=r;}public void run(){while(true)r.out();}}class ThreadCommunication{public static void main(String[] args) {Resource r=new Resource();(new Thread(new Input(r))).start();(new Thread(new Output(r))).start();}}
运行结果如下:

其中,用于同步函数的锁是this,所以唤醒被this锁住的线程用this.notify(),this.wait()同理。

生产者消费者例子

上面的例子有局限性,当多个线程输入,多个线程输出时,结果就会出现错误(输入一次,输出多次)。如图所示是两个输入线程,两个输出线程按照上面一个例子的线程间通信写法,输出的结果:

结果分析:输入线程1生产了一个商品651,却被消费者消费了两次,这显然是错误数据。
为了解决这个问题,将代码调整如下:
class ProductConsumerDemo{public static void main(String[] args) {Resource r=new Resource();(new Thread(new Producter(r))).start();(new Thread(new Producter(r))).start();(new Thread(new Customer(r))).start();(new Thread(new Customer(r))).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()+"...Producter..."+this.name);this.flag=true;this.notifyAll();}public synchronized void out(){while (!flag) {try{this.wait();}catch(Exception e){}}System.out.println(Thread.currentThread().getName()+"...Customer........"+this.name);this.flag=false;this.notifyAll();}}class Producter implements Runnable{Resource r=null;public Producter(Resource r){this.r=r;}public void run(){while(true)r.set("commodity");}}class Customer implements Runnable{Resource r=null;public Customer(Resource r){this.r=r;}public void run(){while(true)r.out();}}
注意上面的代码和等待唤醒机制的例子略有不同,将Input类改为Producter类,将Output类改为Customer类,但是本质还是一样的。上面的代码更改的地方在于循环判断标记,和将notify()改为notifyAll(),这样就会产生正确的输出结果如图:

虽然有两个线程生产,两个线程消费,但是没有出现混乱,符合了生产一个商品,消费一个商品的规则。

生产者消费者例子用新API的写法

在JDK1.5中对多线程通信添加了新的API。主要是java.util.concurrent.locks包中的Lock接口,ReentrantLock类,Condition类。
主要的语句有
Lock lock=new ReentrantLock();创建一个锁对象
Condition condition=lock.newCondition();创建一个Condition对象
lock.lock();给下面的代码加锁,即下面的代码到同一时刻只能被一个线程访问。
lock.unlock();给上面的代码解锁
 用新的API重写上面的例子的代码如下:
import java.util.concurrent.locks.*;class ProductConsumerDemo{public static void main(String[] args) {Resource r=new Resource();(new Thread(new Producter(r))).start();(new Thread(new Producter(r))).start();(new Thread(new Customer(r))).start();(new Thread(new Customer(r))).start();}}class Resource{private String name;private int count=1;private boolean flag=false;Lock lock=new ReentrantLock();Condition conditionProducter=lock.newCondition();Condition conditionCustomer=lock.newCondition();public void set(String name) throws InterruptedException{lock.lock();try{while (flag) {conditionProducter.await();}this.name=name+"--"+count++;System.out.println(Thread.currentThread().getName()+"...Producter..."+this.name);this.flag=true;conditionCustomer.signal();}finally{lock.unlock();}}public void out() throws InterruptedException{lock.lock();try{while (!flag) {conditionCustomer.await();}System.out.println(Thread.currentThread().getName()+"...Customer........"+this.name);this.flag=false;conditionProducter.signal();}finally{lock.unlock();}}}class Producter implements Runnable{Resource r=null;public Producter(Resource r){this.r=r;}public void run(){while(true){try{r.set("commodity");}catch(InterruptedException e){}}}}class Customer implements Runnable{Resource r=null;public Customer(Resource r){this.r=r;}public void run(){while(true){try{r.out();}catch(InterruptedException e){}}}}
运行结果:

可以看出此段代码同样实现了多个生产者多个消费者和谐工作的例子

停止线程

停止运行中的线程

停止运行中的线程,在早期有一个stop()方法,但是现在已经过时。现在使用的方法是,加上一个flag标记,使多线程run()方法中的循环停止。
例子如下:
class StopThread{public static void main(String[] args) {DemoThread dt=new DemoThread();Thread t1=new Thread(dt);Thread t2=new Thread(dt);t1.start();t2.start();int count=0;while(true){if (count++>60) {dt.stopThread();break;}System.out.println(Thread.currentThread().getName()+"..run.."+count);}}}class DemoThread implements Runnable{private boolean flag=true;public void run(){while(flag){System.out.println(Thread.currentThread().getName()+"...run...");}}public void stopThread(){this.flag=false;}}
当主线程跑了60次时停止其他线程 

停止冻结状态的线程

上面的代码有个问题,就是如果线程在判断标记前冻结,但是没有停止,这样线程就永远也不会结束,这个时候就需要用到interrupt()方法,该方法的作用是将冻结的线程强制唤醒到运行状态,这个时候会抛出InterrputedException异常,在异常处理代码中可以更改标志变量,从而正常结束线程。示例代码如下:
class StopThread{public static void main(String[] args) {DemoThread dt=new DemoThread();Thread t1=new Thread(dt);Thread t2=new Thread(dt);t1.start();t2.start();int count=0;while(true){if (count++>60) {t1.interrupt();t2.interrupt();break;}System.out.println(Thread.currentThread().getName()+"..run.."+count);}}}class DemoThread implements Runnable{private boolean flag=true;public synchronized void run(){while(flag){try{wait();}catch(InterruptedException e){System.out.println(Thread.currentThread().getName()+"...InterruptedException...");flag=false;}System.out.println(Thread.currentThread().getName()+"...run...");}}public void stopThread(){this.flag=false;}}
上面代码的运行结果如下:


其他线程方法

setDaemon方法

守护线程就是调用setDaemon方法的线程,调用该方法的线程会在前台线程结束时自动结束,可以理解为守护线程自动变为后台线程并守护着前台线程,前台线程一旦结束,后台线程不管是否运行完,处于何种状态都立即结束。
实例代码如下:
class StopThread{public static void main(String[] args) {DemoThread dt=new DemoThread();Thread t1=new Thread(dt);Thread t2=new Thread(dt);t1.setDaemon(true);t2.setDaemon(true);t1.start();t2.start();int count=0;while(true){if (count++>60) {break;}System.out.println(Thread.currentThread().getName()+"..run.."+count);}}}class DemoThread implements Runnable{private boolean flag=true;public void run(){while(flag){System.out.println(Thread.currentThread().getName()+"...run...");}}public void stopThread(){this.flag=false;}}

join方法

当A线程执行到B线程的join方法,A就会等待直到B线程执行完才会执行。join方法可以用来临时加入线程执行。
例子如下:
class JoinDemo{public static void main(String[] args) throws Exception {DemoThread dt=new DemoThread();Thread t1=new Thread(dt);Thread t2=new Thread(dt);t1.start();t2.start();t1.join();for (int i=0;i<5 ;i++ ) {try{Thread.sleep(100);}catch(Exception e){}System.out.println(Thread.currentThread().getName()+"..run..");}}}class DemoThread implements Runnable{public void run(){for (int i=0;i<5 ;i++ ) {try{Thread.sleep(100);}catch(Exception e){}System.out.println(Thread.currentThread().getName()+"..run..");}}}
当不加入第8行的join方法时结果如下:

当加入第8行的join方法时结果如下:

 由此可见join方法的作用,此例子中是让主线程等待Thread-0线程执行完再执行。

toString()方法

在Thread类中,toString()方法返回该线程的字符串表示形式,包括线程名称名称、优先级、线程组.例如在主函数中执行以下代码:
System.out.println(Thread.currentThread().toString());
会返回Thread[main,5,main]

线程优先级

线程默认的优先级是5,最小优先级是1,最大优先级是10,在Thread类中,有三个关于优先级的常量:MIN_PRIORITY、NORM_PRIORITY、MAX_PRIORITY。其中MIN是1,MAX是10,NORM是5。这三个常量的声明形式是public static final int,这种方式可以学习,用来代替程序中的数字常量,使代码更具可读性。

yield方法

暂停当前正在执行的线程对象,并执行其他对象。用法的例子如下:
class JoinDemo{public static void main(String[] args) throws Exception {DemoThread dt=new DemoThread();Thread t1=new Thread(dt);Thread t2=new Thread(dt);t1.start();t2.start();}}class DemoThread implements Runnable{public void run(){for (int i=0;i<5 ;i++ ) {System.out.println(Thread.currentThread().getName()+"..run..");Thread.yield();}}}
结果是:

如果不加yield结果不会如此规律。

快捷多线程写法

就是用匿名内部Thread类封装要多线程运行的代码,例子如下:
class JoinDemo{public static void main(String[] args){new Thread(){public void run(){for (int i=0;i<3;i++) {System.out.println(Thread.currentThread().toString());}}}.start();for (int i=0;i<10;i++) {System.out.println(Thread.currentThread().toString());}Runnable r=new Runnable(){public void run(){for (int i=0;i<3;i++) {System.out.println(Thread.currentThread().toString());}}};(new Thread(r)).start();}}
结果如下:


0 0
原创粉丝点击