进程、线程、锁
进程
一个计算机程序的运行实例,包含了需要执行的指令:
有自己独立的空间,包含程序内容和数据;
不同进程的地址空间是互相隔离的;
进程拥有各种资源和状态信息,包含打开的文件,子进程和信号处理;
进程使用类:java.lang.Process和java.lang.ProcessBuilder
线程
表示程序的执行流程,是CPU调度执行的基本单位;
线程有自己的程序计数器、寄存器、堆栈和帧。
同一个进程中的线程共用相同的地址空间,相同共享进程锁拥有内存和其他资源。
线程使用类:java.lang.Thread
进程状态
创建、就绪、运行、阻塞、终止。
线程主要有两种方式实现:
继承Thread、实现Runnable;其主要逻辑都是实现Run()函数,
Thread可以直接通过start启动线程;
Runnable就必须新建一个Thread对象进行执行start进行启动线程
wait() notify() notifyAll()方法进行使线程休眠或者唤醒。
注意:对象wait()将当前线程放入,该对象的等待池中,线程A对用了B对象的wait()方法,线程A进入B对象的等待池,并且释放对线程B的锁
(这里线程A必须只有B的锁,所以调用的代码必须在synchronized修饰下,否则直接抛出异常)线程重新执行需要外部唤醒。
sleep()是线程休眠一段时间后线程自动继续执行逻辑,休眠过程中不释放对象锁,线程监控状态一直存在;
java两种锁机制synchronized和Lock
synchronized是在JVM层面上实现的,synchronized在锁定时如果方法块抛出异常,JVM会自动将锁释放掉,不会因为出了异常没有
释放锁造成线程死锁。
注意:synchronized作用在方法上时,锁住的便是对象实例(this);
synchronize作用在静态方法锁住的便是对象对应的Class实例,因为Class数据存在于永久带, 因此静态方法锁相当于对该类的一个全局锁;
synchronized作用于某个对象实例时,锁住的便是对应的代码块;
Lock的锁是通过代码实现的,出现异常时必须在finally将锁释放掉,否则将会引起死锁。
一般使用ReentrantLock类作为锁,多个线程中必须要使用一个ReentrantLock类作为对象才能保证锁的生效。且在加锁和解锁处需要通过lock()
和unlock显示指出。所以一般会在finally块中写unlock以防死锁。
Condition 方法与wait、notify和notifyall方法类似,分别命名为await、signal、和signalAll
某个线程在等待一个锁的控制权的这段时间需要中断,
需要分开处理一些wait-notify,ReentrantLock里边的Condition应用、能够控制notify那个线程
具有公平锁功能,每个到来的线程都将排队等候。
线程安全
再多线程环境下,不管什么时候执行同一段代码得到的结果都是确定的,即线程安全;
线程池主要用ThreadPoolExcutor函数,通过定义线程池的核心池大小, 最大线程数,通过实现Runnable接口的
线程交由线程池执行。
常用函数说明
sleep(long millis):在指定的毫秒数内让当前正在执行的线程休眠
join():指等待线程终止。
join是Thread类的一个方法,启动线程后直接调用,即join的作用是:等待该线程终止,这里需要理解的就是该线程是指的
主线程等待子线程的终止。也就是在子线程调用了join方法后面的代码只有等到子线程结束了才能执行。
yield():暂停当前正在执行的线程对象,并执行其他线程。
使用yield的目的是让相同优先级的线程之间能适当的轮转执行。但是,实际中无法保证yield达到让步目的,
因为让步的线程还有可能被线程调度程序再次选中。
setPriority():更改线程的优先级
interrupt():中断每个线程,这种结束方式比较粗暴,如果线程打开了每个资源还没来得及关闭也就是run
方法还没有执行完就强制结束线程,会导致资源无法关闭
sleep和yield的区别
sleep()使当前线程进入停滞状态,所以执行sleep()的线程在指定的时间内肯定不会被执行;
yield只是使当前线程重新回到可执行状态,所以执行yield的线程有可能在进入到可执行状态后马上又被执行。