线程是操作系统中能够进行运算调度的最小单位,一个进程中最少拥有一个线程,进程中的任务都在线程中运行
如何创建线程:
- 继承 Thread 类,使用/重写 run() 方法
- 实现 Runnable 接口,重写 run() 方法
- 通过 Callable和 Future 创建线程
每次创建一个新的线程对象,他们都会有属于自己的 PC 寄存器,Java虚拟机栈,本地方法栈。堆和方法区则是所有线程共享
线程分为串行和并行两种。串行就是一条线程一条线程顺序执行;并行就是两条及两条以上线程同时运行。
在单核 CPU 中,一次只能运行一条线程,单核的线程并行实际上是 CPU 在快速切换运行线程以实现的伪并行
线程运行状态分为以下几种:
- NEW (创建线程对象)
- Runnable (正在运行状态)
- Bloackd (阻塞状态) //等待对象的内部锁
- Waitting (等待状态) //等待其他线程 以下方法会造成当前状态:
- 无超时值的 Thread.join() 方法
- 无超时值的 Obj.wait() 方法
- Time_Watting (定时等待状态) //有时间限制的等待其他线程
- Thread.sleep()
- 有超时值的 Thread.join() 方法
- 有超时值的 Obj.wait() 方法
- Date
线程安全问题:
当多线程同时访问一块内存并涉及到读写操作时,便有可能引发数据混乱,就可能会引发线程安全问题。如果需要解决线程安全问题一般使用同步技术
线程同步:一个线程在对一块内存进行操作时,其他线程都不可以对这块内存地址进行操作,直到该线程完成操作, 其他线程才能对这块内存地址进行操作。
同步方式: //Java的每个对象都有属于自己的内部锁
- 同步方法
-
- 简单来说就是被 synchronized 关键字修饰的方法,但是他不能修饰构造方法。修饰实例方法时,即锁住了整个方法,在调用该方法前,需要获取该对象的内置锁,不然就会处于堵塞状态。
- synchronized 也可以修饰静态方法,这样在调用时它就锁住了整个类,需要获取类对象的内置锁 //每个类都有属于自己的独一份的类对象
- 同步代码块
- 简单来说就是被 synchronized 修饰的代码块,它会单独锁住一块代码,因为同步是一种高开销的操作,所以通常没必要锁方法,锁部分代码即可
- 可重入锁
-
- 在JavaSE5.0中新增了一个java.util.concurrent包来支持同步。可重入锁(ReentrantLock)就是实现了 Lock 接口的锁,这个锁具有和同步方法/代码块一样的一些基本功能并将其扩展
- 基本方法 ReentrantLock() ReentrantLock.lock() ReentrantLock.tryLock() ReentrantLock.unlock() ReentrantLock.isLocked 分别是创建对象,获取锁,尝试获取锁,释放锁,查看锁是否被持有
线程通信
一条线程的运行需要另一条线程的参与,这样叫做线程之间的通信
线程之间的通信可以使用 wait,notify / notifyAll 实现
实现过程:
对象 A 调用对象 O 的 wait 方法进入等待状态,线程 B 调用对象 O 的 notify / notifyAll 方法通知对象 A 可以运行,对象 A 收到通知后退出等待状态进入可运行状态执行后续代码
- 在执行 wait 方法前必须获取对象的内置锁,执行方法后会使当前线程进入堵塞状态并释放当前锁。
- 执行 notify / All 方法后,当前线程不会马上释放对象锁,而是需要 notify / All 所在的线程将程序执行完毕(退出 synchronized 代码块/方法),当前线程才会释放锁。
死锁
两条及两条以上线程互相堵塞的现象,称为死锁。死锁的产生有四个条件:
- 互斥:一段时间内一块资源仅为一个进程所有
- 不剥夺:一块资源未使用完毕不能被其他线程强制夺走
- 请求和保持:进程提出获取新资源,但是新资源被其他进程所有,而自己已获得的资源也保持不放
- 循环等待:存在一个循环闭环,链中每一个进程所获取的资源为下一个进程所请求
破环死锁:
互斥条件不可破坏,不剥夺条件破坏代价大
只需允许进程使用完一块资源便释放掉就可破坏死锁。另一种方法就是破坏循环链,对各进程访问资源的顺序做一个规定即可
线程池
由于常规线程执行完任务后就会结束生命周期,而频繁创建关闭线程会造成大量内存开销,为了优化这种情况,便引入了线程池的概念
线程池由工作线程组成,工作线程与普通线程不同,工作线程可以执行多个任务,如果一条工作线程被分配到多个任务,那么它会将这多个任务分配到队列中,然后顺序执行
JDK5 提供了一个 Executors 工厂类来生产线程池,方法如下(个别列举)
public static ExecutorService newFixedThreadPool(int nThreads)
public static ExecutorService newSingleThreadExecutor()
public static ExecutorService newCachedThreadPool()
可以通过以上方法创建线程池对象,控制创建几个线程对象。可以控制 Runnable / Callable 对象代表的线程
<T> Future<T> submit(Callable<T> task); //创建线程对象
<T> Future<T> submit(Runnable task, T result); //创建线程对象
Future<?> submit(Runnable task); //创建线程对象
void shutdown(); //结束线程池