进程是程序运行和资源分配的基本单位,一个程序至少有一个进程,一个进程至少有一个线程。进程在执行过程中拥有独立的内存单元,而多个线程共享内存资源,减少切换次数,从而效率更高。
线程是进程的一个执行单元,是cpu调度和分派的基本单位,是比程序更小的能独立运行的基本单位。同一个进程中的多个线程之间可以并发执行。
多进程就是系统中运行的程序,每一个程序就是一个进程
多线程是同一个程序中有多个顺序流在执行
锁池
所有需要竞争同步锁的线程都会放在锁池当中,如果当前对象的锁已经被一个线程占用了,那么其他线程就会进入锁池中,等待这个对象锁的释放,释放后线程就会去竞争这个对象锁,竞争到的线程就会进入就绪队列,等待CPU资源分配。
等待池
当我们调用wait()方法后,线程就会放到等待池中,只有调用notify()或者notifyAll()后等待池的线程才会重新回归到就绪队列,等待CPU资源分配。
等待池中的线程是不会去竞争锁的。
notify()是随机从等待池中选出一个线程放回到锁池中,notifyAll()是将等待池中所有的线程放回到锁池。
堆
堆是线程和进程共有的空间,在进程初始化的时候就会分配好,如果需要也还可以向系统索要,最后归还给操作系统。堆可以分为全局堆和局部堆。
在java中,堆是java虚拟机所管理的内存中最大的一块,所有线程共享堆空间,堆中存放对象的实例,几乎所有的对象实例和数组都在这里分配。在虚拟机启动时创建。
栈
每个线程拥有一个栈空间,互相独立,每个线程都有自己的栈空间,切换线程时会自动切换栈,所以栈是线程安全的。
1、初始状态(NEW)
就是创建了一个线程(new)
2、就绪状态(RUNNABLE)
线程对象被创建后,其他线程调用该对象的start方法,线程位于可运行的线程池中,等待获取CPU的使用权。
3、运行状态
得到了CPU的使用权,执行代码
4、等待状态(WAITING)和超时等待状态(TIME_WAITING)
等待状态就是执行了wait()和join方法后程序进入等待状态
超时等待状态就是执行sleep()方法、等待状态也属于超时等待状态
5、阻塞状态(BLOCKED)
因为某种原因线程放弃了CPU的使用权,暂时停止运行,直到线程进入就绪状态,才有机会转到运行状态。
6、死亡状态(TERMINATED)
线程执行完或者因为异常退出了run方法,该线程生命周期结束
阻塞的情况有些什么:
1、等待阻塞:
1、sleep()
sleep()是Thread类中的静态方法,调用会进入睡眠状态,也就是阻塞状态,是不会释放对象锁的。其实sleep()就是线程回立即释放CPU资源,不在运行此线程,但是当定时结束后会立即取回CPU资源继续运行,所以对象锁也是不会释放的。
在sleep期间,其他线程调用了该线程的interrupt方法,线程就会抛出interruptexception异常返回。
2、wait()
wait()是Object类中的方法,执行后会让线程进入等待池中,没有notify或者notifyAll方法是不会将线程重新放回到就绪状态。
对比:
3、yield()
yield就是立即释放CPU资源,进入就绪状态,谦让一下,如果释放后还是该线程抢到了CPU资源,那么久继续执行。
4、join()
join就是让线程进入阻塞状态,例如:B线程中执行a.start()开始了A线程
,然后在B线程中又执行了a.join(),这是B线程进入阻塞状态,等待A线程执行结束或者中断线程。
应该说是内存安全,堆中的内存是共享的,所有线程都可以访问。
什么情况就说明线程是安全的:
就是多线程的去控制一个对象,最后的结果都应该是单线程是返回的结果。
如何去保证线程安全
1、继承Thread类创建线程
2、实现Tunnalbe接口创建线程
3、通过Callable和Future创建线程
4、通过线程池创建线程
守护线程是为所有非守护线程服务的线程,任何一个守护线程都是整个JVM中所有非守护线程的保姆。守护线程对于进程就是一个小喽喽,为所有线程服务,如果其他线程都没有了,守护线程也就没有必要再工作了,直接中断。非守护线程的中断要看是否可以中断,符合才可以中断。而守护线程的中断是无法控制的,不要将IO,File等重要的操作逻辑给它。
注意:
并发强调的是“发”,共同出发,并行强调的“行”共同执行
Erlang 之父 Joe Armstrong画了一张图解释并发和并行,并发指的时两队人交替使用咖啡机,并行指的两队人可以同时使用两台咖啡机。
并发其实是一种现象,实际就是操作系统会调度不同的线程来让cpu执行,每个线程执行一会就先停下来,然后切换到另一个线程执行,切换的很快所以就产生了并发的现象。而并行就是共同执行。