一、线程与进程的概念
进程就是计算机中的程序关于某数据集合的一次运行活动,是系统进行资源分配和调度的单位。我都理解就是一个程序的运行,可以打开自己的任务管理器查看运行的进程。
线程就是轻量级的进程,是程序执行的最小单位。
一个进程包含若干个线程。使用多线程而不是多进程去进行并发程序设计是因为线程之间切换和调度的成本远远小于进程。
二、学习并发的五个概念
1.同步(Synchronous)与异步(Asynchronous)
同步和异步通常都是形容一次方法的调用。同步方法调用一旦开始,调用者必须等到方法调用返回后才能开始后续行为;异步方法调用更像一个消息传递,方法调用后立即返回,调用者可以继续执行后续的行为。
举个栗子买个衣服,自己去商城挑货,下单款然后带回家,这就是同步调用,而异步调用就是你通过淘宝下单付款,之后的任务就与你无关,你只需要签收就是。
2.并发(Concurrency)与并行(Parallelism)
并行是多个任务同时执行,比如多个cpu或多台电脑同时执行某个任务;而并发是多个任务连续执行,任务之间不断切换。我们常指的多线程就是并发设计。
3.临界区
临界区用来表示一种公共资源或者说是一种共享数据,可以被多个线程使用,但每次只能有一个线程能使用它,一但该资源被占用,其他线程想使用这个资源就必须等待。如我们需要对一个对象的操作进行加锁,那么加锁的这段代码就是临界区,而加锁的对象称为临界资源。
public class criticalRegion extends Thread{
private static Object object = new Object();
public void run(){
synchronized (object) {
System.out.println("临界区调用");
}
}
}
4.阻塞(Blocking)和非阻塞(Non-bolcing)
阻塞和非阻塞通常用来形容多线程间的相互影响。比如一个线程占领了临界区资源,那么其他线程要想使用这个资源就必须等待,等待会造成线程的挂起,这种情况就是阻塞;非阻塞与之相反,它强调没有一个线程可以妨碍其它线程,所有线程都会尝试不断前行。锁和无锁就是阻塞和非诸塞操作,后文会详细阐述。
5.死锁(Deadlock)、饥饿和活锁
死锁每个线程都在等待其他线程释放需要操作的资源,很经典的一个死锁案例就是哲学家就餐问题,后文会详细分析,发生死锁过后我们会发现程序一直在运行但不会停止,但cpu占用为0,程序无任何输出。我们可以通过jstack查看,打开cmd用jps查看java进程id,再用jstack输出该线程的信息。
饥饿表示一个或多个线程由于某种原因无法获取它所需要的资源,导致一直无法运行,不如线程优先级太低,高优先级的线程不断抢占它所需要的资源。饥饿在未来一段时间内可能解决。
活锁表示多个线程都在谦让资源而没有一个线程拿到资源正常执行。
三、多线程的特性
JMM(java内存模型)很多关键技术就是围绕多线程的原子性、可见性和有序性展开的。
1.原子性(Atomicity)
原子性指一个操作不可中断。即使是多个线程在一起执行,一个操作一旦开始就不会被其他线程干扰。
2.可见性(Visibility)
一个线程修改了某一个共享变量之后,其他线程能否立即知道这个修改。
3.有序性(Ordering)
一个程序的执行不会发生乱序,程序给我们的感觉是根据代码的执行从前往后,依次执行。但在并发中,程序可能会发生乱序,这与指令重排有关。
-----------------------------------------------------------------------------------------
最后推荐几本学习多线程的书籍
1.实战java高并发程序设计
本书主要讲解Java的并行程序设计基础、思路、方法和实战,本篇文章就是从中总结的。
2.Java并发编程实战
对Java方方面面的理论知识体系都掌握得是非常清楚和透彻的,本书更多的是关于概念的讲解。
java多线程编程核心技术
本书关于线程安全、synchronized、Reentrant、Timer等等都用详细的代码进行了讲解,而且每个大知识点下的多个小知识点都会详细讲解到,非常有实践价值。