一、进程和线程的概念
进程:是指一个内存中运行的应用程序,每个进程都有自己独立的一块内存空间,一个进程中可以启动多个线程。
线程:是指进程中的一个执行流程,一个进程中可以运行多个线程。线程总是属于某个进程,进程中的多个线程共享进程的内存。
二、线程状态
一般来说,线程包括以下这几个状态:
创建(new)、就绪(runnable)、运行(running)、阻塞(blocked、time waiting、waiting)、消亡(dead)。
以下是每个状态的流转过程说明:
1. 创建到就绪阶段:首先创建一个线程,不会立刻进入就绪状态,而是需要先进行资源分配(内存、CPU等),待资源准备完成之后,就进入线程的就绪状态。
2. 就绪到运行阶段:当线程进入就绪状态之后,不一定会立即被执行,需要等待CPU的执行时间,如果此时CPU正在处理其他任务,需要等到CPU执行完其他任务之后,线程才能进入运行状态。
3. 运行到阻塞阶段,到再次进入就绪阶段:在线程运行过程中,可能由于某种原因导致线程执行不下去了,这时线程就进入阻塞状态。当不再满足阻塞的条件时,线程会再次进入就绪状态。具体分以下几种情况:
3.1. 如果是用户让线程主动等待(调用了wait方法),则线程进入waiting状态,此时如果等待被唤醒(调用了notify方法),线程会再次进入就绪状态;
3.2. 如果是用户让线程主动睡眠(调用了sleep方法),则线程进入time waiting状态,此时如果睡眠时间到了,线程会再次进入就绪状态;3.3. 如果是线程执行到同步代码块时被其他进程锁住,则线程进入blocked状态,此时如果同步代码块已经执行完毕,线程会再次进入就绪状态。
4. 运行到消亡阶段:如果线程突然被中断(调用了interrupt方法)或者任务执行完毕,线程就会进入消亡状态。
线程的几个状态流转示意图:
三、线程创建
创建线程有两种方式:
1. 继承Thread类,重写它的run()方法;
2. 实现Runnable接口,实现它的run()方法。
两种方式比较:实现Runnable接口相对于扩展Thread类来说,具有无可比拟的优势。这种方式不仅有利于程序的健壮性,使代码能够被多个线程共享,而且代码和数据资源相对独立,从而特别适合多个具有相同代码的线程去处理同一资源的情况。这样一来,线程、代码和数据资源三者有效分离,很好地体现了面向对象程序设计的思想。因此,几乎所有的多线程程序都是通过实现Runnable接口的方式来完成的。
四、线程启动
启动线程是通过调用Thread类的start方法来实现的。
在调用start()方法之后:发生了一系列复杂的事情:
1. 启动新的执行线程(具有新的调用栈);
2. 该线程从新状态转移到可运行状态;
3. 当该线程获得机会执行时,其目标run()方法将运行。
五、线程调度方式一:睡眠
线程休眠的方法是调用Thead的静态方法:Thread.sleep(long millis)和Thread.sleep(long millis, int nanos),那调用sleep休眠的哪个线程呢?简单说,哪个线程调用sleep,就休眠哪个线程。
六、线程调度方式二:让步
首先来看线程的优先级相关概念:
1. 线程的优先级:线程总是存在优先级,优先级范围在1~10之间(默认的优先级是5)。JVM线程调度程序是基于优先级的抢先调度机制。在大多数情况下,当前运行的线程优先级将大于或等于线程池中任何线程的优先级。但这仅仅是大多数情况。注意:当设计多线程应用程序的时候,一定不要依赖于线程的优先级。因为线程调度优先级操作是没有保障的,只能把线程优先级作用作为一种提高程序效率的方法,但是要保证程序不依赖这种操作。在一个线程中开启另外一个新线程,则新开线程称为该线程的子线程,子线程初始优先级与父线程相同。
2. 设置线程的优先级方法:线程默认的优先级是创建它的执行线程的优先级。
可以通过setPriority(int newPriority)更改线程的优先级。例如:
Thread t = new MyThread();
t.setPriority(8);
t.start();
Thread类中有三个常量,定义线程优先级范围:
static int MAX_PRIORITY 线程可以具有的最高优先级。
static int MIN_PRIORITY 线程可以具有的最低优先级。
static int NORM_PRIORITY 分配给线程的默认优先级。
3. 线程让步:线程的让步是通过静态方法Thread.yield()来实现的。
yield()方法的作用是:暂停当前正在执行的线程对象,并执行其他线程。
yield()应该做的是让当前运行线程回到可运行状态,以允许具有相同优先级的其他线程获得运行机会。因此,使用yield()的目的是让相同优先级的线程之间能适当的轮转执行。但是,实际中无法保证yield()达到让步目的,因为让步的线程还有可能被线程调度程序再次选中。
结论:yield()从未导致线程转到等待/睡眠/阻塞状态。在大多数情况下,yield()将导致线程从运行状态转到可运行状态,但有可能没有效果。
七、线程调度方式三:合并
Thread的非静态方法join()让一个线程B“加入”到另外一个线程A的尾部。在A执行完毕之前,B不能工作。
例如:
Thread t = new MyThread();
t.start();
t.join();
另外,join()方法还有带超时限制的重载版本。例如t.join(5000);则让线程等待5000毫秒,如果超过这个时间,则停止等待,变为可运行状态。
线程的加入join()对线程栈导致的结果是线程栈发生了变化,当然这些变化都是瞬时的。
八、线程同步
1. 线程同步的目的?
为了防止多个线程访问一个数据对象时,对数据造成的破坏。
2. 线程同步的方法?
首先把竞争访问的资源类Foo变量x标识为private;然后使用synchronized关键字同步方法或代码。
3. 对象加锁的概念?
加锁:当程序运行到synchronized同步方法或代码块时才该对象锁才起作用。一个对象只有一个锁。所以,如果一个线程获得该锁,就没有其他线程可以获得锁,直到第一个线程释放(或返回)锁。这也意味着任何其他线程都不能进入该对象上的synchronized方法或代码块,直到该锁被释放。
释放锁:指持锁线程退出了synchronized同步方法或代码块。
4. 关于锁和同步,有一下几个要点:
1)只能同步方法,而不能同步变量和类;
2)每个对象只有一个锁;当提到同步时,应该清楚在什么上同步?也就是说:在哪个对象上同步?
3)不必同步类中所有的方法,类可以同时拥有同步和非同步方法。
4)如果两个线程要执行一个类中的synchronized方法,并且两个线程使用相同的实例来调用方法,那么一次只能有一个线程能够执行方法,另一个需要等待,直到锁被释放。也就是说:如果一个线程在对象上获得一个锁,就没有任何其他线程可以进入(该对象的)类中的任何一个同步方法。
5)如果线程拥有同步和非同步方法,则非同步方法可以被多个线程自由访问而不受锁的限制。
6)线程睡眠时,它所持的任何锁都不会释放。
7)线程可以获得多个锁。比如,在一个对象的同步方法里面调用另外一个对象的同步方法,则获取了两个对象的同步锁。
8)同步损害并发性,应该尽可能缩小同步范围。同步不但可以同步整个方法,还可以同步方法中一部分代码块。
9)在使用同步代码块时候,应该指定在哪个对象上同步,也就是说要获取哪个对象的锁。
10)注意:在使用synchronized关键字时候,应该尽可能避免在synchronized方法或synchronized块中使用sleep或者yield方法,因为synchronized程序块占有着对象锁,你休息那么其他的线程只能一边等着你醒来执行完了才能执行。不但严重影响效率,也不合逻辑。同样,在同步程序块内调用yeild方法让出CPU资源也没有意义,因为你占用着锁,其他互斥线程还是无法访问同步程序块。当然与同步程序块无关的线程可以获得更多的执行时间。
5. 当考虑阻塞时,一定要注意哪个对象正被用于锁定:
1、调用同一个对象中非静态同步方法的线程将彼此阻塞。如果是不同对象,则每个线程有自己的对象的锁,线程间彼此互不干预。
2、调用同一个类中的静态同步方法的线程将彼此阻塞,它们都是锁定在相同的Class对象上。
3、静态同步方法和非静态同步方法将永远不会彼此阻塞,因为静态方法锁定在Class对象上,非静态方法锁定在该类的对象上。
4、对于同步代码块,要看清楚什么对象已经用于锁定(synchronized后面括号的内容)。在同一个对象上进行同步的线程将彼此阻塞,在不同对象上锁定的线程将永远不会彼此阻塞。
九、线程交互
void notify() :唤醒在此对象监视器上等待的单个线程。
void notifyAll():唤醒在此对象监视器上等待的所有线程。
void wait():导致当前的线程等待,直到其他线程调用此对象的 notify()方法或 notifyAll()方法。
当然,wait()还有另外两个重载方法:
void wait(long timeout):导致当前的线程等待,直到其他线程调用此对象的 notify()方法或 notifyAll()方法,或者超过指定的时间量。
void wait(long timeout, int nanos):导致当前的线程等待,直到其他线程调用此对象的 notify()方法或 notifyAll()方法,或者其他某个线程中断当前线程,或者已超过某个实际时间量。
当在对象上调用wait()方法时,执行该代码的线程立即放弃它在对象上的锁。然而调用notify()时,并不意味着这时线程会放弃其锁。如果线程荣然在完成同步代码,则线程在移出之前不会放弃锁。因此,只要调用notify()并不意味着这时该锁变得可用。
综上所述,上图的改进版如下图:
相关推荐
Java多线程是Java编程中的核心概念,它允许程序同时执行多个任务,从而提高应用程序的效率和响应性。本教程将深入讲解Java线程的相关知识,包括进程与线程的基本概念、线程的创建和启动、多线程的互斥与同步、线程...
Java多线程是Java编程中的一个重要领域,它允许程序同时执行多个任务,从而提高系统效率和资源利用率。在这个未完成的案例中,我们可能正在探讨如何在Java中创建和管理线程,以及处理多线程环境下的并发问题。下面是...
很好的JAVA多线程实例,方便初学都学习。
Java 多线程编程应用场景 —— 电影院售票系统设计 本资源摘要信息将对 Java 多线程编程在电影院售票系统设计中的应用进行详细介绍。该系统模拟了电影院三个售票窗口同时出售电影票的过程,通过 Java 多线程编程...
1. SpringBoot 自定义线程池以及多线程间的异步调用(@Async、@EnableAsync) 2.Java多线程之定时任务 以及 SpringBoot多线程实现定时任务 3.@EnableScheduling 与 @Scheduled
这个名为"Java多线程的小例子——吃包子"的示例,旨在帮助开发者直观地理解多线程的工作原理。下面我们将深入探讨该示例所涉及的核心知识点。 首先,多线程通常涉及到以下几个关键概念: 1. **线程(Thread)**:...
(注意,本资源附带书中源代码可供参考) 多线程与并发处理是程序设计好坏优劣的重要课题,本书通过浅显易懂的文字与实例来介绍Java线程相关的设计模式概念,并且通过实际的Java程序范例和 UML图示来一一解说,书中...
接下来我们将通过一个具体的例子——“厨师-食客”模式来深入理解多线程编程的实际应用。 1. **模型介绍**: - 生产者(厨师):负责制作食物,并将其放入共享的“仓库”(桌子)。 - 消费者(食客):从“仓库”...
通过这个项目,学习者可以掌握Java多线程编程的基本概念和实际应用,了解线程池的使用,以及如何在并发环境中处理文件I/O。此外,还将锻炼到问题解决能力,如处理网络不稳定、断点续传等常见问题。对于Java程序员来...
### Java多线程详解 #### 一、线程与进程的概念 在计算机科学中,**进程**是指一个正在运行的应用程序或程序实例,...以上是关于Java多线程的一些基础概念和技术细节,理解这些内容对于编写高质量的并发程序至关重要。
本知识点将深入探讨Java多线程设计以及如何利用“不可变对象”(immutable objects)来避免多线程环境中的非安全问题。 一、Java多线程基础 1. 线程的创建:Java提供了两种创建线程的方式——继承Thread类和实现...
本指南将深入探讨Java线程的基本概念、创建方式以及管理策略,旨在帮助初学者快速掌握线程的工作原理。 一、Java线程基础知识 在Java中,线程是程序中的单一顺序控制流程。一个进程可以包含多个线程,每个线程都有...
- **实现Callable接口并使用FutureTask**:这种方式不仅可以实现多线程,还可以获取线程执行的结果。 - **使用ExecutorService和ThreadPoolExecutor**:这是一种高级的线程池管理机制,可以有效地管理和控制线程的...
### JAVA多线程详解 #### 一、线程与进程的概念 在计算机科学中,**进程**是指一个正在运行的应用程序或程序实例,而**线程**则是进程中可并发...通过这些示例,我们可以更深入地理解Java多线程的相关概念和技术细节。
本文将深入探讨Java多线程的基础概念、基本原理及其在实践中的应用。 #### 二、核心概念 - **进程**: 是操作系统中的一个独立执行单元,拥有自己的内存空间。在多任务操作系统中,可以同时运行多个进程。 - **线程*...
### Java多线程的使用与介绍 #### 什么是多线程? 多线程是指程序能够同时执行多个线程的能力。每个线程都是一个轻量级的进程,可以在单个程序中独立运行,共享该程序的资源。在Java中,多线程是通过`java.lang....
在Java多线程编程中,线程安全是一个关键概念,特别是在并发访问共享资源时。"线程八锁案例分析"文档中的示例着重展示了`synchronized`关键字如何在控制线程同步方面发挥作用。以下是对这些案例的详细分析: 案例1...
这通常涉及到两种主要技术:多线程和图形用户界面(GUI)的实时更新。在Java中,我们可以使用`Thread`类或`Runnable`接口来创建和管理线程,而`Swing`或`JavaFX`库则可以用于构建GUI。 1. **线程创建与运行**: - ...
这个压缩包中的文件提供了几个关于Java多线程交互的实例,可以帮助我们深入理解如何在并发环境中控制线程的同步,确保数据的一致性和安全性。 首先,让我们讨论一下标题和描述中提到的关键概念——“多线程交互”和...
### Java多线程编程详解:深入理解与实践 #### 一、理解多线程机制 多线程,作为现代编程语言的重要特性之一,允许在单一应用程序内并发执行多个任务,从而极大提升了程序的效率和响应速度。在Java中,多线程的...