关于线程
线程与进程
进程:真正运行时的应用程序 (程序是静止的),由多个线程组成
而单核cpu在任何时间点上只能运行一个进程>>>>>宏观并行,微观串行
线程((轻量级进程)Light Weight Process):进程的执行单位,一个程序中的顺序执行流程
多线程:彼此间完成不同的工作,交替执行
并发与并行
并发:多个任务同时请求执行,但cpu只能处理一个,需要cpu轮转执行
多线程的并发原理:cpu分时间片,交替执行,宏观并行,微观串行
并行:多个任务同时请求执行,需要多核cpu的支持
多核cpu:宏观并行,微观并行
线程的基本组成部分(实现原理)
cpu时间片
运行数据:
堆空间:储存线程需要使用的对象,多个线程可共享堆中对象
栈空间:储存线程需要使用的局部变量,每个线程都拥有独立的栈
线程的逻辑代码:启动代码和执行代码
线程的实现方式
设置线程的名称:
1.通过构造方法设置线程的名称
2.通过setName方法
3.获取当前线程的对象Thread.currentThread()
继承类(继承Thread类):
实现一个线程类步骤:
1.自定义一个类 继承 Thread类
2.重写run方法
3.将这个线程要执行的代码写到run方法中
4.创建自定义类的对象
5.用自定义类的对象调用start方法开启线程
好处:代码相对简洁
弊端:Java是单继承的 如果一个类有父类就不能通过继承Thread类创建线程类
实现接口(Runnable接口):
Runnable 接口 只有一个run 没有抛出任何的异常 run方法没有返回值
创建线程类的步骤
1.自定义类 实现 Runnable 接口
2.实现接口中的run方法
3.将线程要做的事写在 run方法中
4.创建自定义类的对象(资源对象)
5.创建线程Thread t1 = new Thread(资源对象);
6.线程对象调用start方法启动线程
好处:Java是单继承的 但是一个类可以同时实现多个接口 不会影响Java的单继承性
弊端:代码稍微繁琐一点
匿名内部类创建线程的方式:
继承一个类 或者 实现一个接口
继承Thread类的匿名内部类实现方式:
语法:Thread 对象名=new Thread(){
public void run(){
}
};
实现接口的匿名内部类的实现方式
语法:
new Thread(new Runnable(){
public void run() {
}
}).start();
线程池:
Executor 父类
ExecutorService子类
Executors 工厂类
静态的方法 都可以通过类名调用
newFixedThreadPool(线程的条数)
newCachedThreadPool() 能够获取动态的线程池 根据任务提交的次数
步骤:
1.创建一个线程池
ExecutorService eService = Executors.newFixedThreadPool(固定数量);
ExecutorService eService = Executors.newCachedThreadPool();
2.创建资源对象
3.eService.submit(资源对象);
4.eService.shutdown();
Callable接口
jdk5 提供了 Callable接口
class 类名 implements Callable<数据类型>{
public 数据类型 call() throws Exception {
}
return 结果;
}
Future 对象的引用中存放着异步计算的结果 需要通过get方法区获取异步计算的结果
线程的状态
1. 初始状态(New状态)
2. 就绪状态(Ready状态)
3. 运行状态(Running状态)
4. 有限期状态(Timed Waiting状态)
5. 无限期状态(Waiting状态)
6. 阻塞状态(Blocked状态)
7. 终止状态(Terminated状态)
方法
sleep(毫秒):当一个正在执行的线程 调用sleep(毫秒数)后线程就进入到一 个限时等待状态 当毫秒数已过 就会重新成为就绪状态 等着抢夺 cpu资源
插队线程
join():当一个线程调用join方法后 会抢夺当前线程的cpu 等到自己执行完之后 才让出cpu
join(毫秒):当一个线程调用join(毫秒) 在毫秒的范围内 和join一样 一旦 超过了这个时间 就和之前一样 公平的抢夺cpu资源
setDaemon(布尔值): 当方法的参数为true 设置调用该方法的线程为守护线程
解释:当某个线程设置为了守护线程,这个线程在其他线 程执行结束后,不管它的任务有没有执行完,也随着结束
线程安全
临界资源:多个线程共同操作的资源对象 当多个线程对临界资源进行写操作的时候 如果线程不安全会出现数据错误的情况
原子操作:不可分割的一段代码 在线程运行这段代码的时候 不能被其他的线程打断
同步代码块:
synchronized(临界资源对象){
}
同步方法:
语法:
public synchronized 方法的返回值类型 方法名(){}
说明:在同步方法中的代码是一个不可分割的原子操作
jdk5提供一个接口 Lock 接口
Lock接口下边有 ReentrantLock实现类:
步骤:获得一个锁对象
锁对象.lock
原子操作的代码
锁对象.unlock
线程通信
sleep(毫秒值):进入到限时等待状态 但是没有释放锁标记
wait():进入到了阻塞状态 释放了锁标记 无限期阻塞 如果这个线程想进入到就绪状态 那么必须由其他的线程去唤醒notifyAll()/notify():在所有的阻塞的线程中唤醒全部/单个线程
这两个方法只能用在加锁的的同步代码块中 只有锁对象可以去调用
关于线程安全的集合
通过集合工具类Collections可以获得安全的集合:
public static <T> List<T> synchronizedList(List<T> list)
public static <T> Set<T> synchronizedSet(Set<T> s)
public static <K,V> Map<K,V> synchronizedMap(Map<K,V> m)
读写锁:ReadWriteLock,所在包java.util.concurrent.locks
① 概念:一种支持一写多读的同步锁,可分别分配读锁、写锁。
一个集合中 读的操作 查询size 通过get()获得元素 不改变元素的值
写操作 改变了集合中元素的值 (增加元素 插入元素 删除元素)