1.线程间通信: 实际就是多个线程在操作同一资源,但是操作的动作不同 要求: 有一个资源Res,成员变量为name和sex; 有两个进程input和output用来操作资源res,其中input用于设置资源,output用于取出资源。
package transportation;class Res{ String name; String sex;}class Input implements Runnable{ PRivate Res r; //采用带参数的默认构造函数,用于保证输入和输出进程控制的是同一资源 Input(Res r){ this.r=r; } //如果将此函数变为同步函数,那么成为单线程函数; //需要分析清楚那些需要采用同步,哪些不需要 public void run(){ int x=0; while(true){ //同步的参数为r,可以设置为Input.class或者output.class synchronized(r){ if(x%2==0){ r.name="mike"; r.sex="man"; } else { r.name="Lily"; r.sex="woman"; } x++; } } }}class Output implements Runnable{ private Res r; Output(Res r){ this.r=r; } public void run(){ while(true){ synchronized(r){ System.out.println(r.name+"..."+r.sex); } } }}public class Trans { public static void main(String[] args) { Res r=new Res();//一堆煤 即资源 Input i=new Input(r);//两个大卡车,一进一出 Output o=new Output(r); Thread t1=new Thread(i);//两条公路 Thread t2=new Thread(o); t1.start(); t2.start(); }}此时,出现的结果为: 理想结果应该为mike和Lily相互交错 分析原因: 程序中没有设置两个进程必须一前一后轮流交替运行 2.解决方案:为资源设置一个成员变量flag,用于控制输入和输出进程的执行顺序 代码如下:
标注: 1) notifyAll();唤醒所有进程 notify( ) 唤醒一个进程 wait( ) 使一个进程进入等待状态 2)使用flag来控制线程的运行, flag==false 进程Input可以运行 flag==true 进行Output可以运行 3) Wait和notify,notifyAll都使用同步中,因为要对持有监视器(锁)的线程操作 所以要使用在同步中,因为只有同步才只有锁的概念
为什么这些操作线程的方法要定义在Object类中? 因为这些方法在操作同步中线程时,都必须要标识他们所操作的线程所独有的锁。只有同一个锁上的被等待线程 可以被同一锁的notify唤醒,不可以对不同锁中的线程进行唤醒。即等待和唤醒是同一把锁 而锁可以是任意对象,所以定义在Object类中 3.代码优化
class Res{ private String name; private String sex; private boolean flag=false; public synchronized void set(String name,String sex){ if(flag==true){ try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } this.name=name; this.sex=sex; flag=true; this.notify(); } public synchronized void out(){ if(flag==false) { try { this.wait(); } catch (Exception e) { } } System.out.println(name+"..."+sex); flag=false; this.notify(); }}class Input implements Runnable{ private Res r; //同步锁在资源上,用来保证两个同步函数使用同一个锁 Input(Res r){ this.r=r; } //如果将此函数变为同步函数,那么成为单线程 public void run(){ int x=0; while(true){ if(x%2==0){ r.set("mike","man"); } else { r.set("Lily","Woman"); } x++; } }}class Output implements Runnable{ private Res r; Output(Res r){ this.r=r; } public void run(){ while(true){ r.out(); } }}public class Trans { public static void main(String[] args) { Res r=new Res();//一堆煤 new Thread(new Input(r)).start(); new Thread(new Output(r)).start(); }}1. 要求:输入输出打印编号; 两个进程,一个负责输入,一个负责输出
package transportation;class Resource{ private String name; private int count=1; private boolean flag=false; public synchronized void set(String name){ if(flag==true){ try { this.wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } this.name=name+"--"+count++; System.out.println(Thread.currentThread().getName()+"...生产者..."+this.name); flag=true; this.notify(); } public synchronized void out(){ if(!flag) { try { this.wait(); } catch (Exception e) { } } System.out.println(Thread.currentThread().getName()+"...消费者..."+this.name); flag=false; this.notify(); }}class Producer implements Runnable{ private Resource res; Producer(Resource res){ this.res=res; } public void run() { while(true){ res.set("+product+"); } }}class Consumer implements Runnable{ private Resource res; Consumer(Resource res){ this.res=res; } public void run() { while(true){ res.out(); } }}public class PV { 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); t1.start(); t2.start(); }}结果符合预期要求 2. 将主函数改为如下的代码段来设置多个线程同时运行
结果如下,可知出现问题 原因: 并不是每次进程被唤醒都会判断标记; notify唤醒的是线程池中的第一个; 会导致数据混乱 3.改进的代码: 1)notify->notifyAll //唤醒所有的线程而非线程池中的第一个线程 if(flag)->while(flag)//每次线程被唤醒之后都需要判断标志位 if(!flag)->while(!flag) 2) 有多个线程时, 判断标记位需要while,唤醒线程需要notifyAll
原因: 1.对于多个生产者和消费者,为什么要定义while判断标记 原因:让被唤醒的线程再一次判断标记 2.为什么定义notifyAll
java.util.concurrent.locks JDK1.5 提供了多线程升级解决方案。 将同步synchronized替换为lock操作,将object中的wait,notify,notifyAll,替换为condition对象。 API中的代码
class BoundedBuffer { final Lock lock = new ReentrantLock(); final Condition notFull = lock.newCondition(); final Condition notEmpty = lock.newCondition(); final Object[] items = new Object[100]; int putptr, takeptr, count; public void put(Object x) throws InterruptedException { lock.lock(); try { while (count == items.length) notFull.await(); items[putptr] = x; if (++putptr == items.length) putptr = 0; ++count; notEmpty.signal(); } finally { lock.unlock(); } } public Object take() throws InterruptedException { lock.lock(); try { while (count == 0) notEmpty.await(); Object x = items[takeptr]; if (++takeptr == items.length) takeptr = 0; --count; notFull.signal(); return x; } finally { lock.unlock(); } } }生产者消费者升级代码JDK1.5 1)显式锁机制 2)显示锁上的等待唤醒机制 该实例中实现了只唤醒对方的操作.
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; //创建锁和condition 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==true){ condition_pro.await(); //生产者等待 } this.name=name+"--"+count++; System.out.println(Thread.currentThread().getName()+"...生产者..."+this.name); flag=true; condition_con.signalAll();//消费者唤醒 }finally{ lock.unlock();//释放锁 } } public void out() throws InterruptedException { lock.lock(); try{ while(!flag) condition_con.await();//消费者等待 System.out.println(Thread.currentThread().getName()+"...消费者..."+this.name); }finally{ lock.unlock(); } condition_pro.signalAll();//唤醒生产者 flag=false; }}class Producer implements Runnable{ private Resource res; Producer(Resource res){ this.res=res; } public void run() { while(true){ try{ res.set("+product+"); }catch(InterruptedException ex){ } } }}class Consumer implements Runnable{ private Resource res; Consumer(Resource res){ this.res=res; } public void run() { while(true){ try { res.out(); } catch (InterruptedException e) { e.printStackTrace(); } } }}public class PV { 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(); }}1.停止线程 stop方法已经过时。 如何停止线程? 只有一种方法:run方法运行结束 开启多线程运行,运行代码通常是循环结构。只要控制住循环,就可以让run方法运行结束,也就是线程结束。
1)正常代码:程序可以自然终止
package transportation;class StopThread implements Runnable{ boolean flag=true; public void run() { while(flag){System.out.println(Thread.currentThread().getName()+"....run"); } } public void changeFlag(){ flag=false; }}public 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); } }}2)异常代码:线程没有正常结束
package transportation;/*不是死循环,代码并没有消耗资源;当线程处于冻结状态,就不会读取到标记,则线程不会运行*/class StopThread implements Runnable{ boolean flag=true; /*当线程1或线程2开启后,线程进入后,wait()被锁住*/ public synchronized void run() { while(flag){ try { wait(); } catch (InterruptedException e) { System.out.println(Thread.currentThread().getName()+"....Exception"); } System.out.println(Thread.currentThread().getName()+"....run"); } } public void changeFlag(){ flag=false; }}public 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); } }}结果为: 3)interrupt将处于冻结状态的线程强制恢复为运行状态
运行结果: 4)消除冻结 class StopThread implements Runnable { 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()+”….run”); } } public void changeFlag(){ flag=false; } } public class StopThreadDemo { public static void main(String[] args) { StopThread t=new StopThread();
System.out.println(Thread.currentThread().getName()+”….”+num); } } } 4)
class StopThread implements Runnable{ 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()+"....run"); } } public void changeFlag(){ flag=false; }}public 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); } }}2.守护线程 setDaemon 1)public final void setDaemon(boolean on) 将该线程标记为守护线程或用户线程。当正在运行的线程都是守护线程时,Java 虚拟机退出。 该方法必须在启动线程前调用。 2)将上面的主函数代码变为下面代码
public class StopThreadDemo { public static void main(String[] args) { StopThread t=new StopThread(); Thread t1=new Thread(t); Thread t2=new Thread(t); t1.setDaemon(true); t2.setDaemon(true); 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); } System.out.println("over"); }}3)程序中有三个线程:主线程,开启的t1,t2线程 t1,t2是守护线程;只要主函数结束,守护线程便结束 3.join public final void join() throws InterruptedException 等待该线程终止 抛出: InterruptedException - 如果任何线程中断了当前线程。当抛出该异常时,当前线程的中断状态被清除。
新闻热点
疑难解答