java线程
1.什么是进程?
只有运行的程序才会出现进程。
正在运行的程序,是系统进行资源分配和调度的独立单元
每一个进程都有自己的内存空间和系统资源
2.多进程的意义?
提高Cpu的使用率?
单进程的计算机只能做一件事情
多进程的计算机可以在一个时间段做多件事情(一边听歌,一边敲代码)
是cpu在高效切换让我们觉得同时在进行。
3.什么是线程?
在一个进程中可以执行多项任务,每一个任务就可以当做是一个线程。一个进程中一般会包含多个线程。
线程是程序的执行单元,也是程序使用cpu的最基本单位。
什么是多线程?单线程?
单线程:程序只有一条执行路径
多线程:程序有多条执行路径
4.多线程的意义? 抛绣球?
程序的执行都是在抢cpu的执行权。
多个进程都在抢,其中有一个进程的执行路径比较多(线程数比较多),抢到的cpu执行权的几率是更高(提高进程的使用率)。
绣球:CPU的执行权(CPU资源)
大汉:进程,一个大汉就是一个进程,其中有一个大汉有分身术,那这个大汉抢到绣球的几率更高。
5.自己写程序实现多线程?
继承类 :Thread
重写 run 方法
创建对象
启动线程 start()
6.java程序的运行原理:
Java命令启动虚拟机,jvm启动会启动一个主线程
接着主线程去调用main.你可以把main当做一个线程。
7.获取或设置线程的名字
setName(string) 设置
getName()获取
Thread.currentThread() 获取当前正在执行的线程
8.线程调度模型(两种)
分时调度:所有线程轮流使用CPU执行权
抢占式调度:多个线程之间互相抢占,
优先级高的抢占到的几率高。
Java 使用的抢占式调度。
9.设置/获取优先级
优先级范围1-10 ,值越大,优先级越高
默认是5
setPriority(num) 设置
getPriority() 获取
10.线程方法
start() 启动线程
void setName (String) 设置线程名字
String getName 获取名字
static void sleep(long) 休眠,参数是休眠的时间
Thread类的静态方法,能够让当前线程休眠一段时间,让出cpu执行权限,休眠时间到,进入就绪状态
static void yield() 线程礼让
礼让后回到就绪状态,但是有可能自己还会抢到CPU执行权,如果确保能够让出,使用sleep
join() 线程加入
t.join() 等待t线程执行结束后,再执行当前线程
setDaemon(boolean) 守护线程
主公(主线程)死了,忠臣(守护线程)也会自杀
当执行的线程都是守护线程时(没有非守护线程执行),所有守护线程会结束掉
线程中断
stop() 让线程停止/结束,方法已过时,不建议使用
interrupt() 中断线程,抛出异常
isInterrupted() 判断线程是否被中断
11.面试题:线程状态,生命周期?
12.实现多线程的第二种方式
1.自定义一个类,实现Runnable接口
2.重写run方法
3.创建自定义类的对象
4.创建Thread类的对象(把自定义类的对象作为参数传递给Thread的构造)
5. Thread类的对象.start()
13.有了第一种为什么还要第二种方式?
a.避免java单继承带来的局限性
b.适合多个相同程序的代码去处理同一个资源的情况,把线程程序的代码,数据有效分离。
14.火车票(电影票)售票问题
假设有电影票100张,有三个窗口在售卖。
出现问题:
重复:cpu执行的原子性
负数:延迟
解决?
同步机制synchronized
同步代码块
锁对象是任意对象
同步方法的锁对象是? This
静态同步方法的锁对象是? Class
什么是线程安全?
所谓的线程安全即不管多少个线程同时 执行,代码的运行结果总是与单个线程的执行结果是一致的。反之线程不安全。
线程的安全问题通常是由全局变量,静态变量造成。
线程不安全问题的解决:
加同步锁(互斥锁)synchronized
通过java的锁对象Lock,常用实现类ReentrantLock
lock() 获得锁
unlock() 释放锁
同步的弊端
效率低
死锁:两个或两个以上的线程在争夺资源的过程中,发生的一种相互等待的现象。
类似于:
中国人吃饭 :两根筷子
外国人吃饭:一个叉子,一把刀
中国人有:一根筷子,一把刀
外国人:一个叉子,一根筷子
死锁的产生原因:
线程间交叉调用
线程锁资源重叠,线程相互持有对方的锁资源
避免死锁
尽量避免同一线程同时持有多个锁。如果不可避免
尽量缩短锁的持有时间
只在必要的最短时间持有锁
线程间通信
wait() 进入阻塞状态,必须要通过notify唤醒
notify() 随机唤醒一个在wait中的线程
notifyAll() 唤醒所有在wait中的线程
生产者与消费者问题
产品类:产品名称,最大容量,当前数量
生产者:属性(产品)锁
当当前数量<最大容量 可以生产(最大容量-当前数量)
生产完成,唤醒消费者消费,停止生产
消费者:属性(产品)锁
如果购买数量(随机数=random*最大容量)> 当前数量
唤醒生产者,消费者等待
ELSE:直接购买
线程池
程序启动一个新线程成本是比较高的,因为它涉及到要与操作系统进行交互。而使用线程池可以提高性能。尤其是当程序创建很多线程的时候,要考虑使用线程池。 线程池特点:线程池中的线程可以重复使用,也就是说线程池的线程运行结束后,并不会死亡,而是再次回到线程池,等待下一个对象来使用。 Jdk5之前需要自己手动实现自己的线程池 Jdk5内置支持线程池。 线程池的分类: 可缓存线程池 newCachedThreadPool(); 定长线程池 newFixedThreadPool(int); 定长线程池,支持定时及周期性任务 newScheduledThreadPool(int) scheduleAtFixedRate(myRun,5,3, TimeUnit.SECONDS); scheduleAtFixedRate(任务,首次执行的延迟时间,周期执行间隔,时间单位) 要执行周期性的,调用子接口ScheduledExecutorService 的scheduleAtFixedRate方法 单个线程的线程池newSingleThreadExecutor() 创建线程池 ExecutorService executorService = Executors.newCachedThreadPool(); 通过Executors类的静态方法创建 返回的ExecutorService 对象 通过ExecutorService里的方法去执行线程 有以下方法: void execute(Runnable command)
sleep和wait区别
(1)sleep是Thread的方法。而wait是Object类的方法。
(2)调用两个方法都会进入阻塞状态,但是sleep不释放对象锁。
而wait会释放对象锁。
(3)wait必须要在持有对象锁的情况下调用,否则会抛出异常,
而sleep没有此限制。
(4)wait方法如果不带超时时间,则需要通过notify或notifyAll进行唤醒。
如果带超时时间,时间过了会自动唤醒
bug怎么这么多!