一直以来对Java多线程方面挺感兴趣的;在刚开始学习Java时,也只仅仅知道一些关于线程方面的基础知识,Thread t = new Thread(new RunnableImpl()); t.start(); 要么就是直接实现Runnabled接口,其实自己实现类继承Thread时,底层Thread类其实同样是实现了Runnable接口:public class Thread implements Runnable ;
我相信大家都会使用以上两种方式创建和启动线程,但是可能有些同学对线程的生命周期不太了解;首先提个问题,Thread().start()和Thread.run();两个方法的区别,start();方法是准备接受系统调度,一旦线程获取到CPU资源,线程立马运行起来,则此在原来主进程中又多了一个你刚刚新建的线程启动了;而run();方法则是Thread中一个普通的方法,当调用时其该run方法时,它的生命周期在当前主线程里执行,则此它的生命周期是伴随主线程的生命周期。
当然了线程的基础知识还很多了,比如面试经常问到的Wait和Sleep区别,谈谈线程的Interrupter机制,以及Synchronized和Lock(Concurrent包下的锁),还有难点多线程编程以及性能调优等等,后续有时间我会再接下来的博客中,把自己的线程知识分享出来。第一个次在Java Eye上写博客,先前两篇算是摘要吧,往后会继续更新自己的博客。欢迎大家来拍砖,共同学习Java提高。
啰嗦半天圆规正传,实际工作中队列算是用的比较多,有自己实现的比如LinkedList和Redis List数据结构Lpush和Rpop来实现,也有现成的比如:Concurrent包下各种队列,该包下的队列特性围绕在阻塞非阻塞以及是否双端。我个人实际工作中用到的队列,都是自己实现的如前面提到的。今天索性使用ArrayBlockingQueue写了一个Demo,主要功能是一个线程定时往队里仍数据,另一个线程消费该队列中数据,代码如下:
package com.test.mulit.thread; import java.util.Random; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.logging.Logger; public class MulitThread { //写线程池容量 public static final Integer writerCorePoolSize = 1; //读线程池容量 public static final Integer readCorePoolSize = 1; //队列大小 public static final Integer queueSize = 5; //定义使用日志 public static final Logger logger = Logger.getLogger("com.test.mulit.thread.MulitThread"); //读写线程共享该队列 private static ArrayBlockingQueue<Integer> queue = new ArrayBlockingQueue<Integer>(queueSize, true); public static void main(String[] args) { ScheduledExecutorService exec_w = Executors.newScheduledThreadPool(writerCorePoolSize); //满足周期性执行 logger.info("-------------准备周期性往队列扔数字啦--------------------"); exec_w.scheduleAtFixedRate(new WriterTask(queue), 0L, 4L, TimeUnit.SECONDS); ExecutorService exe_r = Executors.newFixedThreadPool(readCorePoolSize); while(true){ logger.info("----------------开始消费队列中的数字---------------"); //倘若队列为空则消费队列阻塞,在take()方法 //休息2秒钟,与生产者节奏一致 try { TimeUnit.SECONDS.sleep(1L); } catch (InterruptedException e) { logger.info(String.format("使用TimeUnit线程睡眠发生异常:", new Object[]{e.getLocalizedMessage()})); e.printStackTrace(); } ReadTask readTask = new ReadTask(queue); exe_r.submit(readTask); readTask.setExec(new AtomicBoolean(true)); } } } class WriterTask implements Runnable{ private Logger logger = Logger.getLogger("com.test.mulit.thread.EriterTask"); private ArrayBlockingQueue<Integer> queue_w; public WriterTask(ArrayBlockingQueue<Integer> queue){ this.queue_w = queue; } @Override public void run() { Random r = new Random(); int random = r.nextInt(10000); boolean flage = queue_w.offer(random); System.out.println("队列大小:" + queue_w.size()+ " Add 返回结果:" + flage); logger.info(String.format("准备写入队列数字:[wwwwwwwwwwwwwwwwwwwww] %s", new Object[]{random})); } } class ReadTask implements Runnable { public static final Logger logger = Logger.getLogger("com.test.mulit.thread.ReadTask"); private ArrayBlockingQueue<Integer> queue_r; private volatile AtomicBoolean exec = new AtomicBoolean(false); public ReadTask(ArrayBlockingQueue<Integer> queue){ this.queue_r = queue; } public AtomicBoolean getExec() { return exec; } public void setExec(AtomicBoolean exec) { this.exec = exec; } @Override public void run() { while(exec.get()){ Integer result = 0; try { result = queue_r.take(); } catch (InterruptedException e) { e.printStackTrace(); } logger.info(String.format("读取队列数字: [rrrrrrrrrrrrrrrrrrr] %s", new Object[]{result})); } } }
注意红色标注的文字,写线程和读线程的操作队列周期都刚好是2秒,控制台打印的结果如下:
信息: ----------------开始消费队列中的数字---------------
2014-1-13 0:23:47 com.test.mulit.thread.ReadTask run
信息: 读取队列数字: [rrrrrrrrrrrrrrrrrrr] 3478
2014-1-13 0:23:47 com.test.mulit.thread.WriterTask run
信息: 准备写入队列数字:[wwwwwwwwwwwwwwwwwwwww] 3478
2014-1-13 0:23:47 com.test.mulit.thread.MulitThread main
当我把读线程TimeUnit.MILLISECONDS.sleep(2L);调成2微妙时,刚好和入定时入队列操作周期2秒相差1000倍,我们再看控制台打印:
信息: ----------------开始消费队列中的数字---------------
2014-1-13 0:26:54 com.test.mulit.thread.MulitThread main
信息: ----------------开始消费队列中的数字---------------
2014-1-13 0:26:54 com.test.mulit.thread.MulitThread main
信息: ----------------开始消费队列中的数字---------------
2014-1-13 0:26:54 com.test.mulit.thread.MulitThread main
信息: ----------------开始消费队列中的数字---------------
2014-1-13 0:26:54 com.test.mulit.thread.MulitThread main
信息: ----------------开始消费队列中的数字---------------
2014-1-13 0:26:54 com.test.mulit.thread.MulitThread main
信息: ----------------开始消费队列中的数字---------------
2014-1-13 0:26:54 com.test.mulit.thread.MulitThread main
......................后面还有很多,看不到消费数字打印的日志信息,很简单的印证了当Queue为空时,调用queue.take();方法会阻塞。后来我又在WriterTask类中稍作改动代码,使用queue.add(),同时队列大小我设置为5,那么按道理队列一会就会满了,引用API中的话:“将指定的元素插入到此队列的尾部(如果立即可行且不会超过该队列的容量),在成功时返回 true,如果此队列已满,则抛出 IllegalStateException。“但是我发现后台没有抛异常,Debug发现,FutureTask里面innerRunAndReset讲该异常吃掉了。后来验证了poll() 获取并移除此队列的头,如果此队列为空,则返回 null。;peek() 获取但不移除此队列的头;如果此队列为空,则返回 null。;put()将指定的元素插入此队列的尾部,如果该队列已满,则等待可用的空间。等方法。
”
相关推荐
在多线程环境中,有时读操作比写操作更频繁。ReadWriteLock读写锁允许多个读线程同时访问资源,但写线程访问时会独占资源,确保数据的一致性。Java的ReentrantReadWriteLock实现了这种锁。 六、双端队列(Bounded ...
Java的BlockingQueue接口(如ArrayBlockingQueue)非常适合实现这一模型,它提供了线程安全的数据插入和移除操作。 在实例中提到的"全部开始 全部停止 单个停止"可能涉及到线程的启动和控制,这可以通过控制线程的...
如果多线程之间需要交换信息,可能使用了`BlockingQueue`,例如`ArrayBlockingQueue`,来进行数据传递。 虽然原始代码可能存在组织结构不清晰的问题,但作为学习过程的一部分,这样的项目可以帮助开发者理解如何在...
由于Java程序中的多个线程共享相同的内存地址空间,因此必须谨慎处理线程间的同步问题。Java提供了多种机制来实现线程安全,包括`synchronized`关键字、`volatile`关键字以及`ReentrantLock`等高级锁机制。 #### 三...
Java多线程设计模式是Java开发中的重要领域,它涉及到如何在并发环境下高效、安全地管理资源和控制程序执行流程。本资料集包含了清晰完整的PDF版书籍和源代码,为学习和理解Java多线程设计模式提供了丰富的素材。 ...
1. 同步机制:为了解决多线程并发访问共享资源导致的数据不一致问题,Java提供了synchronized关键字、Lock接口(如ReentrantLock)以及相关的并发工具类。 2. synchronized:用于修饰方法或代码块,实现互斥访问。...
2. **共享资源**:在多线程环境中,多个线程可能会访问同一块内存空间,即共享资源。这可能导致数据竞争和不一致的状态,因此需要有效的通信机制来协调线程的执行顺序。 3. **线程通信方法**: - **wait() 和 ...
Java多线程是Java编程中的一个核心概念,它允许程序同时执行多个任务,极大地提高了程序的效率和响应性。在Java中,实现多线程有两种主要方式:通过继承`Thread`类或者实现`Runnable`接口。这个压缩包文件"JAVA多...
这在多线程共享资源时尤为重要,防止多个线程同时操作同一资源,导致数据不一致。 2. **资源共享**:在生产者消费者模式中,数据的生产和消费都需要访问一个公共的数据结构,比如一个队列。这个队列就是共享资源,...
在Java编程领域,多线程是提升程序性能和并发处理能力的重要手段。本文将深入探讨《java多线程设计模式详解》中所涵盖的关键知识点,旨在帮助开发者掌握多线程设计的最佳实践。 首先,我们要理解Java多线程的基础...
在服务器多线程通信中,常常需要用到阻塞队列(BlockingQueue),如ArrayBlockingQueue、LinkedBlockingQueue等。它们提供了一种线程安全的数据结构,允许生产者线程添加元素,消费者线程移除元素,而无需显式同步。...
多线程操作是计算机编程中的一个重要概念,尤其是在并发处理和优化性能方面。在现代计算机系统中,特别是多核处理器的普及,多线程已经成为提升应用程序效率的关键技术之一。标题和描述提到的应用场景是一个典型的...
本资料主要探讨的是如何在Java环境中利用多线程设计模式来实现资源共享,以达到高效、安全和可控的并发处理。 一、什么是多线程 多线程是指在一个进程中可以同时运行多个不同的执行流,每个执行流被称为一个线程。...
多线程编程是Java语言的核心特性之一,它允许开发者创建能并发执行的多个任务,从而提高了程序的执行效率和响应速度。在Java中,多线程不仅提供了并发性,还能有效地利用CPU资源,尤其在网络应用和高交互性的环境中...
- **区别**:一个进程至少包含一个线程,而一个进程中的多个线程共享该进程的资源,如内存空间。线程切换的开销小于进程。 - **联系**:线程是进程的一部分,它们之间的关系紧密,线程的执行是在进程中进行的。 **2...
- **定义**:原子操作是指在多线程环境下不可中断的操作。Java中的原子操作包括基本的数据类型操作、String对象的操作等。此外,Java并发库还提供了一些原子类,如`AtomicInteger`、`AtomicLong`等,用于支持原子...
在多线程编程中,数据保护是至关重要的一个环节,特别是在并发环境中,多个线程可能会同时访问和修改共享数据,这可能导致数据不一致、死锁等问题。本节将深入探讨如何利用安全队列来实现多线程之间的数据保护,确保...
生产者-消费者模式是一种经典的多线程设计模式,用于解决数据共享问题,尤其是在一个线程生产数据而另一个线程消费数据的情况下。在这个模式中,生产者负责生成数据并放入共享的数据结构(如队列),而消费者则从这...
* 多线程安全:ArrayBlockingQueue使用ReentrantLock来保证多线程操作共享变量的安全问题。 * 线程等待:ArrayBlockingQueue使用Condition来控制线程等待,当队列为空或队列已满时,会让当前线程等待。 ...
- **可见性**:使用volatile保证多线程间共享变量的可见性。 - **有序性**:使用volatile、synchronized或java.util.concurrent包中的工具类来保证指令的执行顺序。 通过深入理解以上知识点,开发者可以更好地...