多线程__下

来源:互联网 发布:网络主播文儿的歌曲 编辑:程序博客网 时间:2024/03/29 07:47

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

wait和sleep的区别:
wait:释放cpu执行权,释放同步中的锁。

sleep:释放cpu执行权,不释放同步中的锁。


/**线程间通信示例需求:输入一次信息,打印一次信息分析:需要两个线程一个是输入线程一个是打印线程有一个共享资源:用来存储信息和获取信息思路:定义三个类类Rec:共享的资源类Input:具备输入信息的功能类Output:具备打印信息的功能*/class Rec//定义一个对象{String name;String sex;boolean flag = false; //定义一个标记,用于控制线程输入一次,打印一次}class Input implements Runnable//具备输入功能的对象{private Rec r;Input(Rec r)//传入要操作的对象{this.r = r;}public void run()//输入方法{int x = 0;while (true){while (r.flag){try{r.wait();}catch(Exception e){}}synchronized(r){if (x == 0){r.name = "张三";//输入信息r.sex = "++++++男";}else{r.name = "李四";r.sex = "女";}x = (x+1)%2;//此代码的结果不是0就是1,利用这个特性来控制信息的交替输入,使用boolean数据类型也可以实现r.flag = true;//该语句的结果控制信息输入一次后,就进入冻结状态r.notify();//唤醒线程池中冻结的线程}}}}class Output implements Runnable{private Rec r;Output(Rec r){this.r = r;}public void run(){while (true){synchronized(r){while (!r.flag)//要让线程交替执行,就必须使其满足的条件互斥,所以这里条件是!r.flag{try{r.wait();}catch(Exception e){}}System.out.println(r.name+"++++++"+r.sex);r.flag = false;//执行完毕后修改值,让该线程执行wait()进入冻结状态,并跳转到输入功能r.notify();}}}}class  InputOutputDemo{public static void main(String[] args) {Rec r = new Rec();//建立共享资源对象Input in = new Input(r);//将共享资源传入具备输入功能的对象中Output ou = new Output(r);//将共享资源传入具备输出功能的对象中Thread t1 = new Thread(in);//建立线程Thread t2 = new Thread(ou);t1.start();//开启线程t2.start();}}

wait():
notify();
notifyAll();

都使用在同步中,因为要对持有监视器(锁)的线程操作。
所以要使用在同步中,因为只有同步才具有锁。

为什么这些操作线程的方法要定义Object类中呢?
因为这些方法在操作同步中线程时,都必须要标识它们所操作的线程的锁,
只有同一个锁上的等待线程,可以被同一个锁(notify())唤醒。
不可以对不同锁中的线程进行唤醒。
也就是说,等待和唤醒的线程必须持有相同的锁。
而锁可以是任意对象,所以可以被任意对象调用的方法定义Object类中。

生产消费者案例:
/**需求:多条线程生产商品,多条线程销售商品,且保证生产一个销售一个分析:定义多条线程生产商品定义多条线程销售商品定义一个生产者类,该类具备生产商品的功能定义一个消费者,该类具备销售商品的功能该程序中的共享资源是商品,商品可以被生产和销售*/class Res{private String name;private int count = 1;private boolean flag = false;public synchronized void set(String name)//将生产功能封装起来{/*上一个案例中,是一个线程负责输入,一个线程负责输出这个案例是,多个线程负责输入,多个线程负责输出这里的while不可以使用if代替,否则会出现,生产两个商品只卖出了一个商品,或者是生产一个商品却卖出了两次那是因为,当线程冻结后,被唤醒的线程直接执行wait后的语句,而不用判断flag标记而造成的此处改为while语句就可以避免这样的情况发生,被唤醒的线程将会再次判断flash标记这是本演示案例的重点之一*/while (flag){try{wait();}catch(Exception e){}}this.name = name+"----"+count++;//这句代码不能放在while语句前面,否则会出现先消费后生产的怪异现象System.out.println(Thread.currentThread().getName()+"-------生产者-----------"+this.name);flag = true;/*本案例重点二该出使用notifyAll是为了唤醒除本方线程的其他线程如果使用notify,将有可能一直唤醒本方线程,而本方线程的flag一直是false,下面的语句得不到执行,将会出现线程停止不动的现象*/notifyAll();}public synchronized void out(){while (!flag){try{this.wait();}catch(Exception e){}}System.out.println(Thread.currentThread().getName()+"--消费者--"+this.name);flag = false;notifyAll();}}class Producer implements Runnable{private Res r;Producer(Res r){this.r = r;}public void run(){while (true){r.set("商品");//直接调用set方法}}}class Customer implements Runnable{private Res r;Customer(Res r){this.r = r;}public void run(){while (true){r.out();}}}class ProducerCustomerDemo {public static void main(String[] args) {Res r = new Res();Producer pro = new Producer(r);Customer cus = new Customer(r);Thread t1 = new Thread(pro);//开启两个生产者线程Thread t2 = new Thread(pro);Thread t3 = new Thread(cus);//开启两个消费者线程Thread t4 = new Thread(cus);t1.start();t2.start();t3.start();t4.start();}}

这个程序有一个bug。就是每次notifyAll。都会唤醒本方。
可不可以只唤醒对方呢?有的。

JAVA1.5版本之后出现了一个新的包java.util.concurrent.locks
该包中提供了condition接口和lock接口
 将同步Synchronized替换成现实Lock操作。
将Object中的wait,notify notifyAll,替换成了Condition对象。
该对象可以对Lock锁 进行获取。
该示例中,实现了本方只唤醒对方操作。


Lock:替代了Synchronized
lock 
unlock
newCondition()

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

生产消费者案例代码升级:
/**使用1.5版本出现的lock和condition对程序进行升级优化*/import java.util.concurrent.locks.*;class Res{private String name;private int count = 1;private boolean flag = false;private Lock lock = new ReentrantLock();//定义一个锁private Condition con_pro = lock.newCondition(); //创建一个生产者conditionprivate Condition con_cus = lock.newCondition();//创建一个消费者conditionpublic void set(String name) throws InterruptedException{lock.lock();//获取锁,也可称为加锁try{while (flag){con_pro.await()//冻结pro中线程}this.name = name+"----"+count++;System.out.println(Thread.currentThread().getName()+"-------生产者-----------"+this.name);flag = true;con_cus.signal();//唤醒消费者的线程}finally{lock.unlock;//释放锁,一定要执行}}public void out() throws InterruptedException{lock.lock();//加锁try{while (!flag){con_cus.await();//冻结本方的线程}System.out.println(Thread.currentThread().getName()+"--消费者--"+this.name);flag = false;con_pro.signal();//唤醒对方线程}finally{lock.unlock();//释放锁}}}class Producer implements Runnable{private Res r;Producer(Res r){this.r = r;}public void run(){while (true){r.set("商品");//直接调用set方法}}}class Customer implements Runnable{private Res r;Customer(Res r){this.r = r;}public void run(){while (true){r.out();}}}class ProducerCustomerDemo {public static void main(String[] args) {Res r = new Res();Producer pro = new Producer(r);Customer cus = new Customer(r);Thread t1 = new Thread(pro);//开启两个生产者线程Thread t2 = new Thread(pro);Thread t3 = new Thread(cus);//开启两个消费者线程Thread t4 = new Thread(cus);t1.start();t2.start();t3.start();t4.start();}}


控制线程(停止线程,加入线程,暂停线程,控制线程的优先级)

如何停止线程?
只有一种,run方法结束。
开启多线程运行,运行代码通常是循环结构。
只要控制住循环,就可以让run方法结束,也就是线程结束。
特殊情况:
当线程处于了冻结状态。
就不会读取到标记。那么线程就不会结束。

处理方法:
当没有指定的方式让冻结的线程恢复到运行状态时,如果需要对冻结状态进行清除,
强制让线程恢复到运行状态中来。这时 可以使用Thread类中的interrupt()方法来清除冻结,
这样就可以继续判断操作标记让线程结束。

代码示例:
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()+"....Excepiton");flag = false;//改变标记,让进入运行状态的线程开始执行}System.out.println(Thread.currentThread().getName()+"....run");}}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){t1.interrupt();//强制将冻结状态的线程终止,并让其进入运行状态t2.interrupt();//调用此方法会出现InterruptException异常break;}System.out.println(Thread.currentThread().getName()+"......."+num);}System.out.println("over");}}

守护线程(后台线程)
守护线程的特点:当前台线程执行完毕,只剩下守护线程时,守护线程自动结束

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


理解:调用了join()方法的线程,抢夺了CPU的执行权,这时,其他线程将会被冻结,该线程join结束,其他线程恢复到运行状态
应用:多个线程运行时,临时加入一个线程,并让其执行完。

优先级(Priority)
优先级的三种级别
MAX_PRIORITY
MIN_PRIORITY
NORM_PRIORITY(主线程的默认级别)

改变优先级的方法:Thread.setPriority(MAX_PRIORITY)
优先级的大小,可以控制线程获取执行权的概率;优先级越大,获取执行权的概率越大,优先级越小,获取执行权的概率越小。

yield()
暂停(释放执行权)当前正在执行的线程对象,并执行其他线程。

线程中使用到的方法
 void interrupt() 
          中断线程。 
java.util.concurrent
|--lock
|--lock():获取锁
|--unlock():释放锁
|--condition
|--await():等待
|--signal():唤醒一个等待线程
|--signalAll():唤醒所有等待线程

0 0
原创粉丝点击