1.创建线程的方式:
继承Thread类(实际上Thread也是实现Runnable接口)
实现Runnable接口
实现Callable接口(能返回执行结果)
通过线程池创建(通过Runnable或Callable参数)
2.Thread常用的函数
任务:
run函数:这个是线程运行的真正任务代码,需要开发人员去重写该方法,但是不能直接调用,直接调用只是执行了一个函数,不是运行线程。运行线程只能通过start方法。
开启:
start函数:这是线程的启动函数,调用这个函数之后会让JVM回调线程的run方法,实现线程的任务代码执行工作。
守护:
setDaemon函数:这个函数如果参数传的是true,那么在主线程(也就是main函数的对应线程)退出之后,这个函数对应的线程也会跟着退出。当一个JVM进程里面开启了多个线程时,这些的线程就会分成两种:守护线程和非守护线程。在Java中有一个规定:当所有的非守护线程退出之后,整个JVM进程就会退出。意思就是,守护线程不影响整个JVM进程的退出。例如:垃圾回收的线程就是守护线程,它们在后台默默工作,当开发者所有的非守护线程都退出之后,整个JVM进程就退出了。
阻塞:
Object.wait函数:线程会释放对象的锁进入阻塞状态,只有当其他线程调用 notify /notifyAll函数时才可能唤醒此线程。
Thread.sleep函数:线程不会释放锁,只会阻塞线程,休眠参数对应的时间之后,恢复运行状态。
暂停:
yield函数:线程会让出自己的时间片给同优先级或更高优先级的线程去执行,自身线程回到就绪的状态,等待下次获取时间片继续执行。
唤醒:
Object.notify函数:当使用某个对象的notify()方法时,将从该对象的等待集合中选择一个等待的线程唤醒。
Object.notifyAll函数:唤醒所有处于等待状态的线程,然后让它们重新进入锁的争夺队列争抢锁,最终只有一个线程获得该锁,进行执行状态,其他线程仍要继续等待。
interrupted函数:这个函数名经常令人误解,字面理解为打断一个线程,实际上是唤醒一个线程,等于是给线程发送了一个唤醒的信号。
isInterrupted函数:这个函数是实例化后才能调用的函数,是用来判断线程是否收到过中断的信号,但是只读取中断的状态,不会去修改状态。
Thread.interrupted函数:这个是静态函数,也是用来判断线程是否收到过中断的信号,但是这个不仅读取中断的状态,还会重置中断的标志位。
打断:
interrupt函数:这个函数其实也一直让很多人误解,以为线程只要调用了这个函数,就会发生InterruptedException异常。实际只有那些声明了会抛出InterruptedException异常的函数在调用interrupt的时候才会抛出异常。目前有:
public static native void sleep(long millis) throws InterruptedException;(Thread的函数,包括其重载函数)
public final synchronized void join(long millis);(Thread的函数,包括其重载函数)
public final native void wait(long timeout) throws InterruptedException;(Object的函数)
关闭:
stop函数、destroy函数:这些函数都是官方不建议使用的函数,因为如果强制杀死线程,那么线程中使用的资源将不能正常关闭,比如网络连接、句柄、文件操作符等。所以,线程一旦开始运行,就没必要强制去关闭它。正确的做法应该是:让程序正常运行结束、释放完占用的资源之后退出运行。
3.如何优雅的实现让其他线程退出?
利用线程间的通信机制,通知其他线程退出。实际的开发中,一般都是通过一个关闭的标志位来实现进程的关闭。特别是有一些死循环的业务处理代码(比如轮询业务消息进行业务处理,就需要通过判断标志位实现退出)。需要注意的是,在退出之前需要处理好业务数据(比如未处理的数据存放到数据组件)和资源的释放工作(比如网络连接释放)。