黑马程序员_多线程2

来源:互联网 发布:性价比高的汽车知乎 编辑:程序博客网 时间:2024/05/19 20:37

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

一、线程间通讯

所谓线程间通讯,其实就是多个线程在操作同一资源,但操作的动作不同。

在Java1.5版本之前,实现线程间通讯的解决方案是:等待唤醒机制

该机制用到了Object类中的3个方法:wait()、notify()、notifyAll()。

这3个方法都使用在同步中,因为要对持有对象监视器(锁)的线程操作,只有同步才具有锁。

之所以定义在Object类中,是因为这些方法在操作同步中线程时,都必须标识它们所操作线程持有的锁,只有同一个锁上的被等待进程,可以被同一个锁上notify唤醒,不可以唤醒不同锁中的线程。而锁可以是任意对象,所以可以被任意对象调用的方法定义在Object类中。

例如在下面“生产者和消费者”这个例子中,有2个生产者和2个消费者总共4个线程,每次操作共享数据时都用while(flag)判断资源标记来决定是否需要wait(),在操作完共享数据后使用notifyAll()唤醒所有生产者线程和消费者线程。

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 (InterruptedException e) {e.printStackTrace();}}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 (InterruptedException e) {e.printStackTrace();}}System.out.println(Thread.currentThread().getName() + "...消费...." + name  );flag = false;this.notifyAll();}}

在Java1.5版本之后,Java提供了一种新的线程间通讯的解决方案:Lock 和 Condition

Lock 实现提供了比使用 synchronized 方法和语句可获得的更广泛的锁定操作。此实现允许更灵活的结构,可以具有差别很大的属性,可以支持多个相关的 Condition 对象。 

Condition 将 Object 监视器方法(wait、notify 和 notifyAll)分解成截然不同的对象,以便通过将这些对象与任意 Lock 实现组合使用,为每个对象提供多个等待 set(wait-set)。其中,Lock 替代了 synchronized 方法和语句的使用,Condition 替代了 Object 监视器方法的使用。
这个解决方案意味着可以唤醒特定的线程,比唤醒所有线程效率更高。

用这个方法改进后的完整程序代码如下:

import java.util.concurrent.locks.Condition;import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock;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 synchronized void out() throws InterruptedException {lock.lock();try {while(!flag) {condition_con.await();}System.out.println(Thread.currentThread().getName() + "...消费...." + name  );flag = false;condition_pro.signal();}finally {lock.unlock();}}}class Producer implements Runnable {private Resource r;Producer(Resource r) {this.r = r;}public void run() {for(int x = 0; x < 100; x++) {try {r.set("cola");} catch (InterruptedException e) {e.printStackTrace();}}}}class Consumer implements Runnable {private Resource r;Consumer(Resource r) {this.r = r;}public void run() {for(int x = 0; x < 100; x++) {try {r.out();} catch (InterruptedException e) {e.printStackTrace();}}}}public class ProducerConsumer {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();}}

二、停止线程

注:stop方法已过时不再使用。

1.定义循环结束标记

因为线程运行代码一般都是循环,只要控制了循环即可。

class StopDemo implements Runnable {private boolean flag = true;public void run() {while(flag) {System.out.println(Thread.currentThread().getName()+"...run");}}public void changeFlag() {flag = false;}}

但这个方法有一种特殊情况会无法停止线程:当线程处于冻结状态,就不会读取到flag标记,线程就不会结束。

于是有了下面的第2种方法。

2.使用interrupt(中断)方法

该方法是结束线程的冻结方法,使线程回到运行状态中来。

这样就可以操作flag标记让线程结束。

class StopDemo implements Runnable {private boolean flag = true;public synchronized void run() {while(flag) {try {wait();} catch (InterruptedException e) {flag = false;}System.out.println(Thread.currentThread().getName()+"...run");}}}


三、守护线程

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


四、优先级

public final void setPriority(int newPriority)
更改线程的优先级。
参数:newPriority - 要为线程设定的优先级(1~10)。

static int MAX_PRIORITY
线程可以具有的最高优先级(10)。
static int MIN_PRIORITY
线程可以具有的最低优先级(1)。
static int NORM_PRIORITY
分配给线程的默认优先级(5)。


五、join和yield

public final void join() throws InterruptedException

等待该线程终止。
该方法可以指定某一个线程加入,当该线程执行完毕再继续执行原线程。

public static void yield()

暂停当前正在执行的线程对象,并执行其他线程。 

该方法可以稍微减缓线程运行,达到各线程都有机会平均运行的效果。


六、用匿名内部类写多线程

//第一种new Thread() {public void run() {//运行代码}}.start();//第二种Runnable r = new Runnable() {public void run() {//运行代码}};new Thread(r).start();