Java JUC高并发编程(一)

Java JUC高并发编程(一)_第1张图片

1.1 JUC 简介

在 Java 5.0 提供了 java.util.concurrent(简称JUC)包,在此包中增加了在并发编程中很常用的工具类,
用于定义类似于线程的自定义子系统,包括线程池,异步 IO 和轻量级任务框架;还提供了设计用于多线程上下文中
的 Collection 实现等;

1.2.1 进程

进程是资源(CPU、内存等)分配的基本单位,它是程序执行时的一个实例。
程序运行时系统就会创建一个进程,并为它分配资源,然后把该进程放入进程就绪队列。
进程调度器选中它的时候就会为它分配CPU时间,程序开始真正运行。
Java JUC高并发编程(一)_第2张图片

1.2.2线程

线程是程序执行时的最小单位,它是进程的一个执行流,是CPU调度和分派的基本单位。
一个进程可以由很多个线程组成,线程间共享进程的所有资源,每个线程有自己的堆栈和局部变量。
线程由CPU独立调度执行,在多CPU环境下就允许多个线程同时运行。同样多线程也可以实现并发操作,每个请求分配一个线程来处理。
Java JUC高并发编程(一)_第3张图片
线程和进程的了解其他链接

1.2.3线程的状态

线程在一定条件下,状态会发生变化。线程一共有以下几种状态:

1、新建状态(New): 新创建了一个线程对象。
2、就绪状态(Runnable): 线程对象创建后,其他线程调用了该对象的start()方法。该状态的线程位于“可运行线程池”中,变得可运行,只等待获取CPU的使用权,即在就绪状态的进程除CPU之外,其它的运行所需资源都已全部获得。
3、运行状态(Running): 就绪状态的线程获取了CPU,执行程序代码。
4、阻塞状态(Blocked): 阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。

阻塞的情况分三种:
①.等待阻塞:运行的线程执行wait()方法,该线程会释放占用的所有资源,JVM会把该线程放入“等待池”中。进入这个状态后,是不能自动唤醒的,必须依靠其他线程调用notify()或notifyAll()方法才能被唤醒,
②.同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入“锁池”中。
③.其他阻塞:运行的线程执行sleep()或join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时,或者I/O处理完毕时,线程重新转入就绪状态。

5、死亡状态(Dead): 线程执行完了或者因异常退出了run()方法,该线程结束生命周期。
总算把线程六种状态的转换说清楚了!

1.2.4 wait和sleep

由于sleep()方法是Thread类的方法,因此它不能改变对象的锁。所以当在一个Synchronized方法中调用sleep()时,线程虽然休眠了,但是对象的机锁没有被释放,其他线程仍然无法访问这个对象。而wait()方法则会在线程休眠的同时释放掉机锁,其他线程可以访问该对象。

wait()方法和notify()方法:当一个线程执行到wait()方法时(线程休眠且释放机锁),它就进入到一个和该对象相关的等待池中,同时失去了对象的机锁。当它被一个notify()方法唤醒时,等待池中的线程就被放到了锁池中。该线程从锁池中获得机锁,然后回到wait()前的中断现场。

yield()方法是停止当前线程,让同等优先权的线程运行。如果没有同等优先权的线程,那么Yield()方法将不会起作用。 join()方法使当前线程停下来等待,直至另一个调用join方法的线程终止。线程的在被激活后不一定马上就运行,而是进入到可运行线程的队列中。
sleep()
1、属于Thread类,表示让一个线程进入睡眠状态,等待一定的时间之后,自动醒来进入到可运行状态,不会马上进入运行状态
2、sleep方法没有释放锁
3、sleep必须捕获异常
4、sleep可以在 任何地方使用

wait()
1、属于Object,一旦一个对象调用了wait方法,必须要采用notify()和notifyAll()方法唤醒该进程
2、wait方法释放了锁
3、wait不需要捕获异常
4、wait,notify和notifyAll只能在同步控制方法或者同步控制块里面使用

sleep(1000)和wait(1000)的区别:
Thread.Sleep(1000) 意思是在未来的1000毫秒内本线程不参与CPU竞争,1000毫秒过去之后,这时候也许另外一个线程正在使用CPU,那么这时候操作系统是不会重新分配CPU的,直到那个线程挂起或结束,即使这个时候恰巧轮到操作系统进行CPU 分配,那么当前线程也不一定就是总优先级最高的那个,CPU还是可能被其他线程抢占去。
wait(1000)表示将锁释放1000毫秒,到时间后如果锁没有被其他线程占用,则再次得到锁,然后wait方法结束,执行后面的代码,如果锁被其他线程占用,则等待其他线程释放锁。注意,设置了超时时间的wait方法一旦过了超时时间,并不需要其他线程执行notify也能自动解除阻塞,但是如果没设置超时时间的wait方法必须等待其他线程执行notify。

1.2.5 并发和并行

并发(concurrency):指在同一时刻只能有一条指令执行,但多个进程指令被快速的轮换执行,使得在宏观上具有多个进程同时执行的效果,但在微观上并不是同时执行的,只是把时间分成若干段,使多个进程快速交替的执行。
Java JUC高并发编程(一)_第4张图片
并行(parallel):指在同一时刻,有多条指令在多个处理器上同时执行。所以无论从微观还是从宏观来看,二者都是一起执行的。
Java JUC高并发编程(一)_第5张图片
普通解释:
并发:交替做不同事情的能力
并行:同时做不同事情的能力
专业术语:
并发:不同的代码块交替执行
并行:不同的代码块同时执行

1.2.6 管程

Java是利用管程解决并发编程问题的,那么究竟什么是管程?而它又是如何解决并发问题的呢?
什么是管程
管程,英文名是 Monitor ,因此有的时候会被翻译为监视器。其实你也许很早就接触到这个概念了,比如 synchronized关键字,很多文章就介绍过其原理是使用了监视器,只是你那个时候还并不知道监视器和管程,其实是一回事。

管程 (英语:Monitors,也称为监视器) 是一种程序结构,结构内的多个子程序(对象或模块)形成的多个工作线程互斥访问共享资源。

管程提供了一种机制,线程可以临时放弃互斥访问,等待某些条件得到满足后,重新获得执行权恢复它的互斥访问。

1.2.7 用户线程和守护线程

在Java中有两类线程:User Thread(用户线程)、Daemon Thread(守护线程)
java线程分为用户线程和守护线程,线程的daemon属性为true表示是守护线程,false表示是用户线程。

守护进程(Daemon) 是运行在后台的一种特殊进程。它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。也就是说守护线程不依赖于终端,但是依赖于系统,与系统“同生共死”。那Java的守护线程是什么样子的呢。当JVM中所有的线程都是守护线程的时候,JVM就可以退出了;如果还有一个或以上的非守护线程则JVM不会退出。

守护线程 是一种特殊的线程,在后台默默地完成一些系统性的服务,比如垃圾回收线程、JIT线程都是守护线程。与之对应的是用户线程,用户线程可以理解为是系统的工作线程,它会完成这个程序需要完成的业务操作。如果用户线程全部结束了,意味着程序需要完成的业务操作已经结束了,系统可以退出了。所以当系统只剩下守护进程的时候,java虚拟机会自动退出。

用户线程和守护线程的区别:
 1.主线程结束后用户线程还会继续运行,JVM存活
 2.如果没有用户线程,都是守护线程,那么JVM结束(所有的线程都会结束)

Java用户线程和守护线程

你可能感兴趣的:(JUC,JUC)