Java多线程
Java多线程
进程:正在运行的程序或者是程序的一次执行过程,是动态的过程
线程:是程序中的一条执行路劲,也被称为轻量级进程
多线程:是指操作系统能同时执行多个线程
并行:多核CPU同时执行多个任务,例如 ,多个人同时做不同的事
并发:单核CPU同时执行多个任务,例如,多个人同时做同件事,秒杀系统
线程有五种状态
创建(准备)状态:当创建完Thread对象时(Thread thread = new Thread() ),线程便进入了创建状态
就绪状态:当调用start()方法后便立即进入就绪状态,但不意味着立刻调度执行
运行状态:CPU调用,获得资源,就会进入运行状态,执行线程体里的代码块
阻塞状态:当调用sleep(),wait() 或同步锁定时,线程便进入阻塞状态,解除阻塞事件后便又进入就绪状态,等待CPU调度执行
死亡状态:线程中断或线程执行结束便进入死亡状态,就不能再执行,需要重新启动
每个线程都有一个优先级,这样有助于操作系统确定线程的调度顺序,Java线程优先级在Min_priority(1)到Max_priority(10)之间的范围内。默认优先级是norm_priority(5)
实现线程的方式有
1.继承Thread类:1.自定义线程类继承Thread类,2.重写run()方法编写线程执行体 3.创建线程对象,调用start()方法启动线程
使用继承Thread类的方式创建多线程时。编写简单,如果需要访问当前线程,则无需使用currentThread()方法,直接使用this关键字即可获得当前线程,
2.实现Runnable接口(优先选择): 1.自定义线程类实现Runnable接口,2.重写run()方法,编写线程执行体 3.创建线程对象,调用start()方法启动对象
a.避免单继承的局限性 b.Thread类也实现Runnable接口,实现该接口还可以继承其它类
3.实现Callable接口:1.自定义实现Callable接口,需要返回值类型,2.重写call()方法,需要抛出异常,3.创建目标对象
使用FutureTask类来包装Callable对象,该FutureTask对象封装了该Callable对象的call()方法的返回值;
使用FutureTask对象作为Thread对象的target创建并启动新线程,调用FutureTask对象的get()方法来获得子线程执行结束后的返回值。
a.避免单继承的局限性 b.实现该接口还可以继承其它类
线程池:由于线程频繁的创建和销毁,会浪费大量资源,所以创建线程池可以节约资源
实现Runnable接口创建线程池
1.创建线程池对象
2.创建Runnable子类对象
3.提交Runnable子类对象
4.关闭线程池
实现Callable接口创建线程池
1.创建线程池对象
2.创建Callable接口子类对象
3.提交Callable接口子类对象
4.关闭线程池
多线程来的问题==>死锁
死锁产生的条件
1.请求和保持条件:一个线程因请求被占用资源而发生堵塞时,对已获得的资源保持不放
2.互斥条件:排斥性,当线程获得所分配的资源后,所分配的资源不能被其它线程占用
3.循环等待条件:发生死锁时,所等待的线程必定形成一个环路,死循环会造成永久阻塞。
4.不可剥夺条件:在一个线程未使用完已获得的资源之前,不能被其他线程强行剥夺,只有等待该线程使用完后并释放才能被其他线程获取。
如何避免死锁
1.破坏请求和保持条件:一次性申请所有的资源
2.破坏互斥条件:无法破坏,我们的锁本身就是用来让线程产生互斥
3.破坏循环等待条件:按照次序来申请资源
4.破坏不可剥夺条件:占有资源的线程可以尝试申请其它资源,如果申请不到,可以主动释放它占有的资源。
sleep()方法:使正在运行的线程处于(阻塞)睡眠状态,是属于Thread类一个静态方法,并且不考虑线程的优先级不会释放锁,会自动苏醒;
yield()方法:使线程处于就绪状态,
sleep()方法和yeild()方法的区别 没有声明任何异常,
wait()方法:使线程处于等待转台,并且会释放所持有对象的锁,是属于Object类的方法,不会自动苏醒,需要被唤醒;
notify()方法:可以唤醒一个处于等待状态的线程,又JVM决定唤醒哪个线程,与优先级无关;
notifyAll()方法:可唤醒所有处于等待状态的线程,并不是将对象的锁直接让给所有的线程,而是让它们竞争,只有获得锁的线程才能进入就绪状态。