并发
- 并发就是指程序在同时处理多个任务的能力。
- 并发编程的根源在于对多任务情况下对访问资源的有效控制。
程序,进程与线程
- 程序是静态的概念,windows 下 通常指 exe 文件。
- 进程是动态的概念,是程序在运行状态,进程说明程序在内存中的边界。
- 线程是进程的一个 “基本任务”,每个线程都有自己的功能,是 cpu 分配与调度的基本单位。
并发与并行
- 并行:在多个 cup 上同时执行 多个线程。
- 并发:在单个cpu 上 执行 多个线程(在单核cpu 上,因为其执行线程速度快,所以每个线程执行的时间被称为cpu 的时间片)。
同步与异步
同步:在任务执行时,不能进行任务以外的操作(包括任务后续操作,只要任务未执行完成)。
异步:在任务执行时,可以进行任务以外的操作。
临界区
- 临界区用来表示一种公共资源与共享数据,可以被多个线程使用。
- 同一时间只能有一个线程访问临界区(阻塞状态),其他资源必须等待。
线程安全
- 在拥有共享数据的多条线程并行执行的程序中,线程安全的代码会通过同步机制保证各个线程都可以正常且正确的执行,不会出现数据污染等意外情况。
线程安全的特性
- 原子性
即一个操作或多个操作,要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行。
- 可见性
当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看到修改的值。
- 有序性
如果在本线程内观察,所有的操作都是有序的;如果在一个线程观察另一个线程,所有的操作都是无序的(可以使用 volaitile 进行重排序,JVM默认进行排序 )。
线程安全与线程不安全的区别
- 线程安全:
- 优点:可靠
- 缺点:执行速度快
- 使用建议:需要数据共享时使用
- 线程不安全:
- 优点:速度快
- 缺点:可能与预期不符
- 使用建议:在线程内部使用,无需线程间共享
-线程(不)安全的类:
- Vector 是线程安全的,ArrayList,LinkedList 是线程不安全的。
- Properties 是 线程安全的,HashSet ,TreeSet 是不安全的。
- StringBuffer 是线程安全的,StringBuild 是线程不安全的。
- HashTable 是线程安全的,HashMap 是线程不安全的。
Java 内存模型 (JMM Java Memory Model)
JVM Memory :
- 栈 Stack:
- 每个线程创建一个栈
- 存储执行方法的执行信息
- 线程私有化,无法共享
- 先进后出,后进先出
- 连续存储,执行效率高
- 堆 Heap:
- 用于存储对象
- JVM 全局唯一
- 堆是不连续的
- 执行效率低
- 所有线程共享
- 方法区(静态区) :
- 类结构信息
- 静态变量(static)
- 静态方法
- 存储内容是不变的
- 存储字符串
Java 创建线程的方式
- 继承 Thread 类 创建线程
- 实现 Runnable 接口创建线程
- 使用 Callable 和 Future 创建线程
JDk 1.5 以后,Java 专门提供了并发工具包 java.util.concurrent.
java.util.concurrent 包含许多线程安全,测试良好,高性能的并发构建块,创建concurrent 的目的是实现 Collection 框架对数据结构所执行的并发操作,通过提供一系列可靠的,高性能并发构建块,提高并发类的线程安全,可伸缩性,性能,可读性和可靠性。
创建线程方式对比
继承 Thread | 实现 Runnable | 利用线程池 | |
---|---|---|---|
优点 | 编程简单,执行效率高 | 面向接口编程,执行效率高 | 容器管理线程,允许返回值与异常 |
缺点 | 单继承,无法对线程组进行有效控制 | 无法对线程组进行有效控制,没有返回值,异常 | 执行效率相对较低,编程麻烦 |
使用场景 | 不推荐使用 | 简单多线程程序 | 企业级应用,推荐使用 |
继承 Thread
- 1.自定义 类 继承 Thread 类
- 2.重新 Run() 方法
- 3.创建自定义线程类对象,调用 start() 方法启动线程。
public class Demo1{
public static void main(String [] args){
MyThread t1=new MyThread();
MyThread t2=new MyThread();
t1.start();
t2.start();
}
}
// 自定义线程类
public class MyThread extends Thread{
@Override
public void run(){
// 线程要执行代码
}
}
实现 Runnable 接口的方式进行实现
- 1.自定义 类 实现 Runnable 接口
- 2.重新 实现 Run() 方法
- 3.创建自定义线程类对象,
- 4.创建Thread 类对象,调用 start() 方法启动线程。
public class Demo2{
public static void main(String [] args){
MyThread mt1=new MyThread();
Thread t1=new Thread(mt1)
Thread t2=new Thread(mt1)
t1.start();
t2.start();
}
}
// 自定义接口实现类
public class MyThread implements Runalbe {
@Override
public void run(){
// 线程要执行代码
}
}
Callable 实现
- 1.创建一个 类 实现 Callable 接口
- 2.重新 call() 方法 【call 具有返回值,表示多线程运行的结果】
- 3.创建 实现类 对象 【表示多线程要执行的任务】
- 4.创建 FutureTask 对象【作用管理多线程运行的结果】
- 5.创建 Thread 类 的对象 ,并启动【表示线程】
public class Demo3{
public static void main(String [] args){
MyThread mt1=new MyThread();
FutureTask<V> ft=new FutureTask<V>(mt1);
Thread t1=new Thread(ft);
t1.start();
V data=ft.get();
}
}
// 自定义接口实现类
public class MyThread implements Callable<V> {
@Override
public V call() throws Exception{
// 线程要执行代码
}
}
线程的成员方法
方法名称 | 说明 |
---|---|
String getName() | 返回当前线程的名 |