关于Java多线程的内容会记录在此,方便日后回顾
关于Java实现多线程的方式.
- 创建一个类继承类:Thread;重写,并将线程运行的逻辑放在run方法内
class ThreadDemo extends Thread{ public void run(){ // 运行逻辑 } } new ThreadDemo("ThreadName").start() - 创建一个类实现Runnable接口,通过Thread类启动
class ThreadDemo implements Runnable{ public void run(){ } } new Thread(new ThreadDemo(), "ThreadName").start() - 实现Runnable相对于继承Thread
-
通过实现Runnable可以同时实现其他多个接口
- Callable:可获取到线程返回值
ExecutorService pool = Executors.newCachedThreadPool(); Future<Integer> future = pool.submit(new CallableDemo()); Future<Integer> future = pool.submit(new Callable<Integer>() { @Override public Integer call() throws Exception { return 100; } }); System.out.println(future.get()); pool.shutdown(); - ExecutorService介绍: 是java对线程池的一个接口,有两个实现:ThreadPoolExecutor & ScheduledThreadPoolExecutor java提供了Executors工厂类,包括newCachedThreadPool newFixedThreadPool newScheduledThreadPool newSingleThreadExecutor,返回的都是ThreadPoolExecutor & ScheduledThreadPoolExecutor的实例 submit(Runnable) & execute(Runnable) 区别是前者可以返回一个Future对象,通过对象可知任务是否执行完毕 shutdown():如果的应用程序是通过main()方法启动的,在这个main()退出之后,如果应用程序中的ExecutorService没有关闭,这个应用将一直运行。之所以会出现这种情况,是因为ExecutorService中运行的线程会阻止JVM关闭
线程的生命周期
1、新建状态(New):新创建了一个线程对象。
2、就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法。该状态的线程位于可运行线程池中,变得可运行,等待获取CPU的使用权。
3、运行状态(Running):就绪状态的线程获取了CPU,执行程序代码。
4、阻塞状态(Blocked):阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。阻塞的情况分三种:
(一)、等待阻塞:运行的线程执行wait()方法,JVM会把该线程放入等待池中。(wait会释放持有的锁)
(二)、同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池中。
(三)、其他阻塞:运行的线程执行sleep()或join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。(注意,sleep是不会释放持有的锁)
5、死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。
Thread类中的join sleep yield
- join:使得一个线程在另一个线程结束后再执行
若正常主线程中通过new Thread().start()后,主线程会继续运行下去,直至运行结束
如果想等待子线程运行结束,主线程再继续运行,就要用到join
Thread t1 = new Thread(); t1.start(); t1.join(); -
sleep:正在执行的线程主动让出CPU(然后CPU就可以去执行其他任务),但是不释放同步锁
- yield:告诉当前正在执行的线程把运行机会交给线程池中拥有相同优先级的线程
同步锁
synchronized
- 可用于修饰代码块:
synchronized(obj){ // 其作用的范围是大括号{}括起来的代码,作用的对象是调用这个代码块的对象 // 同步的语句块 }当线程运行到该代码块内,就会拥有obj对象的对象锁,如果多个线程共享同一个Object对象,那么此时就会形成互斥 所以obj只要是类的成员变量,方法变量,或者是this关键字
- 可用于修饰方法
synchronized void method1(){} - 对象锁 - 可用于修饰类
synchronized public class Class1{} - 类锁 - 可用于修饰静态方法
synchronized static void method2(){} - 类锁 - 修饰方法 & 修饰代码块的区别
上图method1是同步代码块,根据monitorenter/monitorexit实现的
上图method2是同步方法,根据标识(ACC_SYNCHRONIZED)实现
无论哪种实现,本质上都是对指定对象相关联的monitor的获取,这个过程是互斥性的,也就是说同一时刻只有一个线程能够成功,其它失败的线程会被阻塞,并放入到同步队列中,进入BLOCKED状态。
wait、notify、notifyAll关系
- wait和sleep:如果当前线程是拥有锁的,那么Thread.sleep不会让线程释放锁,而wait会释放锁(重点)
- notify:如果同时有多个线程同步同一对象,且执行了wait,都已释放锁,此时notify会唤醒其中一个线程获取锁,走完流程,其余的线程依然处在阻塞状态
- notifyAll:如果同时有多个线程同步同一对象,且执行了wait,此时notifyAll会唤醒所有线程去争抢锁 注:调用notify/notifyAll后,只会唤醒,不会释放锁,还是需要待执行结束后才会释放锁.
