当前位置: 首页 > news >正文

葫芦岛公司做网站asp网站和php网站

葫芦岛公司做网站,asp网站和php网站,深圳集团网站建设专业公司,百度信息流怎么做效果好文章目录 Java 多线程技术详解目录引言多线程的概念为什么使用多线程#xff1f;多线程的特征多线程的挑战 多线程的实现方式3.1 继承 Thread 类示例代码#xff1a; 3.2 实现 Runnable 接口示例代码#xff1a; 3.3 使用 Executor 框架示例代码#xff1a; 3.4 使用 Calla… 文章目录 Java 多线程技术详解目录引言多线程的概念为什么使用多线程多线程的特征多线程的挑战 多线程的实现方式3.1 继承 Thread 类示例代码 3.2 实现 Runnable 接口示例代码 3.3 使用 Executor 框架示例代码 3.4 使用 Callable 和 Future示例代码 线程的生命周期线程状态新建状态New就绪状态Runnable运行状态Running阻塞状态Blocked / Waiting / Timed Waiting终止状态Terminated线程状态转换 线程调度调度策略Java线程调度影响线程调度的因素调度的不可预测性 线程安全synchronized关键字的使用synchronized关键字的特点synchronized的局限性如何优化synchronized示例代码7.2 Lock 接口ReentrantLock 类Condition 接口示例代码详细解释构造函数put() 方法take() 方法使用awaitNanos()方法的原因 总结 线程间通信线程间通信概述Java 中的线程间通信使用Object的wait()和notify()方法使用Lock接口和Condition接口 示例代码详细解释构造函数put() 方法take() 方法 使用awaitNanos()方法的原因总结 避免死锁死锁的四个必要条件如何避免死锁1. **破坏互斥条件**2. **破坏占有并等待条件**3. **破坏非抢占条件**4. **破坏循环等待条件** 线程池10.1 ExecutorService线程池的基本概念Java中的线程池创建线程池线程池参数示例代码 1. Fixed Thread Pool2. Cached Thread Pool3. Scheduled Thread Pool4. Single Thread Executor 线程中断如何中断一个线程检查线程中断状态示例代码注意事项 守护线程守护线程的特点创建守护线程示例代码注意事项 线程组线程组的作用创建线程组示例代码线程组的方法注意事项 线程本地存储什么是线程本地存储ThreadLocal类的使用创建ThreadLocal实例示例代码ThreadLocal类的方法注意事项 总结Java多线程的核心概念实现多线程的方式线程间通信使用Object的wait()和notify()方法使用Lock接口和Condition接口避免死锁线程池线程中断守护线程线程组线程本地存储 Java 多线程技术详解 目录 引言 Java多线程是Java平台的一个核心特性它为开发人员提供了一种在单个程序中同时执行多个任务的能力。这种并发执行的能力不仅极大地提高了程序的执行效率还允许软件更好地利用现代多核处理器的硬件资源从而实现高性能和高响应性的应用。 多线程编程的核心优势在于它能够实现任务的并行处理即在同一时间处理多个任务这对于处理I/O密集型或计算密集型的工作负载特别有效。例如一个服务器应用程序可以同时处理多个客户端请求或者一个数据分析程序可以在不同的数据集上并行运行算法。 然而多线程编程也带来了复杂性特别是当涉及到线程之间的数据共享和同步时。如果不恰当地管理多线程程序可能会遭受竞态条件、死锁、活锁、饥饿和资源泄露等问题。因此理解线程生命周期、线程状态、线程调度、线程安全、线程间通信以及如何有效地使用线程池和其他高级同步机制对于成功开发多线程应用程序至关重要。 Java标准库提供了丰富的API来支持多线程编程包括Thread类、Runnable和Callable接口、ExecutorService框架、synchronized关键字、Lock和ReentrantLock接口、Condition接口、ThreadLocal类以及各种线程池类型。熟练掌握这些工具和技术是成为高效Java多线程程序员的基础。 多线程的概念 在计算机科学中多线程是指从软件或者硬件第一级编程语言层面操作系统层面硬件层面支持执行多个线程的操作。在Java中多线程是指在一个单一的Java虚拟机JVM中同时运行多个执行路径即多个线程。每个线程都是操作系统进程中的一个执行单元具有自己的程序计数器、堆栈和局部变量但它们共享进程的全局变量和资源。 为什么使用多线程 资源利用率多线程可以提高CPU的利用率特别是在多核处理器系统中能够同时并行处理多个任务(这里说的是在多核cpu中同一时间并行执行而不是在同一时间间隔内交替执行)从而提高系统的整体性能。响应性在图形用户界面GUI应用程序中多线程确实可以极大地提高应用程序的响应性。模块化多线程可以增强程序的模块化使得大型或复杂的程序更容易管理和扩展。在多线程编程中每个线程通常负责执行一个特定的任务或一组相关任务这可以看作是将程序分解成多个独立运行的组件或模块。并发执行多线程可以实现并发执行这对于处理大量数据或执行长时间运行的任务非常有用。 多线程的特征 并发性多个线程可以同时运行尽管在单核处理器上可以表现为交替执行。共享资源线程之间共享相同的内存空间这意味着它们可以访问和修改相同的变量但这也可能导致数据不一致的问题除非采取适当的同步措施。上下文切换操作系统在多个线程之间切换执行这称为上下文切换会带来一定的开销。独立性每个线程都有自己的执行流它们可以独立于其他线程执行但也可以通过线程间通信机制协作。 多线程的挑战 同步问题当多个线程访问和修改共享资源时必须采取措施防止竞态条件和死锁。死锁当两个或多个线程无限期地等待彼此持有的资源时发生。资源竞争多个线程对同一资源的访问可能需要排队导致性能下降。调试困难多线程程序的错误往往难以重现和诊断因为线程的执行顺序可能在每次运行时都不同。 多线程的实现方式 在Java中实现多线程主要有四种常见的方式继承Thread类、实现Runnable接口、使用Executor框架以及使用Callable和Future接口。每种方式都有其适用场景和优缺点。 3.1 继承 Thread 类 继承Thread类是最直接的实现多线程的方式。你需要创建一个Thread类的子类并重写run()方法其中包含线程要执行的代码。当通过子类实例调用start()方法时run()方法会被系统调用从而开始线程的执行。 示例代码 public class MyThread extends Thread {Overridepublic void run() {System.out.println(Hello from this.getName());}public static void main(String[] args) {MyThread myThread new MyThread();myThread.start();} }优点 直接使用Thread类的方法如start(), join(), interrupt()等。可以访问和修改线程的一些属性如线程名称、优先级。 缺点 Java不允许多重继承因此如果需要继承其他类就不能再继承Thread类。不能直接使用Thread类的其他成员变量。 3.2 实现 Runnable 接口 实现Runnable接口是更常用的多线程实现方式因为它避免了Java单继承的限制。你需要创建一个实现了Runnable接口的类并实现run()方法。之后创建一个Thread对象将你的Runnable对象作为参数传入然后调用start()方法开始线程。 示例代码 public class MyRunnable implements Runnable {Overridepublic void run() {System.out.println(Hello from Thread.currentThread().getName());}public static void main(String[] args) {Thread myThread new Thread(new MyRunnable(), My Runnable Thread);myThread.start();} }优点 不影响类的继承链可以继承其他类。更加灵活适合复杂的业务逻辑。 缺点 需要额外的Thread对象来启动线程。 3.3 使用 Executor 框架 Executor框架是Java并发工具包(java.util.concurrent)的一部分提供了更高级别的抽象来管理线程。ExecutorService接口是Executor框架的核心它提供了一系列的线程管理方法如submit(), execute(), shutdown(), isTerminated()等。使用ExecutorService可以创建线程池有效地复用线程避免频繁创建和销毁线程的开销。 示例代码 import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors;public class ExecutorExample {public static void main(String[] args) {ExecutorService executor Executors.newFixedThreadPool(5);for (int i 0; i 10; i) {Runnable worker new WorkerThread(i);executor.execute(worker);}executor.shutdown();while (!executor.isTerminated()) {// 等待所有线程完成}} }class WorkerThread implements Runnable {private int id;public WorkerThread(int id) { this.id id; }Overridepublic void run() {System.out.println(Hello from WorkerThread id);} }优点 更好的资源管理通过线程池可以控制线程数量避免过多线程导致的系统资源浪费。提供了更丰富的线程控制方法如定时执行、批量提交任务等。 缺点 相对于直接使用Thread和Runnable实现起来稍微复杂一些。 3.4 使用 Callable 和 Future Callable接口类似于Runnable但是它允许线程执行后返回一个结果并且可以抛出异常。Future接口用于获取Callable执行的结果或取消任务的执行。 示例代码 import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future;public class CallableExample {public static void main(String[] args) throws ExecutionException, InterruptedException {ExecutorService executor Executors.newSingleThreadExecutor();FutureInteger future executor.submit(new MyCallable());int result future.get(); // 阻塞直到得到结果System.out.println(Result: result);executor.shutdown();} }class MyCallable implements CallableInteger {Overridepublic Integer call() throws Exception {return 42; // 假设这是一个计算结果} }优点 允许线程执行后返回结果适合需要返回值的任务。可以抛出异常提供更完整的错误处理机制。 缺点 相较于Runnable实现起来稍微复杂因为需要处理Future和可能的异常。Future.get()方法会阻塞直到任务完成需要注意避免在主线程中调用以免造成UI冻结或其他性能问题。 线程的生命周期 在Java中一个线程从创建到结束会经历一系列的状态变化这些状态构成了线程的生命周期。理解线程生命周期对于正确管理和控制线程非常重要尤其在处理线程的启动、终止、同步和调度时。 线程状态 新建状态New 线程的生命周期始于新建状态这时线程对象已经被创建但是start()方法还没有被调用。在这个状态下线程还没有开始执行任何代码。 就绪状态Runnable 当线程对象的start()方法被调用后线程进入就绪状态。此时线程已经准备好运行但是尚未被调度器选中获取CPU时间片。处于就绪状态的线程由操作系统管理等待CPU资源以便开始执行。 运行状态Running 一旦线程被调度器选中并分配到CPU时间片线程开始执行其run()方法中的代码此时线程处于运行状态。在运行状态中线程可能会因为各种原因而暂停执行如执行完一个时间片、等待I/O操作、等待其他线程释放锁、响应中断或执行sleep()方法等。 阻塞状态Blocked / Waiting / Timed Waiting 在执行过程中线程可能会进入阻塞状态这通常发生在以下几种情况下 等待锁当线程试图获取一个已被其他线程锁定的资源时它将被阻塞直到锁被释放。等待通知线程调用Object.wait()方法等待其他线程的notify()或notifyAll()通知。等待定时事件线程调用Thread.sleep(long millis)或Object.wait(long timeout)在指定的时间段内不会被调度。 终止状态Terminated 当线程的run()方法执行完毕或者线程抛出了未捕获的异常线程将进入终止状态。一旦线程终止它将不再参与调度也不能再次启动。线程对象仍然存在于内存中直到垃圾回收器将其回收。 线程状态转换 线程状态的转换是由Java虚拟机和操作系统共同管理的。以下是一些常见的状态转换 新建 → 就绪当start()方法被调用后线程从新建状态变为就绪状态。就绪 → 运行当线程被调度器选中并分配到CPU资源时从就绪状态变为运行状态。运行状态 → 就绪状态当线程的时间片用尽或者主动让出CPU如调用yield()方法它会从运行状态变回就绪状态等待下一次调度。运行 → 阻塞当线程遇到阻塞条件如等待锁、I/O操作或执行wait()时从运行状态变为阻塞状态。阻塞 → 就绪当阻塞条件解除如锁被释放、等待时间到期或收到通知线程从阻塞状态回到就绪状态。运行 → 终止当线程的run()方法执行完毕或抛出未捕获异常线程从运行状态变为终止状态。 线程调度 线程调度是操作系统的一项核心功能负责确定哪些线程应该在什么时候运行以及运行多长时间。在Java中线程调度由Java虚拟机JVM和底层操作系统协同完成主要依据线程的优先级和系统的调度策略。 调度策略 操作系统通常采用以下几种调度策略 先来先服务First-Come, First-Served, FCFS按照线程到达的先后顺序进行调度。时间片轮转Round Robin, RR将CPU时间分成相等的时间片每个就绪状态的线程轮流获得一个时间片。优先级调度Priority Scheduling根据线程的优先级高低进行调度优先级高的线程优先执行。在Java中线程的优先级可以通过Thread类的setPriority()方法设置。最短作业优先Shortest Job First, SJF优先执行预计执行时间最短的线程。 Java线程调度 在Java中线程调度遵循优先级调度原则但实际的调度细节取决于底层操作系统。Java虚拟机并不保证线程的优先级一定会直接影响线程的执行顺序而是尽力按照优先级来调度线程。此外线程优先级的范围是1最低到10最高默认优先级为5。 public class PriorityDemo {public static void main(String[] args) {// 创建低优先级线程Thread lowPriorityThread new Thread(new Runnable() {Overridepublic void run() {System.out.println(Low priority thread is running.);}});//通过Thread类的setPriority()方法来设置线程的优先级。线程的优先级是一个整数值范围从Thread.MIN_PRIORITY常量值为1代表最低优先级到Thread.MAX_PRIORITY常量值为10代表最高优先级。默认的优先级是Thread.NORM_PRIORITY常量值为5lowPriorityThread.setPriority(Thread.MIN_PRIORITY);// 创建高优先级线程Thread highPriorityThread new Thread(new Runnable() {Overridepublic void run() {System.out.println(High priority thread is running.);}});highPriorityThread.setPriority(Thread.MAX_PRIORITY);// 启动线程lowPriorityThread.start();highPriorityThread.start();} } 线程调度的重要概念包括 抢占式调度在Java中线程调度是抢占式的这意味着高优先级的线程可以打断低优先级线程的执行一旦高优先级的线程可用它将立即获得CPU时间片。时间片每个线程在运行时会获得一个时间片时间片结束后线程会回到就绪状态等待下一轮调度。线程让步线程可以通过调用Thread.yield()方法主动放弃剩余的时间片将CPU让给同优先级或更高优先级的线程。 影响线程调度的因素 线程优先级高优先级的线程有更大的机会被调度。线程状态只有处于就绪状态的线程才能被调度。系统负载系统中的线程数量和CPU核心数量会影响线程调度的效率。操作系统调度策略底层操作系统的调度策略会对Java线程的调度产生影响。线程交互线程间的同步和通信操作如等待锁或条件变量会影响线程的调度时机。 调度的不可预测性 Java线程调度的具体行为在不同操作系统和不同JVM实现中可能会有所不同因此开发者不能完全依赖于线程优先级来保证线程的执行顺序。在设计多线程应用程序时应考虑到调度的不确定性和不可预测性避免过度依赖线程调度来实现同步或定时任务。 线程安全 synchronized关键字是Java中用于实现线程安全的基本同步机制之一。它确保了在多线程环境中任何时刻只有一个线程可以执行被synchronized关键字保护的代码段。这种机制通过内部的互斥锁也称为监视器锁或内置锁来实现该锁与Java对象关联。 synchronized关键字的使用 synchronized关键字可以应用于两种情况方法和代码块。 synchronized方法 当你声明一个方法为synchronized时该方法成为一个同步方法。在该方法执行期间任何其他线程都不能调用这个对象上的任何synchronized方法。这意味着对象的锁将被持有直到该方法执行完毕。 public class Counter {private int count 0;public synchronized void increment() {count;} }synchronized代码块 你也可以使用synchronized关键字来同步代码块这允许更细粒度的控制。你必须指定一个对象作为锁通常是this对象或一个类的静态字段。 public class Counter {private int count 0;private final Object lock new Object();public void increment() {synchronized (lock) {count;}} }synchronized关键字的特点 排他性在任意时刻只有一个线程能够执行被synchronized保护的代码。有序性由于synchronized关键字的锁是基于对象的所以它强制执行了变量读取和写入的有序性避免了指令重排序带来的问题。可见性当一个线程更改了共享变量的值然后释放了锁另一个线程在获取该锁时能够看到前一个线程所做的更改。 synchronized的局限性 性能开销由于synchronized需要维护锁的所有权和等待队列因此在高并发的情况下可能会成为性能瓶颈。死锁风险如果多个synchronized代码块或方法没有正确的加锁顺序可能会导致死锁。 如何优化synchronized 虽然synchronized关键字是实现线程安全的一种简单方式但在高并发场景下可能不是最优的选择。以下是一些优化策略 减少锁的范围只在必要的时候使用synchronized尽量减小同步代码块的大小。使用锁分离如果可能将共享资源分割每个资源有自己的锁这样可以减少锁的竞争。使用更高效的锁如java.util.concurrent包中的ReentrantLock它提供了比synchronized更灵活的锁定机制如可重入、公平性和条件变量。 示例代码 public class Counter {private int count 0;private final Object lock new Object();public void increment() {synchronized (lock) {count;}}public int getCount() {synchronized (lock) {return count;}} }在上面的例子中Counter类使用一个私有的锁对象来同步对count字段的访问。这确保了increment和getCount方法在多线程环境下的线程安全性。 7.2 Lock 接口 Lock接口是java.util.concurrent.locks包的一部分它提供了一种更高级别的锁定机制比synchronized关键字更灵活、更强大。Lock接口定义了以下主要方法 void lock(): 获取锁。如果锁已被另一个线程持有则当前线程将一直等待直到锁被释放。void unlock(): 释放锁。boolean tryLock(): 尝试获取锁。如果锁不可用则立即返回false不会阻塞线程。boolean tryLock(long time, TimeUnit unit): 尝试获取锁。如果锁不可用则等待一定的时间如果在等待时间内锁仍未被释放则返回false。Condition newCondition(): 返回一个Condition对象可以用来实现更复杂的线程间同步。 ReentrantLock 类 ReentrantLock是Lock接口的一个实现它提供了一个可重入的互斥锁。ReentrantLock有两个构造函数分别用于创建公平锁和非公平锁 公平锁线程按照请求锁的顺序获取锁这样可以减少线程的饥饿现象但是性能通常不如非公平锁。非公平锁线程获取锁时没有固定的顺序可能会导致后请求锁的线程在某些情况下先于前面的线程获取锁这种情况下锁的获取可能更偏向于当前正在运行的线程从而提高性能。 ReentrantLock还提供了以下额外的控制功能 可中断的锁获取lockInterruptibly()允许线程在等待锁时响应中断。锁的公平性控制通过构造函数的布尔参数来决定锁是否为公平锁。锁的重入次数ReentrantLock允许同一个线程多次获取同一个锁而不会造成死锁。 Condition 接口 Condition接口也是java.util.concurrent.locks包的一部分它与Lock接口一起使用提供了一种比Object类的wait()和notify()方法更高级的线程等待和唤醒机制。Condition接口允许线程等待某个条件满足而不仅仅是在对象监视器上等待。 Condition接口的主要方法包括 void await(): 释放锁并使当前线程等待直到其他线程调用与此Condition相关的signal()或signalAll()方法。void signal(): 唤醒一个等待此Condition的线程。void signalAll(): 唤醒所有等待此Condition的线程。 示例代码 下面是一个使用ReentrantLock和Condition的示例展示如何实现一个简单的生产者-消费者模式 import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.ReentrantLock;public class Buffer {private final ReentrantLock lock new ReentrantLock();private final Condition notFull lock.newCondition();private final Condition notEmpty lock.newCondition();private final Object[] items;private int putIndex, takeIndex, count;public Buffer(int capacity) {if (capacity 0) {throw new IllegalArgumentException(Capacity must be greater than 0.);}items new Object[capacity];}public void put(Object item) throws InterruptedException {lock.lock();try {// 如果缓冲区满则等待while ((count items.length) || (putIndex takeIndex)) {long nanos 1000 * 1000 * 1000; // 1秒nanos notFull.awaitNanos(nanos);}items[putIndex] item;if (putIndex items.length) putIndex 0;count;// 唤醒等待的消费者线程notEmpty.signal();} finally {lock.unlock();}}public Object take() throws InterruptedException {lock.lock();try {// 如果缓冲区空则等待long nanos 1000 * 1000 * 1000; // 1秒while (count 0 nanos 0) {nanos notEmpty.awaitNanos(nanos);}if (count ! 0) {Object x items[takeIndex];if (takeIndex items.length) takeIndex 0;--count;// 唤醒等待的生产者线程notFull.signal();return x;} else {return null; // 缓冲区仍然为空返回null}} finally {lock.unlock();}} }详细解释 构造函数 构造函数接收一个整数参数capacity用于指定缓冲区的大小。如果传递的容量小于或等于0将抛出IllegalArgumentException。构造函数还初始化了items数组并设置了putIndex、takeIndex和count变量的初始值。 put() 方法 生产者线程调用put()方法将对象放入缓冲区。该方法的主要逻辑如下 获取锁: 使用lock.lock()获取锁。 检查缓冲区是否已满: 使用while (count items.length)检查缓冲区是否已满。如果缓冲区已满线程将调用notFull.awaitNanos(nanos)等待其中nanos是最大等待时间以纳秒为单位。awaitNanos()方法允许线程等待一个特定的时间长度并返回剩余的等待时间。如果条件未在指定时间内满足线程将继续执行下一个循环迭代。 放入对象: 如果缓冲区未满生产者将对象放入items数组中。更新putIndex和count变量。 唤醒消费者线程: 调用notEmpty.signal()来唤醒等待的消费者线程。 释放锁: 使用lock.unlock()释放锁。 take() 方法 消费者线程调用take()方法从缓冲区取出对象。该方法的主要逻辑如下 获取锁: 使用lock.lock()获取锁。 检查缓冲区是否为空: 使用while (count 0)检查缓冲区是否为空。如果缓冲区为空线程将调用notEmpty.awaitNanos(nanos)等待其中nanos是最大等待时间以纳秒为单位。awaitNanos()方法允许线程等待一个特定的时间长度并返回剩余的等待时间。如果条件未在指定时间内满足线程将继续执行下一个循环迭代。 取出对象: 如果缓冲区不为空消费者将对象从items数组中取出。更新takeIndex和count变量。 唤醒生产者线程: 调用notFull.signal()来唤醒等待的生产者线程。 释放锁: 使用lock.unlock()释放锁。 使用awaitNanos()方法的原因 使用awaitNanos()方法而不是普通的await()方法有几个好处 避免忙等: awaitNanos()方法允许线程在等待期间释放锁并在指定的时间后自动恢复从而避免了忙等的情况。 节省CPU资源: 通过限制等待时间可以减少CPU的使用率避免不必要的循环检查。 提高系统响应性: 如果条件在等待时间内没有满足线程将退出等待状态并继续执行这有助于提高系统的响应性。 总结 Lock接口和ReentrantLock类提供了更高级、更灵活的锁控制机制适用于需要更细粒度控制的场景。使用Lock接口和ReentrantLock时需要注意锁的获取和释放必须配对否则会导致死锁或资源泄露。同时Condition接口提供了更灵活的线程间同步方式有助于实现更复杂的同步逻辑。 线程间通信 线程间通信概述 线程间通信是指在一个多线程环境中不同线程之间共享信息和协调行为的过程。这对于确保程序的正确执行和提高效率至关重要。线程间通信通常涉及以下几种机制 共享内存: 多个线程共享相同的内存空间通过读写共享变量来通信。必须注意同步访问以防止竞态条件。 信号量和条件变量: 信号量用于管理资源的访问权限。条件变量允许线程等待特定条件的满足。 消息队列: 一种基于队列的数据结构线程可以向队列发送消息其他线程可以从队列中读取消息。 管道Pipes: 允许线程或进程之间通过管道进行通信。 事件对象: 用于信号通知可以用来同步线程。 Java 中的线程间通信 在Java中最常用的线程间通信机制包括使用Object类的wait()和notify()方法以及使用java.util.concurrent包中的Lock接口和Condition接口。 使用Object的wait()和notify()方法 这是Java中最基本的线程间通信方式之一。wait()方法使当前线程等待直到另一个线程调用notify()或notifyAll()方法。这些方法必须在同步块内调用通常在synchronized块或方法中。 使用Lock接口和Condition接口 Lock接口提供了更高级的锁定机制而Condition接口则提供了更灵活的线程等待和唤醒机制。这些接口位于java.util.concurrent.locks包中。 示例代码 下面是一个使用ReentrantLock和Condition的示例展示如何实现一个简单的生产者-消费者模式 import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.ReentrantLock;public class Buffer {private final ReentrantLock lock new ReentrantLock();private final Condition notFull lock.newCondition();private final Condition notEmpty lock.newCondition();private final Object[] items;private int putIndex, takeIndex, count;public Buffer(int capacity) {if (capacity 0) {throw new IllegalArgumentException(Capacity must be greater than 0.);}items new Object[capacity];}public void put(Object item) throws InterruptedException {lock.lock();try {// 如果缓冲区满则等待while ((count items.length) || (putIndex takeIndex)) {long nanos 1000 * 1000 * 1000; // 1秒nanos notFull.awaitNanos(nanos);}items[putIndex] item;if (putIndex items.length) putIndex 0;count;// 唤醒等待的消费者线程notEmpty.signal();} finally {lock.unlock();}}public Object take() throws InterruptedException {lock.lock();try {// 如果缓冲区空则等待long nanos 1000 * 1000 * 1000; // 1秒while (count 0 nanos 0) {nanos notEmpty.awaitNanos(nanos);}if (count ! 0) {Object x items[takeIndex];if (takeIndex items.length) takeIndex 0;--count;// 唤醒等待的生产者线程notFull.signal();return x;} else {return null; // 缓冲区仍然为空返回null}} finally {lock.unlock();}} }详细解释 构造函数 构造函数接收一个整数参数capacity用于指定缓冲区的大小。如果传递的容量小于或等于0将抛出IllegalArgumentException。构造函数还初始化了items数组并设置了putIndex、takeIndex和count变量的初始值。 put() 方法 生产者线程调用put()方法将对象放入缓冲区。该方法的主要逻辑如下 获取锁: 使用lock.lock()获取锁。 检查缓冲区是否已满: 使用while ((count items.length) || (putIndex takeIndex))检查缓冲区是否已满。如果缓冲区已满线程将调用notFull.awaitNanos(nanos)等待其中nanos是最大等待时间以纳秒为单位。awaitNanos()方法允许线程等待一个特定的时间长度并返回剩余的等待时间。如果条件未在指定时间内满足线程将继续执行下一个循环迭代。 放入对象: 如果缓冲区未满生产者将对象放入items数组中。更新putIndex和count变量。 唤醒消费者线程: 调用notEmpty.signal()来唤醒等待的消费者线程。 释放锁: 使用lock.unlock()释放锁。 take() 方法 消费者线程调用take()方法从缓冲区取出对象。该方法的主要逻辑如下 获取锁: 使用lock.lock()获取锁。 检查缓冲区是否为空: 使用while (count 0 nanos 0)检查缓冲区是否为空。如果缓冲区为空线程将调用notEmpty.awaitNanos(nanos)等待其中nanos是最大等待时间以纳秒为单位。awaitNanos()方法允许线程等待一个特定的时间长度并返回剩余的等待时间。如果条件未在指定时间内满足线程将继续执行下一个循环迭代。 取出对象: 如果缓冲区不为空消费者将对象从items数组中取出。更新takeIndex和count变量。 唤醒生产者线程: 调用notFull.signal()来唤醒等待的生产者线程。 释放锁: 使用lock.unlock()释放锁。 使用awaitNanos()方法的原因 使用awaitNanos()方法而不是普通的await()方法有几个好处 避免忙等: awaitNanos()方法允许线程在等待期间释放锁并在指定的时间后自动恢复从而避免了忙等的情况。 节省CPU资源: 通过限制等待时间可以减少CPU的使用率避免不必要的循环检查。 提高系统响应性: 如果条件在等待时间内没有满足线程将退出等待状态并继续执行这有助于提高系统的响应性。 总结 线程间通信是多线程编程的关键部分它确保了线程之间的协作和数据一致性。使用Lock接口和Condition接口可以实现更高级、更灵活的同步机制帮助开发者更好地管理线程间的交互。 避免死锁 避免死锁是多线程编程中的一个重要方面尤其是在Java中。死锁是一种特殊情况下的资源竞争问题其中一个或多个线程永久阻塞因为每个线程都在等待另一个线程持有的锁。为了帮助你完善关于如何避免死锁的内容我将提供一些关键点和建议。 死锁的四个必要条件 死锁通常由以下四个必要条件引起 互斥条件至少有一个资源必须处于非共享模式即一次只能被一个进程使用。占有并等待进程已保持至少一个资源但又等待新的资源。非抢占资源不能被抢占只能由拥有进程自愿释放。循环等待存在一种进程-资源的循环链每个进程已占用的资源被下一个进程所期望。 如何避免死锁 为了避免死锁的发生可以采取以下策略 1. 破坏互斥条件 资源共享尽可能使资源可共享减少独占资源的需求。避免使用锁如果可能的话重新设计代码以避免使用锁。 2. 破坏占有并等待条件 一次性获取所有资源确保线程在开始执行之前获取所有必需的锁。按顺序获取锁如果多个线程需要获取多个锁则让它们按照固定的顺序获取锁这样可以避免形成循环等待。 3. 破坏非抢占条件 超时机制为锁请求添加超时机制如果超过一定时间无法获得锁则释放已经持有的锁并稍后再重试。使用tryLock使用ReentrantLock的tryLock()方法来尝试获取锁如果锁不可用则不会阻塞线程。 4. 破坏循环等待条件 锁顺序如果一个线程需要多个锁确保总是按照相同的顺序获取这些锁。死锁检测定期检查是否有可能出现死锁的情况如果检测到潜在的死锁则释放锁并重试。 线程池 10.1 ExecutorService 线程池是Java多线程编程中的一个重要概念它可以有效地管理线程的创建和销毁过程减少系统资源的消耗并提供了一种更灵活的方式来管理并发任务。下面是关于线程池的一些详细信息可以帮助你更好地理解和使用线程池。 线程池的基本概念 线程池是在Java中管理线程的一种机制它预先创建一定数量的线程这些线程处于等待状态当有新的任务到来时线程池会分配一个线程来执行这个任务。当任务完成后线程并不会被销毁而是返回到线程池中等待下一个任务。这种方式可以避免频繁创建和销毁线程带来的开销提高程序的执行效率。 Java中的线程池 Java中线程池的主要接口是ExecutorService它是java.util.concurrent包的一部分。ExecutorService提供了一些重要的方法来控制线程池的生命周期如submit()、execute()、shutdown()和awaitTermination()等。 创建线程池 线程池可以通过Executors工厂类来创建该类提供了几个静态方法来创建不同类型的线程池。以下是几种常见的线程池类型 Fixed Thread Pool (newFixedThreadPool(nThreads)): 固定大小的线程池线程数量固定。如果提交的任务数量超过了线程池的大小这些任务会被放入一个队列中等待执行。适用于任务数量未知的情况尤其是处理大量短期异步任务时。 Cached Thread Pool (newCachedThreadPool()): 可缓存的线程池线程数量动态调整。当没有任务时多余的空闲线程会被销毁。适用于执行大量的短期异步任务。 Scheduled Thread Pool (newScheduledThreadPool(nThreads)): 定时线程池用于执行周期性或定时任务。支持延迟执行任务和周期性执行任务。 Single Thread Executor (newSingleThreadExecutor()): 单一线程池只包含一个线程。适用于需要保证任务按顺序执行的场合。 线程池参数 核心线程数 (corePoolSize): 表示线程池中的最小线程数量。即使没有任务执行线程池也会维护这些线程。这些线程通常不会被终止除非调用了allowCoreThreadTimeOut(true)。 最大线程数 (maximumPoolSize): 表示线程池中可以创建的最大线程数量。当任务队列满时线程池会继续创建新线程直到达到最大线程数。 空闲线程存活时间 (keepAliveTime): 指定了线程空闲时可以存活的时间长度。对于超过核心线程数的线程如果它们空闲了指定的时间长度就会被终止。对于核心线程默认情况下如果设置了allowCoreThreadTimeOut(true)核心线程也会遵守这个时间限制。 时间单位 (TimeUnit): 用于指定keepAliveTime的时间单位例如秒(SECONDS)、毫秒(MILLISECONDS)等。 工作队列 (workQueue): 用于存放等待执行的任务的队列。通常使用ArrayBlockingQueue, LinkedBlockingQueue或SynchronousQueue等。当线程池中的线程数达到最大线程数时新来的任务将会被放入此队列等待执行。 拒绝策略 (handler): 当任务队列已满并且线程池已经达到最大线程数时如果还有新的任务提交那么线程池会采取拒绝策略来处理这些任务。常见的拒绝策略包括 AbortPolicy: 抛出RejectedExecutionException异常。CallerRunsPolicy: 由调用者所在的线程来执行该任务。DiscardPolicy: 不处理该任务也就是将其丢弃。DiscardOldestPolicy: 丢弃队列中最旧的任务然后重试执行当前任务。 示例代码 1. Fixed Thread Pool import java.util.concurrent.*;public class FixedThreadPoolExample {public static void main(String[] args) {// 设置线程池参数int corePoolSize 3; // 核心线程数int maximumPoolSize 3; // 最大线程数long keepAliveTime 60L; // 空闲线程存活时间TimeUnit unit TimeUnit.SECONDS; // 时间单位BlockingQueueRunnable workQueue new LinkedBlockingQueue(); // 工作队列RejectedExecutionHandler handler new ThreadPoolExecutor.CallerRunsPolicy(); // 拒绝策略// 创建线程池ExecutorService fixedThreadPool new ThreadPoolExecutor(corePoolSize,maximumPoolSize,keepAliveTime,unit,workQueue,handler);for (int i 0; i 10; i) {final int taskId i;fixedThreadPool.submit(() - {System.out.println(Task ID: taskId is running by Thread.currentThread().getName());try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}});}// 关闭线程池fixedThreadPool.shutdown();while (!fixedThreadPool.isTerminated()) {// 等待所有任务完成}System.out.println(All tasks completed.);} }2. Cached Thread Pool import java.util.concurrent.*;public class CachedThreadPoolExample {public static void main(String[] args) {// 设置线程池参数int corePoolSize 0; // 核心线程数int maximumPoolSize Integer.MAX_VALUE; // 最大线程数long keepAliveTime 60L; // 空闲线程存活时间TimeUnit unit TimeUnit.SECONDS; // 时间单位BlockingQueueRunnable workQueue new SynchronousQueue(); // 工作队列RejectedExecutionHandler handler new ThreadPoolExecutor.AbortPolicy(); // 拒绝策略// 创建线程池ExecutorService cachedThreadPool new ThreadPoolExecutor(corePoolSize,maximumPoolSize,keepAliveTime,unit,workQueue,handler);for (int i 0; i 10; i) {final int taskId i;cachedThreadPool.submit(() - {System.out.println(Task ID: taskId is running by Thread.currentThread().getName());try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}});}// 关闭线程池cachedThreadPool.shutdown();while (!cachedThreadPool.isTerminated()) {// 等待所有任务完成}System.out.println(All tasks completed.);} }3. Scheduled Thread Pool import java.util.concurrent.*;public class ScheduledThreadPoolExample {public static void main(String[] args) {// 设置线程池参数int corePoolSize 2; // 核心线程数int maximumPoolSize 2; // 最大线程数long keepAliveTime 60L; // 空闲线程存活时间TimeUnit unit TimeUnit.SECONDS; // 时间单位BlockingQueueRunnable workQueue new LinkedBlockingQueue(); // 工作队列RejectedExecutionHandler handler new ThreadPoolExecutor.CallerRunsPolicy(); // 拒绝策略// 创建线程池ScheduledExecutorService scheduledThreadPool new ScheduledThreadPoolExecutor(corePoolSize,handler);for (int i 0; i 10; i) {final int taskId i;scheduledThreadPool.scheduleAtFixedRate(() - {System.out.println(Task ID: taskId is running by Thread.currentThread().getName());try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}, 0, 2, TimeUnit.SECONDS);}// 关闭线程池scheduledThreadPool.shutdown();while (!scheduledThreadPool.isTerminated()) {// 等待所有任务完成}System.out.println(All tasks completed.);} }4. Single Thread Executor import java.util.concurrent.*;public class SingleThreadExecutorExample {public static void main(String[] args) {// 设置线程池参数int corePoolSize 1; // 核心线程数int maximumPoolSize 1; // 最大线程数long keepAliveTime 0L; // 空闲线程存活时间TimeUnit unit TimeUnit.MILLISECONDS; // 时间单位BlockingQueueRunnable workQueue new LinkedBlockingQueue(); // 工作队列RejectedExecutionHandler handler new ThreadPoolExecutor.CallerRunsPolicy(); // 拒绝策略// 创建线程池ExecutorService singleThreadExecutor new ThreadPoolExecutor(corePoolSize,maximumPoolSize,keepAliveTime,unit,workQueue,handler);for (int i 0; i 10; i) {final int taskId i;singleThreadExecutor.submit(() - {System.out.println(Task ID: taskId is running by Thread.currentThread().getName());try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}});}// 关闭线程池singleThreadExecutor.shutdown();while (!singleThreadExecutor.isTerminated()) {// 等待所有任务完成}System.out.println(All tasks completed.);} }在上面的每个示例中我使用ThreadPoolExecutor构造函数显式地设置了线程池参数并根据每种线程池的特性和用途配置了合适的参数。例如在FixedThreadPool示例中核心线程数与最大线程数相同而在CachedThreadPool示例中核心线程数为0最大线程数为Integer.MAX_VALUE以适应动态任务需求。对于ScheduledThreadPool虽然它也有核心线程数和最大线程数的参数但通常我们使用ScheduledThreadPoolExecutor的构造函数来创建定时线程池而不是直接使用ThreadPoolExecutor。 线程中断 线程中断是Java多线程编程中的一个重要特性它允许一个线程请求另一个线程停止执行。当一个线程被中断时它会抛出InterruptedException这通常发生在阻塞操作中比如Thread.sleep(), Object.wait(), 或者LockSupport.park()等。 如何中断一个线程 要中断一个线程可以调用线程对象的interrupt()方法。这会设置线程的中断标志并且如果线程正在执行一个阻塞操作它会抛出InterruptedException。 检查线程中断状态 每个线程都有一个内部中断标志可以通过以下方法来检查或清除这个标志 Thread.interrupted(): 返回当前线程的中断状态并清除中断标志。Thread.isInterrupted(): 返回当前线程或给定线程的中断状态但不会清除中断标志。 示例代码 下面是一个简单的示例演示如何中断一个线程 public class InterruptExample {public static void main(String[] args) throws InterruptedException {Thread thread new Thread(() - {while (true) {if (Thread.currentThread().isInterrupted()) {System.out.println(Thread interrupted);return;}try {Thread.sleep(1000);System.out.println(Thread is running);} catch (InterruptedException e) {Thread.currentThread().interrupt(); // 重新设置中断标志System.out.println(Thread interrupted during sleep);return;}}});thread.start();Thread.sleep(5000); // 等待5秒后中断线程thread.interrupt();thread.join();System.out.println(Main thread finished.);} }在这个例子中主线程创建了一个新线程并启动它。新线程在无限循环中每秒打印一条消息并检查是否被中断。如果被中断它会退出循环并结束线程。主线程等待5秒后中断新线程并等待新线程结束。 注意事项 中断标志: 中断标志是一个线程级别的标志当线程被中断时这个标志被设置。当线程抛出InterruptedException时中断标志会被清除。因此在捕获InterruptedException后通常需要重新设置中断标志以保持中断状态的一致性。非阻塞操作: 如果线程在非阻塞操作中被中断它不会抛出InterruptedException。因此线程应定期检查它的中断状态。资源清理: 在线程中处理中断时不要忘记清理任何打开的资源或进行必要的清理操作。 守护线程 守护线程Daemon Threads是Java多线程编程中的一个重要概念。它们通常用于执行后台任务如垃圾收集、日志记录、心跳检测等这些任务对于程序的正常运行是辅助性的。当程序中的所有用户线程非守护线程都结束时守护线程会自动结束不需要显式地关闭它们。 守护线程的特点 自动结束当Java程序中没有非守护线程在运行时所有的守护线程都会自动结束即使它们仍在执行。辅助性守护线程通常用于执行后台任务这些任务不是程序的主要业务逻辑但对程序的运行是有益的。生命周期守护线程的生命周期与其他线程相同但它们的行为受到程序中其他线程的影响。 创建守护线程 要创建一个守护线程需要在调用Thread.start()方法之前通过调用Thread.setDaemon(true)方法将线程标记为守护线程。 示例代码 下面是一个简单的示例展示了如何创建一个守护线程 public class DaemonThreadExample {public static void main(String[] args) {Thread daemonThread new Thread(() - {while (true) {System.out.println(Daemon thread running...);try {Thread.sleep(1000);} catch (InterruptedException e) {Thread.currentThread().interrupt();break;}}});// 设置线程为守护线程daemonThread.setDaemon(true);// 启动守护线程daemonThread.start();// 主线程睡眠一段时间后结束try {Thread.sleep(5000);} catch (InterruptedException e) {Thread.currentThread().interrupt();}System.out.println(Main thread finished.);} }在这个示例中守护线程每隔一秒打印一条消息。主线程睡眠5秒后结束。由于守护线程是作为守护线程创建的所以当主线程结束时守护线程也会自动结束。 注意事项 守护线程的启动守护线程必须在调用start()方法之前设置为守护线程一旦线程开始运行就不能改变它的守护状态。资源释放如果守护线程持有资源如文件句柄、网络连接等则应在主线程结束前确保这些资源被妥善释放否则可能导致资源泄露。异常处理守护线程通常不应该抛出未捕获的异常因为这可能导致程序异常终止。因此最好在守护线程中捕获异常并妥善处理。 线程组 线程组Thread Group是Java多线程编程中的一个概念它用于组织和管理一组线程。线程组提供了一种将线程分组的方式使得可以对这些线程进行统一的管理和控制。线程组可以嵌套也就是说一个线程组可以包含其他的线程组形成层次结构。 线程组的作用 组织线程线程组提供了一种将线程按照功能或逻辑进行分类的方法。管理线程可以通过线程组来启动、挂起、恢复或终止线程。线程安全线程组提供了一种机制来限制哪些线程可以访问或控制其他线程。 创建线程组 要创建一个线程组可以使用ThreadGroup类的构造函数。通常线程组会在创建线程时指定。每个线程默认属于其创建者的线程组如果没有指定线程组则属于系统的默认线程组。 示例代码 下面是一个简单的示例展示了如何创建线程组和向其中添加线程 public class ThreadGroupExample {public static void main(String[] args) {// 创建线程组ThreadGroup group new ThreadGroup(MyGroup);// 创建线程并将其加入到线程组中Thread thread new Thread(group, () - {System.out.println(Hello from Thread.currentThread().getName());}, ThreadInGroup);// 启动线程thread.start();// 输出线程组的信息System.out.println(Thread Group Name: group.getName());System.out.println(Active Count: group.activeCount());// 等待线程结束try {thread.join();} catch (InterruptedException e) {Thread.currentThread().interrupt();}} }在这个示例中我们首先创建了一个名为MyGroup的线程组。接着创建了一个线程并将其加入到这个线程组中。然后启动了这个线程并输出了线程组的名称和活动线程的数量。 线程组的方法 ThreadGroup类提供了多种方法来管理和控制线程组内的线程 void destroy(): 销毁线程组及其所有子线程和子线程组仅当线程组中没有任何活动线程时才可用。int activeCount(): 返回线程组中当前活动线程的数量。void enumerate(Thread[] threads): 将线程组中当前活动的线程复制到数组中。void checkAccess(): 检查当前线程是否有权限访问该线程组。void stop(): 请求线程组中的所有线程停止执行不推荐使用因为这可能会导致资源泄露。void suspend(): 暂停线程组中的所有线程。void resume(): 恢复线程组中所有被暂停的线程。 注意事项 安全性线程组提供了一种安全机制只有创建线程组的线程才能访问和控制该线程组中的线程。这有助于保护线程不受未经授权的线程的干扰。资源管理线程组可以帮助管理线程的生命周期比如通过destroy()方法来销毁整个线程组这在某些情况下可能是有用的。局限性尽管线程组提供了一定程度的管理能力但在现代Java并发编程中线程池和ExecutorService等更高级的工具通常被视为更高效和更灵活的选择。线程组主要用于早期版本的Java现在更多的是作为一种历史遗留的概念。 线程本地存储 线程本地存储Thread Local Storage, TLS是Java多线程编程中的一个重要概念它提供了一种机制使得每个线程都可以拥有自己独立的变量副本。ThreadLocal类是Java标准库中用于实现这一特性的工具。 什么是线程本地存储 在多线程环境中多个线程可能会共享同一个对象或变量。当这些线程试图同时修改这些共享变量时就需要考虑同步问题以避免竞态条件和数据不一致。然而在某些情况下我们希望每个线程都能拥有自己的变量副本而不必担心线程之间的同步问题。这就是线程本地存储的目的。 ThreadLocal类的使用 ThreadLocal类提供了一种简单而有效的机制来实现线程本地存储。使用ThreadLocal类时每个线程都可以拥有一个与该线程绑定的变量副本。这些副本是相互独立的一个线程对它的副本所做的修改不会影响到其他线程。 创建ThreadLocal实例 创建ThreadLocal实例非常简单只需要创建一个ThreadLocal对象即可。你可以选择在构造函数中提供一个初始值或者在需要的时候再设置值。 示例代码 下面是一个简单的示例展示了如何使用ThreadLocal public class ThreadLocalExample {private static final ThreadLocalInteger threadLocal new ThreadLocalInteger() {Overrideprotected Integer initialValue() {return 0;}};public static void main(String[] args) {Thread thread1 new Thread(() - {threadLocal.set(100);System.out.println(Thread 1: threadLocal.get());});Thread thread2 new Thread(() - {threadLocal.set(200);System.out.println(Thread 2: threadLocal.get());});thread1.start();thread2.start();try {thread1.join();thread2.join();} catch (InterruptedException e) {Thread.currentThread().interrupt();}System.out.println(Main thread: threadLocal.get());} }在这个示例中我们定义了一个ThreadLocal变量threadLocal并为其提供了一个初始值0。接着我们在两个不同的线程中分别设置不同的值并打印出这些值。注意每个线程中的输出都是独立的不受其他线程的影响。 ThreadLocal类的方法 ThreadLocal类提供了以下主要方法 get(): 获取当前线程中变量的副本。set(T value): 设置当前线程中变量的副本。remove(): 移除当前线程中的变量副本。initialValue(): 可选方法返回当前线程中变量的初始值。 注意事项 内存泄漏当不再需要某个ThreadLocal变量时应该调用remove()方法来移除当前线程中的变量副本。否则即使线程结束了ThreadLocal变量的副本仍会被保留这可能导致内存泄漏。初始化默认情况下ThreadLocal变量的初始值为null。如果需要设置特定的初始值可以通过覆盖initialValue()方法来实现。性能考虑虽然ThreadLocal可以简化多线程编程但频繁地调用get()和set()方法可能会对性能产生影响尤其是当线程频繁创建和销毁时。因此在可能的情况下尽量减少ThreadLocal的使用频率。 总结 Java多线程技术是Java平台的核心特性之一它允许开发人员构建高度并发的应用程序充分利用现代多核处理器的硬件资源。多线程编程虽然强大但也带来了诸多挑战如竞态条件、死锁、资源竞争等问题。下面是对Java多线程技术的总结和完善 Java多线程的核心概念 并发性多个线程可以同时运行尽管在单核处理器上可以表现为交替执行。共享资源线程之间共享相同的内存空间这意味着它们可以访问和修改相同的变量但这也可能导致数据不一致的问题除非采取适当的同步措施。上下文切换操作系统在多个线程之间切换执行这称为上下文切换会带来一定的开销。独立性每个线程都有自己的执行流它们可以独立于其他线程执行但也可以通过线程间通信机制协作。 实现多线程的方式 继承Thread类创建Thread类的子类并重写run()方法。实现Runnable接口创建实现了Runnable接口的类并实现run()方法。使用Executor框架通过ExecutorService接口创建线程池来管理线程。使用Callable和Future接口创建实现了Callable接口的类可以返回结果并使用Future来获取结果。 线程间通信 共享内存多个线程共享相同的内存空间通过读写共享变量来通信。必须注意同步访问以防止竞态条件。信号量和条件变量信号量用于管理资源的访问权限。条件变量允许线程等待特定条件的满足。消息队列线程可以向队列发送消息其他线程可以从队列中读取消息。管道Pipes允许线程或进程之间通过管道进行通信。事件对象用于信号通知可以用来同步线程。 使用Object的wait()和notify()方法 这是Java中最基本的线程间通信方式之一。wait()方法使当前线程等待直到另一个线程调用notify()或notifyAll()方法。这些方法必须在同步块内调用通常在synchronized块或方法中。 使用Lock接口和Condition接口 Lock接口提供了更高级的锁定机制而Condition接口则提供了更灵活的线程等待和唤醒机制。这些接口位于java.util.concurrent.locks包中。 避免死锁 破坏互斥条件尽可能使资源可共享减少独占资源的需求避免使用锁。破坏占有并等待条件确保线程在开始执行之前获取所有必需的锁按顺序获取锁。破坏非抢占条件为锁请求添加超时机制使用ReentrantLock的tryLock()方法。破坏循环等待条件如果一个线程需要多个锁确保总是按照相同的顺序获取这些锁定期检查是否有可能出现死锁的情况。 线程池 固定线程池通过Executors.newFixedThreadPool()创建固定大小的线程池。缓存线程池通过Executors.newCachedThreadPool()创建可以缓存线程的线程池。单线程执行器通过Executors.newSingleThreadExecutor()创建只包含一个线程的线程池。定时线程池通过Executors.newScheduledThreadPool()创建可以安排任务的线程池。 线程中断 线程可以被中断以请求线程提前结束。线程可以通过调用Thread.interrupt()方法被中断。当一个线程被中断时它会抛出InterruptedException这通常发生在阻塞操作中。 守护线程 守护线程是那些在后台运行为其他线程服务的线程当所有非守护线程结束时守护线程自动结束。守护线程通常用于执行后台任务如垃圾收集、日志记录等。 线程组 线程组用于组织和管理一组线程。通过ThreadGroup类可以创建线程组并将线程加入到线程组中从而方便地管理和控制线程。 线程本地存储 ThreadLocal类提供了一个线程本地变量每个线程都有自己的副本。ThreadLocal可以用来存储线程特定的数据避免了线程之间的数据共享和同步问题。
http://www.dnsts.com.cn/news/4401.html

相关文章:

  • 做网站的软件图标重庆云阳网站建设公司推荐
  • 深圳住房建设局官方网站半月报网站建设商务代表工作总结
  • wordpress建站社区重庆森林讲了什么故事
  • 网站建设公司-信科网络专业制作网站推荐
  • wordpress新站不收录品质好的客户管理系统
  • 俄罗斯网站建设公司做搜狗网站优化排名
  • 重庆潼南网站建设价格广东省备案网站建设方案书
  • 如何做积分商城网站wordpress数据表前缀
  • 注册网站刀具与钢材经营范围网站代码软件
  • 绵阳做手机网站公司网站域名价格
  • 北京示范校建设网站中山做网站的公司
  • 网站制作网站做网山西省建设监理协会网-官方网站
  • 软件网站开发合同襄州区城乡建设局网站
  • 北京网站建设中企云达wordpress post 模板
  • 设计感很强的中文网站好看又免费的图片素材网站
  • 动易网站做值班表设计一个自己公司网站开发
  • 怎么修改网站标题关键词描述永久免费自助建站系统
  • 外包网站开发价格统一门户网站建设参考规范
  • 深圳平湖网站开发网站建设和网站运营包括什么
  • 电信电信网站备案系统网站建设维护课件ppt
  • 临猗商城网站建设平台网页设计技术论文
  • 网站开发要用什么语言网站制作宣传
  • 企业网站源码 可去版权网站网络排名优化方法
  • 专业做网站 台州玉环山东住房和建设厅网站首页
  • 企业网站如何做网警备案长春网站建设q479185700棒
  • 品牌网站制作公司濮阳头条新闻最新消息
  • 公司网站域名如何申请北京网站建设推荐华网天下
  • 东莞哪里能学建设网站兰州画册设计
  • 配送网站开发wordpress网站统计代码放哪个文件
  • 中国糕点网页设计网站批量网站建设合作协议