一、进程
- 进程:操作系统(OS)中正在执行的应用程序。
允许多个进程同时工作 - 多进程并发执行原理:
宏观上并行(一起执行)
微观上串行(一个一个执行),获取CPU时间片的进程就会执行
二、线程
-
概念:在一个进程中,可以同时执行多个任务,每一个任务可以说是为一个线程,线程是进程的工作单位。线程也被称为轻量级的进程。
-
目前的程序是单线程:以main函数为开始,以main函数的结束为结束,这个线程为主线程。
-
线程的组成部分:
(1)获取cpu时间片
(2)栈空间独立,堆空间共享。
每一个线程都有自己独有的栈空间
所有线程共享同一堆空间
(3)程序代码:利用Java代码实现多线程。 -
代码实现多线程的方式:【重点】
(1)第一种方式: Thread类
a. 类 继承Thread类,同时覆盖 public void run( ) ,
b. 创建线程
c. 开启线程-> 利用start( )开启,jvm自动调用run()开启指定线程
public class TestThread {
static{
//创建线程
MyThread mt = new MyThread();
//开启线程
mt.start();
}
public static void main(String[] args) {
for (int i = 0; i < 15; i++) {
System.out.println("+++++++ main +++++++"+i);
}
}
}
class MyThread extends Thread{
public void run(){
for (int i = 0; i < 10; i++) {
System.out.println("------- run --------"+i);
}
}
}
结果1: 结果2:
(2)第二种方式:Runnable接口
a. 类实现 java.lang.Runnable接口,同时实现run(),即 public void run ( ) { }
b. 创建目标对象
c. 创建线程对象 Thread t = new Thread(Runnable target);
d. 开启线程 t.start();
public class Class_2_Runnable {
static{
//创建目标对象
Runnable r = new MyTarget();
//创建线程对象
Thread t = new Thread(r);
//开启线程
t.start();
}
public static void main(String[] args) {
for (int i = 0; i < 15; i++) {
System.out.println(".........main........."+i);
}
}
}
class MyTarget implements Runnable{
public void run(){
for (int i = 0; i < 10; i++) {
System.out.println("*******Runnable*******+i");
}
}
}
结果1: 结果2:
(3)线程池:
①线程池:线程容器,将预先创建的线程对象存入到线程池中,只要将任务提交给线程池,会分配线程对象完成提交任务,线程池中的线程对象可重复使用。
②作用:避免频繁的创建和销毁线程,从而提高空间利用率呵执行效率。
③线程池常用的接口和类:(位于java.util.concurrent包中)
a. Executor:线程池的顶级接口
b. ExecutorService:是Executor的子接口,线程池的核心接口
Ⅰ. submit(Runnable task):将线程任务提交给线程池
submit(Callable<\T> task):将线程任务提交给线程池
Ⅱ. shutdown():关闭线程池,将线程池中所有线程销毁(慎用)。
c.Executors:获取线程池对象的工具类,其中方法基本都是静态方法
Ⅰ. static ExecutorService newFixedThreadPool(int n):获取n个线程的线程池。
Ⅱ. static ExecutorService newCachedThreadPool():获取动态数量线程对象的线程池,根据提交的任务需求,不够用自动完成线程创建。
(4)第四种方式:Callable接口
a.位于 java.util.concurrent包中,类似于Runnable接口,对应的对象代表线程任务。注意Callable是泛型接口,泛型约束了接口中方法的返回值类型
b.接口中的方法:V call();带有返回值的方法,同时可以抛出异常
Future< T> f= pool.submit(c1);,使用 f.get() 从Future中获得所需的返回值
三、线程状态
-
New状态 --> Runnable(Ready、Running) --> Terminated终止状态
-
线程状态 - 带有时间的等待状态
a. static void sleep (long ms) : 如果在某一个线程中调用Thread.sleep() ,则此方法进入有期限等待状态,同时释放cpu时间片
b. public final join():允许其他线程加入当前线程中并且优先执行,其结束后本身线程再执行
四、线程同步
多线程共同访问同一个对象,如果破坏了不可分割的操作,就可能导致数据不一致
临界资源:被多线程共同访问的对象
原子操作:不可分割的操作,要么都执行,要么都不执行
- 同步代码块
synchronized (obj) {} 对obj对象加锁的同步代码块
- Java中,每个对象都有一个互斥锁标记(monitor),用来分配给一个线程
- 只有拿到对象obj锁标记的线程,才能进入对obj加锁的同步代码块。
- 线程离开同步代码块时释放obj的锁标记
线程安全的对象:一个对象成为临界资源时,被多个线程访问,但其原子操作不会被破坏。
- 同步方法
public class A{
public synchronized void m(){}
}
只有获得对象A的锁标记线程,才能调用A的同步方法
调用之前,竞争A的锁标记
方法调用结束后,会释放A的锁标记
- Lock
a. Lock:接口,位于java.util.concurrent.locks包中,代表锁。
b. Lock中常用方法
(1)void lock() : 获取锁,如果被占用,则需要等待
(2)void unlock() : 释放锁
最好写在对应的try-finally中
(3)实现类:ReentrantLock
五、死锁
- 死锁现象:两个线程或是多个线程相互占用对方所需资源,并都不释放,导致彼此相互等待对方释放资源,产生无限制等待的现象。
- 结果:出现死锁后,不会抛出异常,也没有任何提示,只是所有的线程都处于堵塞状态,如果没有外力介入,程序将无法继续。
- 解决方案:
(1)尽可能调整加锁的方案
(2)尽量避免同步的嵌套
(3)采用线程中的通信
在Object类中游 wait() 和 notify()/notifyAll()
a. wait():
①让当前进程进入等待状态,释放cpu的同时,释放拥有的锁标记。
②wait方法调用必须使用在调用它的对象所在的同步代码块中
如 synchronized(o){
o.wait();
}
b. notify():
①notify(): 通知一个线程从等待状态结束
notifyAll(): 通知所有线程从等待状态结束
②notify()/notifyAll()的调用必须在调用它的对象所在的同步代码块中,同wait②
③notify()和notifyAll() 只是其到通知的作用,不会释放锁标记
五、线程安全的集合 JDK5
解释:【面试题】
ArrayList:数组实现 JDK1.2 线程不安全 并发效率高
Vector: 数组实现 JDK1.0 线程安全 并发效率低
- ArrayList :所有方法都不是同步方法,当其成为临界资源时,可能会造成数据不一致,则线程不安全
- Vector :所有方法都是同步方法,成为临界资源时,不会造成数据不一致,则线程安全,并发效率很低
线程安全的集合类:既保证线程安全,有保证并发效率很高 【面试】
-
CopyOnWriteArrayList
(1)读操作不加锁,写操作加锁,并且在进行写操作时会copy一个旧集合,在新集合中写数据。
(2)应用场景:读操作次数远大于写操作次数。效率仅次于ArrayList -
CopyOnWriteArraySet 原理和CopyOnWriteArrayList一致
-
ConcurrentHashMap
(1)JDK8以前,采用分段锁,控制锁的粒度,把Map分为16个片段,针对每个片段加锁。
(2)JDK8以后,采用 CAS算法(比较交换算法),无锁算法,效率非常高,仅次于HashMap
-
Queue ‘<'E> 接口
(1)Queue是Collection的子接口,描述队列存储结构。简称FIFO
(2)实现类 LinkedList
(3)特点:先进先出,后进后出。
(4)常用方法:boolean offer(Object o) 往队列中添加元素
E poll() ,获取队列头元素 -
BlockingQueue :
(1)是Queue的子接口,位于java.util.concurrent包中
(2)常用方法:put(Object o)往队列结构中存储元素,如果没有可用空间,则等待
take():从队列结构中取元素,没有可取元素,则等待
(3)常用实现类
ArrayBlockingQueue:数组实现,指定固定容量,有界队列
LinkedBlockingQueue:链表实现,可以实现无界队列 -
ConcurrentLinkedQueue’<'E>
(1)是Queue接口的实现类,位于java.util.concurrent包中(juc)
(2)特点:无锁并且线程安全的集合实现类,采用CAS(比较交换算法)