生产者消费者模型ThreadLocal原理及实例详解
1、生产者消费者模型作用和示例如下:
1)通过平衡生产者的生产能力和消费者的消费能力来提升整个系统的运行效率,这是生产者消费者模型最重要的作用
2)解耦,这是生产者消费者模型附带的作用,解耦意味着生产者和消费者之间的联系少,联系越少越可以独自发展而不需要收到相互的制约
备注:对于生产者消费者模型的理解将在并发队列BlockingQueue章节进行说明,本章不做详细介绍。
packagethreadLearning.productCustomerModel; /* wait/notify机制:以资源为例,生产者生产一个资源,通知消费者就消费掉一个资源,生产者继续生产资源,消费者消费资源,以此循环。 wait():使一个线程处于等待(阻塞)状态,并且释放所持有的对象的锁; sleep():使一个正在运行的线程处于睡眠状态,是一个静态方法,调用此方法要处理InterruptedException异常; notify():唤醒一个处于等待状态的线程,当然在调用此方法的时候,并不能确切的唤醒某一个等待状态的线程而是由JVM确定唤醒哪个线程,而且与优先级无关; notityAll():唤醒所有处于等待状态的线程,该方法并不是将对象的锁给所有线程,而是让它们竞争,只有获得锁的线程才能进入就绪状态; 备注:java5通过Lock接口提供了显示的锁机制,Lock接口中定义了加锁(lock()方法)和解锁(unLock()方法),增强了多线程编程的灵活性及对线程的协调 */ //资源对象:包含商品名属性;提供生产和消费方法; classResource{ privateStringname;//商品名 privateintcount=0; privatebooleanflag=false;//生产或者消费的控制开关 publicsynchronizedvoidset(Stringname){ //生产资源 while(flag){ try{ //线程等待。消费者消费资源 wait(); }catch(Exceptione){ } } this.name=name+"---"+count++; System.out.println(Thread.currentThread().getName()+"...生产者..." +this.name); flag=true; //唤醒等待中的消费者 this.notifyAll();//唤醒在此对象监视器上等待的所有线程Object.notifyAll() } publicsynchronizedvoidout(){ //消费资源 while(!flag){ //线程等待,生产者生产资源 try{ wait(); }catch(Exceptione){ } } System.out.println(Thread.currentThread().getName()+"...消费者..." +this.name); flag=false; //唤醒生产者,生产资源 this.notifyAll(); } } //生产者 classProducerimplementsRunnable{ privateResourceres; Producer(Resourceres){ this.res=res; } //生产者生产资源 publicvoidrun(){ while(true){ res.set("商品"); } } } //消费者消费资源 classConsumerimplementsRunnable{ privateResourceres; Consumer(Resourceres){ this.res=res; } publicvoidrun(){ while(true){ res.out(); } } } publicclassProducerConsumerDemo{ publicstaticvoidmain(String[]args){ Resourcer=newResource(); Producerpro=newProducer(r); Consumercon=newConsumer(r); Threadt1=newThread(pro); Threadt2=newThread(con); t1.start(); t2.start(); } }
2、ThreadLocal
ThreadLocal提供一个线程的局部变量,访问某个线程拥有自己局部变量。当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。
ThreadLocal的接口方法只有4个方法,先来了解一下:
•voidset(Objectvalue)设置当前线程的线程局部变量的值;
•publicObjectget()该方法返回当前线程所对应的线程局部变量;
•publicvoidremove()将当前线程局部变量的值删除,目的是为了减少内存的占用,该方法是JDK5.0新增的方法。需要指出的是,
当线程结束后,对应该线程的局部变量将自动被垃圾回收,所以显式调用该方法清除线程的局部变量并不是必须的操作,但它可以加快内存回收的速度;
•protectedObjectinitialValue()返回该线程局部变量的初始值,该方法是一个protected的方法,显然是为了让子类覆盖而设计的。
这个方法是一个延迟调用方法,在线程第1次调用get()或set(Object)时才执行,并且仅执行1次。ThreadLocal中的缺省实现直接返回一个null;
总的来说ThreadLocal就是一种以空间换时间的做法,在每个Thread里面维护了一个以开地址法实现的ThreadLocal.ThreadLocalMap,把数据进行隔离,数据不共享,自然就没有线程安全方面的问题了。
示例1:
packagethreadLearning.thredLocal; /* 1、该类提供了线程局部(thread-local)变量。这些变量不同于它们的普通对应物,因为访问某个变量(通过其get或set方法)的每个线程都有自己的局部变量,它独立于变量的初始化副本。ThreadLocal实例通常是类中的privatestatic字段,它们希望将状态与某一个线程(例如:用户ID或事务ID)相关联。 2、ThreadLocal的使用 (1)在关联数据类中创建privatestaticThreadLocal在下面的类中,私有静态ThreadLocal实例(serialNum)为调用该类的静态SerialNum.get()方法的每个线程维护了一个“序列号”,该方法将返回当前线程的序列号。(线程的序列号是在第一次调用SerialNum.get()时分配的,并在后续调用中不会更改。 每个线程都保持对其线程局部变量副本的隐式引用,只要线程是活动的并且ThreadLocal实例是可访问的;在线程消失之后,其线程局部实例的所有副本都会被垃圾回收(除非存在对这些副本的其他引用)。 */ publicclassSerialNum{ privatestaticintnextSerialNum=3; privatestaticThreadLocalserialNum=newThreadLocal(){//创建一个线程本地变量 protectedsynchronizedObjectinitialValue(){ returnnewInteger(nextSerialNum++); } }; publicstaticintget(){ return((Integer)(serialNum.get())).intValue(); } publicstaticvoidmain(Stringargs[]){ Threadthead1=newThread(newRunnable(){ publicvoidrun(){ System.out.println("thead1-->"+get()); } }); Threadthead2=newThread(newRunnable(){ publicvoidrun(){ System.out.println("thead2-->"+get()); } }); thead1.start(); thead2.start(); /* 同一个Thread启动第二次会报错java.lang.IllegalThreadStateExceptionThread报错的原因,并不是说,重新启动Thread导致的, 而是因为共用一个Thread导致的,因为,如果是实现Runnable的类,每次启动线程都需要newThread(Runnable).start(),这就使得线 程没有被共用。 while(true){ thead2.start(); } */ } }
运行结果:
thead1-->3
thead2-->4
示例2:
packagethreadLearning.thredLocal; classRes{ //生成序列号共享变量 publicstaticIntegercount=0; publicstaticThreadLocalthreadLocal=newThreadLocal (){ //覆盖返回此线程局部变量的当前线程的“初始值”方法 @Override protectedIntegerinitialValue(){ return0; }; }; publicIntegergetNum(){ intcount=threadLocal.get()+1;//get()该方法返回当前线程所对应的线程局部变量 threadLocal.set(count);//将此线程局部变量的当前线程副本中的值设置为指定值 returncount; } } publicclassThreadLocaDemo2extendsThread{ privateResres; publicThreadLocaDemo2(Resres){ this.res=res; } @Override publicvoidrun(){ for(inti=0;i<3;i++){ System.out.println(Thread.currentThread().getName()+"---"+"i---"+i+"--num:"+res.getNum()); } } publicstaticvoidmain(String[]args){ Resres=newRes(); ThreadLocaDemo2threadLocaDemo1=newThreadLocaDemo2(res); ThreadLocaDemo2threadLocaDemo2=newThreadLocaDemo2(res); ThreadLocaDemo2threadLocaDemo3=newThreadLocaDemo2(res); threadLocaDemo1.start(); threadLocaDemo2.start(); threadLocaDemo3.start(); } }
运行结果:
Thread-1---i---0--num:1 Thread-2---i---0--num:1 Thread-0---i---0--num:1 Thread-2---i---1--num:2 Thread-1---i---1--num:2 Thread-2---i---2--num:3 Thread-0---i---1--num:2 Thread-1---i---2--num:3 Thread-0---i---2--num:3
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。