1.多线程回顾

​ 一、多线程回顾

  1. Thread 和 Runnable

    线程的5个状态:

    (1)新建

    (2)就绪

    (3)运行

​ (4)阻塞

​ (5)运行

​ Thread类中枚举类States的状态有:NEW、RUNNABLE、BLOCKED、WAITING、TIME_WAITING、TERMINATED。

​ Thread一些常见方法:

​ (1)setProprity(),getProprity() 设置优先级

​ (2)setDaemon() 设置守护线程

​ (3)interrupt() 当线程处于等待状态时,唤醒

​ (4)interrupted() 查看线程有没有被中断,并清除状态

​ (5)isInterrupt() 查看线程中断有没有被中断,不清除状态

​ (6)join() 当前调用此方法的线程,等待join进去的线程死亡,才会继续执行

​ (7)currentThread() 获取当前线程

​ (8) sleep() 将线程的执行暂停一定时间

​ (9)setUncaughtExceptionHandler(), 当线程执行出现未校验异常时,该方法用于建立未校验异常的控制器。

创建线程:

​ Callable 和 Runnable的区别

​ Runnable执行方法没有返回值,Callable可以返回线程执行结果。

Callable使用方法:

​ (1)

​ FutureTask futureTask=new FutureTask(new Callable(){

​ public Object call() throws Exception{

​ reutrn null;

​ }

​ });

​ new Thread(futueTask).start();

​ Object o= futureTask.get(); // 同步等待结果

​ (2)线程池

​ ThreadPoolExecutor executor=new ThreadPoolExecutor(5,10,10,TimeUnit.SECONDS,new ArrayBlockingQueue<>(100)){

​ @Override

​ public void afterExecute(Runnable r, Throwable t){

​ // run执行完后执行

​ }

};

Future<String>  future=executor.sumit(new Callable{......});

​ future.get(); // 同步等待结果

​ executor.shutdown();

2.synchronized

​ 该关键字给某个对象加锁

锁是一个对象,作用如下

(1)这个对象内部有个标志位,表示该对象有没有上锁,并能记录获取锁的thread id

(2) 没有获得锁的线程放入list中,锁被释放,从list唤醒一个thread。

要访问的共享资源本身也是一个对象,例如下面的MyTest,这两个对象可以合成一个对象,代码就变成了synchronized(this){},要访问的共享资源是对象a,锁加在对象a上。当然也可以创建一个对象,代码变成synchronized(obj1){},这个时候,访问的共享资源是对象a,而锁加在新建对象obj1上。

资源和锁的合二为一,使得在java里面,synchronized关键字可以加在任何对象的成员上。这意味着,这个对象既是共享资源,同时也具备锁的功能。

示例代码:

​ public class MyTest{

​ publilc synchronize void test(){

​ }

​ public static synchronized void test2(){

​ }

​ }

​ 等价于

​ public class MyTest{

​ public void test(){

​ synchronied(this){

​ }

​ }

​ public static void test2(){

​ synchronized(Mytest.class){

​ }

​ }

​ }

​ 实现原理:

​ 锁如何实现:

​ 在对象头里,有一块数据叫Mark Word. 在64位机器上,Mark Word是8字节,含有两个重要字段:锁标志位和占用该锁的thread ID。不同版本的jvm实现,对象头的数据结构有各种差异。

3.wait()/notify()

为什么要和synchronized一起使用?

两个线程之间要通信,对于同一个对象来说,一个线程调用该对象的wait(),另一个线程调用该对象的notify(),该对象本身就需要同步! 所以在调用wait()、notify()之前,要先通过synchronized关键字同步给对象,也就是给该对象加锁。

​ synchronized可以加载任何对象的实例方法上面,任何对象都可能成为锁,因此,wait()和notify()只能放在Object里面了。

​ 为啥wait()的时候必须释放锁?

若不释放锁,调用wait进入阻塞状态,一直不能推出synchronized代码块,那么其他线程永远没有办法拿到锁,永远没有机会调用notify,发生死锁。

​ wait()内部伪代码:

​ wait(){

	// 释放锁

​ // 阻塞,等待被其他线程notify

​ // 重新获取锁

​ }

4.interruptedException与interrupte()方法

​ 只有那些声明了会抛出InterruptedException的函数才会抛出异常,也就是下面常用的函数:

​ sleep(long) wait() join()

​ 轻量级阻塞和重量级阻塞:

​ 能够被中断的阻塞称为轻量级阻塞,对应的线程状态时WAITING或者TIMED_WAITING;而像synchronized这种不能被中断的阻塞成为重量级阻塞,对应的状态是BLOCKED。

​ 初始线程处于NEW,调用start开始执行后,进入running或ready,如果没有任何阻塞函数,线程只会在running和ready之间切换,也就是系统时间片调度,这两种状态的切换是操作系统完成的,除非手动调用yield函数,放弃对CPU的占用。

​ 一旦调用了任何了wait,sleep,join ,LockSupport.park,线程就会进入waiting或timed_waiting状态,两者的区别只是前者位无限期阻塞,后者则传入了一个时间参数,阻塞一个有限时间。如果使用了synchronized,则会进入blocked状态。

​ 不太常见的阻塞/唤醒函数,LockSupport.park/unpark,这对函数非常关键,Concurrent包中Lock的实现即依赖这一对原语。

​ 因此thread.interrupt()的精确含义是“唤醒轻量级阻塞”,而不是字面意思“中断一个线程”。

​ thread.isInterrupted()是实例方法,读取中断状态,不修改状态

​ Thread.interrupted()不仅读取中断状态,还会重置中断标志位。

5.线程优雅关闭

​ 线程是“一段运行中的代码”,一个运行中的方法。运行到一半的线程能否强制杀死?

不能,在Java中,有stop(),destroy()等方法,但这些方法官方明确不建议使用,如强制杀死线程,则线程中所有的资源,例如文件描述符,网络连接等无法正常关闭。

​ 因此一个线程一旦运行起来,不要强行关闭,合理的做法是让其运行完,干净地释放掉所有资源,然后退出,如果是一个不断循环运行的线程,就需要到线程间的通信机制,让主线程通知其推出。还可在线程对象中设置标志位来退出 。

​ 守护线程,当所有非守护线程退出之后,整个JVM进程就退出 了。

上一篇:[kernel 启动流程] (第四章)第一阶段之——dtb的验证【转】


下一篇:MySQL 日期函数