【JavaEE初阶】多线程实战:懒汉模式与生产者消费者 个人主页User_芊芊君子欢迎大家点赞评论收藏⭐文章系列专栏JavaEE文章目录多线程案例1.单例模式懒汉模式2.阻塞队列1阻塞队列的特性2生产者消费模型3阻塞队列的使用4编写生产者消费者模型4阻塞队列的自我实现3.定时器定时器的自我实现总结【前言】本篇笔记记录多线程典型实战案例。从懒汉式单例模式入门再深入学习阻塞队列特性、生产者‑消费者模型代码编写最后上手简易定时器的手动实现把抽象的线程同步知识落地到代码案例夯实 Java 多线程实操能力。多线程案例1.单例模式某个类在同一个进程中只能产出唯一实例。实例就是对象进程或者是服务器…classSingleton{//借助static,称为类对象是单例的privatestaticSingletoninstancenewSingleton();//想使用这个实例需要通过下面这个方法来获取publicstaticSingletongetInstance(){returninstance;}//防止通过其他方法创建出多个实例把类的构造方法设为privateprivateSingleton(){}}这个写法创建实例是在类加载时期进程启动就立刻加载可能会有很多个单例类扎堆初始化导致程序卡顿。懒汉模式就可以解决这个问题懒汉模式创建的时间是在第一次调用getInstance的时候但如果是多线程会不安全if判定和new必须是原子所以得加上锁加锁前得判定一下是否需要加锁初始化之前才行如果是在初始化之后代码不涉及修改就天然线程安全了再加锁就多余了。第一个条件是判定是否需要加锁第二个条件是判定是否需要new对象这个代码中还涉及指令重排序问题优化执行顺序用volatile可以解决使编译器对变量进行读和写时紧张优化classSingletonLazy{privatestaticvolatileSingletonLazyinstancenull;publicstaticSingletonLazygetInstance(){if(instancenull){synchronized(SingletonLazy.class){if(instancenull){instancenewSingletonLazy();}}}returninstance;}privateSingletonLazy(){}}【线程安全的懒汉模式】正确加锁双重ifvolatile2.阻塞队列1阻塞队列的特性先进先出线程安全带有阻塞效果阻塞让程序停下来暂不参与调度如果队列满了继续入队列就会产生阻塞知道队列不满为止如果队列空了继续出队列就会产生阻塞直到队列不空为止2生产者消费模型阻塞队列可以实现生产者消费者模型阻塞队列相当于是中介生产出来的东西通过中介转移给消费者生产者消费者模型的好处一定程度规避锁竞争一定程度解耦合队列不包含“业务逻辑”队列逻辑和功能都是稳定的几乎不会修改。所以A和队列交互B和队列交互耦合也没有影响。直接AB之间交互就容易出问题了生产者消费者模型可以更好的“削峰填谷”使整个系统承受更大的“突发请求”。如果A流量激增压力就会被队列承担B仍然可以按照自己的速率处理队列中的数据。队列本身业务逻辑简单消耗资源小能抗住很高的并发坏处效率降低系统结构更复杂提高了运维成本3阻塞队列的使用Java标准库中提供实现了BlockingQueue。interface修饰的不能实例化只能创建子类分别有三种实现形式数组链表优先级这里的入队列和出队列操作带有阻塞最为合适阻塞队列本身已经保证了线程安全put和take内部是带有锁的publicstaticvoidmain(String[]args)throwsInterruptedException{BlockingQueueStringqueuenewArrayBlockingQueue(10);//capacity:初始容量//BlockingDequeString queue new LinkedBlockingDeque();//BlockingDequeString queue new PriorityBlockingQueue();queue.put(1);//入队列queue.put(2);//入队列queue.put(3);//入队列Stringret;retqueue.take();//出队列System.out.println(ret);retqueue.take();//出队列System.out.println(ret);retqueue.take();//出队列System.out.println(ret);}4编写生产者消费者模型packagethread;importjava.util.concurrent.ArrayBlockingQueue;importjava.util.concurrent.BlockingQueue;publicclassDemo7{publicstaticvoidmain(String[]args){//生产者消费者模型BlockingQueueStringqueuenewArrayBlockingQueue(1000);//生产者消费者都是线程ThreadproducernewThread(()-{try{intcount0;while(true){count;queue.put(count);System.out.println(生产元素count);//生产速度慢的话消费者就需要等生产者Thread.sleep(1000);}}catch(InterruptedExceptione){e.printStackTrace();}});ThreadconsumernewThread(()-{try{while(true){Stringretqueue.take();System.out.println(消费元素ret);//消费速度慢的话生产者就需要等消费者//Thread.sleep(1000);}}catch(InterruptedExceptione){e.printStackTrace();}});producer.start();consumer.start();}}4阻塞队列的自我实现classMyBlockingQueue{privateString[]datanull;privateinthead0;privateinttail0;privateintsize0;privateObjectlockernewObject();publicMyBlockingQueue(intcapacity){datanewString[capacity];}//入队列publicvoidput(Stringelem)throwsInterruptedException{synchronized(locker){while(sizedata.length){//队列满了locker.wait();}//队列没满把元素放到tail位置上data[tail]elem;tail;if(taildata.length){tail0;}size;locker.notify();}}//出队列publicStringtake()throwsInterruptedException{synchronized(locker){while(size0){//队列为空locker.wait();}}Stringretdata[head];head;if(headdata.length){head0;}size--;locker.notify();returnret;}}3.定时器跟闹钟一样在一定时间后执行某个任务。标准库提供了定时器的实现TimerTimertimernewTimer();timer.schedule(newTimerTask(){Overridepublicvoidrun(){System.out.println(hello);}},3000);定时器的自我实现//表示定时器中要执行的一个任务classMyTimerTaskimplementsComparableMyTimerTask{privateRunnablerunnable;//存毫秒级的时间戳privatelongtime;publicMyTimerTask(Runnablerunnable,longdelay){this.runnablerunnable;this.timeSystem.currentTimeMillis()delay;}publiclonggetTime(){returntime;}publicvoidrun(){runnable.run();}OverridepublicintcompareTo(MyTimerTasko){return(int)(this.time-o.time);}}classMyTimer{privatePriorityQueueMyTimerTaskqueuenewPriorityQueue();privateObjectlockernewObject();publicMyTimer(){//创建专门的线程复制执行队列中的任务ThreadtnewThread(()-{//从队列中取首元素//判定队首元素是否时间到了//时间没到先不执行//时间到了执行任务并且把元素出队列try{while(true){synchronized(locker){while(queue.isEmpty()){locker.wait();}MyTimerTasktaskqueue.peek();longcurrentTimeSystem.currentTimeMillis();if(currentTimetask.getTime()){//时间到了可以执行任务了task.run();queue.poll();}else{//需要带有超时时间的等待locker.wait(task.getTime()-currentTime);}}}}catch(InterruptedExceptione){e.printStackTrace();}});t.start();}publicvoidschedule(Runnabletask,longdelay){synchronized(locker){queue.offer(newMyTimerTask(task,delay));locker.notify();}}}publicclassDemo2{publicstaticvoidmain(String[]args){MyTimermyTimernewMyTimer();myTimer.schedule(newRunnable(){Overridepublicvoidrun(){System.out.println(hello 1);}},4000);myTimer.schedule(newRunnable(){Overridepublicvoidrun(){System.out.println(hello 2);}},3000);myTimer.schedule(newRunnable(){Overridepublicvoidrun(){System.out.println(hello 3);}},2000);myTimer.schedule(newRunnable(){Overridepublicvoidrun(){System.out.println(hello 4);}},1000);System.out.println(hello main);}}总结本次练习覆盖三类高频多线程场景。掌握了线程安全版懒汉单例的编写思路依托阻塞队列吃透生产者消费者经典模型也完成简易阻塞队列手写最后自研定时器加深对任务调度、线程等待的理解。后续还需要多加编码练习规避并发下的线程安全问题。