------------------------------------------------------------------------- 生产者和消费者问题----java 我们现在生产的是信息,有两种信息的生产 信息种类1: name = 李兴华 content = Java讲师 信息种类2: name = "mldn" content = "www.mldnJava.com" 先看一个问题例子: class Info{ private String name = "李兴华"; private String content = "Java讲师"; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getContent() { return content; } public void setContent(String content) { this.content = content; } } class Producer implements Runnable{ private Info info = null; public Producer(Info info){ this.info = info; } public void run(){ boolean isPersonInfo = false; //这个工厂生产两种东西,而且是交替生产,注意,这种东西与锁有着巨大的区别,因为他允许多个线程访问它,虽然在第一次大家都进入isPersonInfo=false的里面,但是必然有一个线程先执行完,执行完后就改变了isPersonInfo=true,那么显然这对另外一个线程来说是不合逻辑的。。。 for(int i=0;i<50;i++){ if(isPersonInfo){ this.info.setName("李兴华"); try{ Thread.sleep(90); //故意出现延时,如果不把上下两个操作看成原子操作的话,就可能别别的线程篡改 }catch(Exception e){} this.info.setContent("java讲师"); isPersonInfo = false; }else{ this.info.setName("mldn"); try{ Thread.sleep(90); //故意出现延时,如果不把上下两个操作看成原子操作的话,就可能别别的线程篡改 }catch(Exception e){} this.info.setContent("www.mldnjava.com"); isPersonInfo = true; } } } } class Consumer implements Runnable{ private Info info = null; public Consumer(Info info){ this.info = info; } public void run(){ for(int i=0;i<50;i++){ //消费者休息90秒就去取东西 try{ Thread.sleep(90); }catch(Exception e){ } System.out.println(this.info.getName()+"--->"+this.info.getContent()); } } } class ThreadCaseDemo01{ public static void main(String args[]){ Info info = new Info(); Producer pro = new Producer(info); Consumer comsumer = new Consumer(info); new Thread(pro).start(); new Thread(pro).start(); } } //以上的程序很可能被篡改。。。 我们改变下代码,因为篡改是没有原子操作,所以我们给他来个原子操作,最好把需要原子化的这些操作放到一个方法里面,然后同步方法即可。 class Info{ private String name = "李兴华"; private String content = "Java讲师"; public synchronized void set(String name,String content){ this.name = name; try{ Thread.sleep(90); //故意出现延时,如果不把上下两个操作看成原子操作的话,就可能别别的线程篡改 }catch(Exception e){} this.content = content; } public synchronized void get(){ System.out.println(this.name+"---->"+this.content); } } class Producer implements Runnable{ private Info info = null; public Producer(Info info){ this.info = info; } public void run(){ boolean isPersonInfo = false; //这个工厂生产两种东西,而且是交替生产 for(int i=0;i<50;i++){ if(isPersonInfo){ this.info.set("李兴华", "java讲师"); isPersonInfo = false; }else{ this.info.set("mldn", "www.mldnjava.com"); isPersonInfo = true; } } } } class Consumer implements Runnable{ private Info info = null; public Consumer(Info info){ this.info = info; } public void run(){ for(int i=0;i<50;i++){ //消费者休息90秒就去取东西 try{ Thread.sleep(90); }catch(Exception e){ } this.info.get(); } } } //上面的代码使得操作原子化了,但是发生了重复取东西的情况,并没有达到设置一个取走一个的目的,要解决这个问题,需要使用等待与唤醒机制 在Object类中的唤醒有两个:notify() 和notifyAll() 一般来说,等待的线程一般会按照顺序排列,如果现在使用了notify()方法的话,则会唤醒第一个等待的线程执行,如果使用notifyAll()方法的话,则会唤醒所有等待的线程,那么优先级最高的那个线程就有可能先执行 因为set和get的时候需要判断是否有人在操作,那么我们可以修改info类 class Info{ private String name = "李兴华"; private String content = "Java讲师"; // private boolean isProducing = true;//因为Info是临界区,当然他最清楚谁在里面 //修改得更加易于阅读 private boolean producerHaveLock = true; //因为是放东西到仓库里面,当然是生产者的行为 //synchronized是为了防止多个同类线程进入 //但是他不能防止set和get操作在同一时间只能发生一个,为了使得两个毫不相关的方法也同步起来,那么只能使用一个变量来判断是否有线程在执行,并且启用wait()和notify()来同步多个方法。。。哈哈。。终于找到精髓 public synchronized void set(String name,String content){ //如果消费者在里面我就等。。。 if(!lockInProducer()){ try{super.wait(); }catch(Exception e){} } //----等待通过,这个时候也就是说消费者出来了后,我进行生产 this.name = name; try{ Thread.sleep(90); //故意出现延时,如果不把上下两个操作看成原子操作的话,就可能别别的线程篡改 }catch(Exception e){} this.content = content; //生产完成 giveLock2Coustomer(); } public synchronized void get(){ if(lockInProducer()){ super.wait(); } try{ Thread.sleep(300); }catch(Exception e){} System.out.println(this.name+"---->"+this.content); giveLock2Produce(); } private void giveLock2Produce(){ producerHaveLock = true; super.notify(); } private void giveLock2Coustomer(){ producerHaveLock = false; super.notify(); } private boolean lockInProducer(){ return producerHaveLock ; } } 还有几个方法已经不建议使用了,因为很可能引起死锁问题。。。 suspend();挂起 resume()反挂起 stop()定制线程 如果我们现在要想停止一个线程运行怎么办。。。? 用设置标志位的方法啦。。 class MyThread implements Runnable{ private boolean isStop = true; public void run(){ int i = 0; while(!isStop){ System.out.println("运行了"+i+“次”); } } public void stop(){ this.isStop = false; } } public class Test{ public static void main(String[] args){ MyThread m = new MyThread(); Thread t = new Thread(m); t.start(); Thread.sleep(500); m.stop(); } } --------------------------------------------------------------------------- sleep和wait的区别 sleep就是正在执行的线程主动让出cpu,cpu去执行其他线程,在sleep指定的时间过后,cpu才会回到这个线程上继续往下执行,如果当前线程进入了同步锁,sleep方法并不会释放锁,即使当前线程使用sleep方法让出了cpu,但其他被同步锁挡住了的线程也无法得到执行。wait是指在一个已经进入了同步锁的线程内,让自己暂时让出同步锁,以便其他正在等待此锁的线程可以得到同步锁并运行,只有其他线程调用了notify方法(notify并不释放锁,只是告诉调用过wait方法的线程可以去参与获得锁的竞争了,但不是马上得到锁,因为锁还在别人手里,别人还没释放。 public class MultiThread { public static void main(String[] args) { // TODO Auto-generated method stub new Thread(new Thread1()).start(); try { Thread.sleep(10); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } new Thread(new Thread2()).start(); } private static class Thread1 implements Runnable { public void run() { synchronized (MultiThread.class) { System.out.println("enter thread1..."); System.out.println("thread1 is waiting"); try { // 释放锁有两种方式,第一种方式是程序自然离开监视器的范围,也就是离开了synchronized关键字管辖的代码范围,另一种方式就是在synchronized关键字管辖的代码内部调用监视器对象的wait方法。这里,使用wait方法释放锁。 MultiThread.class.wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println("thread1 is going on..."); System.out.println("thread1 is being over!"); } } } private static class Thread2 implements Runnable { public void run() { // TODO Auto-generated method stub synchronized (MultiThread.class) { System.out.println("enter thread2..."); System.out.println("thread2 notify other thread can release wait status.."); // 由于notify方法并不释放锁, // 即使thread2调用下面的sleep方法休息了10毫秒,但thread1仍然不会执行,因为thread2没有释放锁,所以Thread1无法得到锁。 MultiThread.class.notify(); System.out.println("thread2 is sleeping ten millisecond..."); try { Thread.sleep(10); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println("thread2 is going on..."); System.out.println("thread2 is being over!"); } } } }