java多线程-生产者消费者模式
进程定义:
进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位
线程定义:
线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源.
线程运行图:
线程有三种创建方式
1实现Runnable接口
2 继承Thread类
3使用Callable和Future接口创建线程。具体是创建Callable接口的实现类,并实现clall()方法。并使用FutureTask类来包装Callable实现类的对象,且以此FutureTask对象作为Thread对象的target来创建线程。
线程的一些方法:
守护线程 执行一些非业务方法,比如gc。当全部都是守护线程的时候,jvm退出
线程优先级 设置线程优先级:setPriority(int priorityLevel)。参数priorityLevel范围在1-10之间,值越大优先级越高,
能被执行到的概率越大,但非优先执行。
sleep() 让当前的正在执行的线程暂停指定的时间,并进入阻塞状态 时间到了,恢复到就绪状态
join() 等待线程结束,我执行完了 你才能执行
yield() 谦让,我暂时不执行,我回到就绪状态
另外 yield()方法还与线程优先级有关,当某个线程调用yiled()方法从运行状态转换到就绪状态后,CPU从就绪状态线程队列中只会选择与该线程优先级相同或优先级更高的线程去执行。但是分配还是随机的 可能还是执行到原来的线程
线程的生命周期
新建状态(New):当线程对象对创建后,即进入了新建状态
就绪状态(Runnable):当调用线程对象的start()方法(t.start();),线程即进入就绪状态。处于就绪状态的线程,只是说明此线程已经做好了准备,能否在执行需要看待cpu的调度。
运行状态(Running):当cpu调度到该线程,该线程就会运行。
阻塞状态(Blocked):线程因为某些原因,暂时被挂起,暂时不继续执行的状态。常见的有以下三种
1.等待阻塞:运行状态中的线程执行wait()方法,使本线程进入到等待阻塞状态;
2.同步阻塞 -- 线程在获取synchronized同步锁失败(因为锁被其它线程所占用),它会进入同步阻塞状态;
3.其他阻塞 -- 通过调用线程的sleep()或join()或发出了I/O请求时,线程会进入到阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。
死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。
注:
只有使用yield()方法以后 线程从运行到就绪状态
无论使用 wait(),sleep(), join() 都是先进入阻塞 当满足了相关条件以后,在进入就绪状态
线程交互
1使用wait()、notify()和notifyAll()时需要首先对调用对象加锁【必须使用在同步代码块】
2调用wait()方法后,线程状态会从RUNNING变为WAITING,并将当线程加入到lock对象的等待队列中【会释放锁】 ,后面的代码就不执行了 。当被唤醒并被执行时,是接着上次执行到的wait()方法代码后面继续往下执行的。
3调用notify()或者notifyAll()方法后(不会释放锁),等待在lock对象的等待队列的线程不会马上从wait()方法返回,必须要等到调用notify()或者notifyAll()方法的线程将lock锁释放,等待线程才有机会从等待队列返回。这里只是有机会,因为锁释放后,等待线程会出现竞争,只有竞争到该锁的线程才会从wait()方法返回,其他的线程只能继续等待
生产者线程:
如果水果的数量在[0,9]则开始生产,否则转到消费线程。
public class Producer implements Runnable { private Fruit fruit; public Producer() { super(); } public Producer(Fruit fruit) { super(); this.fruit = fruit; } public Fruit getFruit() { return fruit; } public void setFruit(Fruit fruit) { this.fruit = fruit; } @Override public void run() { // TODO Auto-generated method stub while(true){ synchronized (fruit) { //积压了10个水果未卖出去 停掉生产的线程等待卖出去 if(fruit.getNumber()>=10){ try { fruit.wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } if(fruit.getNumber()>=0&&fruit.getNumber()<10){ try { Thread.sleep(200); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println(fruit.getName()+"被生产出来了"); fruit.setNumber(fruit.getNumber()+1); System.out.println("当前水果数量为"+fruit.getNumber()); fruit.notify(); } } } } }
消费者线程:
如果水果的数量在大于0,即可消费,否则转到生产线程
public class Customer implements Runnable { private Fruit fruit; public Customer() { super(); } public Customer(Fruit fruit) { super(); this.fruit = fruit; } public Fruit getFruit() { return fruit; } public void setFruit(Fruit fruit) { this.fruit = fruit; } @Override public void run() { while(true){ //处理并发问题 synchronized (fruit) { //判断水果的数量是否>0; if(fruit.getNumber()>0){ try { Thread.sleep(200); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println(fruit.getName()+"被买走了"); //减少水果数量 fruit.setNumber(fruit.getNumber()-1); System.out.println("当前水果数量为"+fruit.getNumber()); fruit.notify(); } else{ try { //水果已经卖完拿了 等待生产 fruit.wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } } }
Fruit类
public class Fruit { private int number; private String name; public Fruit() { super(); } public Fruit(int number, String name) { super(); this.number = number; this.name = name; } public int getNumber() { return number; } public void setNumber(int number) { this.number = number; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
当调用obj.notify()/notifyAl0()l后,调用线程依旧持有obj锁,当调用obj.wait()后,调用线程会释放obj锁,如果将synchronized (fruit)放在while(true)前面,会因为同步锁未及时释放,一直运行到obj.wait()才释放同步锁。 如果用的synchronized 同步代码块 在当运行到while结束循环的break时会自动释放锁 ,而用lock时 不会自动释放锁, synchronized在实现runable接口时 修饰在方法上 锁对象用this 而继承类Thread时 修饰在静态方法上锁对象表示当前类的类对象
public class SalesThread implements Runnable{ //这个类本身就会被共享 故变量不用再加static private int tickets=100; public SalesThread(){ } @Override public void run() { while(true){ if(tickets>0){ ff(); } else{ System.out.println("票已售完!"); break; } } } public void ff1(){ synchronized(this){ try { Thread.sleep(200); System.out.println(Thread.currentThread().getName()+"正在卖第"+(tickets--)+"张票"); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }