一、并发与并行
1.并行:指两个或多个事件在同一时刻发生(同时发生)。
2.并发:指两个或多个事件在同一时间段内发生。
二、线程与进程
1. 进程:是指一个内存中运行的应用程序,每个进程都有一个独立的内存空间,一个应用程序可以同时运行多个进程;进程也是程序的一次执行过程,是系统运行程序的基本单位;系统运行一个程序即是一个进程从创建、运行到消亡的过程。
2. 线程:进程内部的一个独立执行单元;一个进程可以同时并发的运行多个线程,可以理解为一个进程便相当于一个单 CPU 操作系统,而线程便是这个系统中运行的多个任务。
3.进程与线程的区别
进程:有独立的内存空间,进程中的数据存放空间(堆空间和栈空间)是独立的,至少有一个线程。
线程:堆空间是共享的,栈空间是独立的,线程消耗的资源比进程小的多。
三、创建线程两种方式
方式一、编写一个类继承Thread类
方式二、实现Runnable接口
四、线程安全
如果有多个线程在同时运行,而这些线程可能会同时运行这段代码。程序每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。
五、线程安全问题的产生(就是访问共享资源产生的!!)
线程安全问题都是由全局变量及静态变量引起的。若每个线程中对全局变量、静态变量只有读操作,而无写操作,一般来说,这个全局变量是线程安全的;若有多个线程同时执行写操作,一般都需要考虑线程同步,否则的话就可能影响线程安全。
六、解决方式
基本上所有的并发模式在解决线程安全问题的时候,都是采用序列化访问共享资源的方案(即同步)。这意味着在给定的时刻只允许一个任务访问共享资源。通常是通过锁机制来实现的。
七、锁机制实现方式
方式一、Synchronized 关键字
用法
一、修饰方法
Synchronized修饰一个方法很简单,就是在方法的前面加synchronized,synchronized修饰方法和修饰一个代码块类似,只是作用范围不一样,修饰代码块是大括号括起来的范围,而修饰方法范围是整个函数。
public synchronized void method(){ //todo }
注意:
同步锁是谁?
Ø 对于非static方法,同步锁就是this。
Ø 对于static方法,我们使用当前方法所在类的字节码对象(类名.class)。
不能加synchronized关键字的方法:
1.接口方法。
2.构造方法,但可以使用synchronized代码块来进行同步。
synchronized关键字不能继承
二、修饰一个代码块
指定当前对象为锁
指定某个对象为锁
指定某个类为锁,此时所有对象公用一把锁
Ø 一个线程访问一个对象中的synchronized(this)同步代码块时,其他试图访问该对象的线程将被阻塞
Ø 当一个线程访问对象的一个synchronized(this)同步代码块时,另一个线程仍然可以访问该对象中的非synchronized(this)同步代码块
三、修饰一个静态的方法
public synchronized static void mehtod(){ //todo }
静态方法是属于类的而不属于对象的。同样的,synchronized修饰的静态方法锁定的是这个类的所有对象。
给静态方法加synchronized关键字解决的是,对静态资源的并发访问的安全问题的。
总结:
A. 无论synchronized关键字加在方法上还是代码块上,如果它作用的对象是非静态的,则它取得的锁是对象;如果synchronized作用的对象是一个静态方法或一个类,则它取得的锁是对类,该类所有的对象同一把锁。
B. 每个对象只有一个锁(lock)与之相关联,谁拿到这个锁谁就可以运行它所控制的那段代码。
C. 实现同步是要很大的系统开销作为代价的,甚至可能造成死锁,所以尽量避免无谓的同步控制。
方式二、Lock锁
java.util.concurrent.locks.Lock 机制提供了比synchronized代码块和synchronized方法更广泛的锁定操作,
同步代码块/同步方法具有的功能Lock都有,除此之外更强大,更体现面向对象。
Lock锁也称同步锁,加锁与释放锁方法化了,如下:
Ø public void lock() :加同步锁。
Ø public void unlock() :释放同步锁。
线程状态
当线程被创建并启动以后,它既不是一启动就进入了执行状态,也不是一直处于执行状态。在线程的生命周期中,在API中 java.lang.Thread.State 这个枚举中给出了六种线程状态: