线程的生命周期:
新建(New):线程对象创建
就绪(Runnable):调用线程对象的start()方法,线程进入就绪状态,等待CPU调用
运行(Running):CPU调用就绪状态的线程,开始执行线程
阻塞(Blocked):运行状态的线程暂时放弃CPU使用权,停止执行,进入阻塞状态。根据产生阻塞的原因不同,阻塞状态可分为三种:
- 等待阻塞:运行状态中的线程执行wait()方法,使本线程进入等待阻塞状态
- 同步阻塞:线程在获取synchronized同步锁失败,进入同步阻塞状态
- 其他阻塞:通过调用线程的sleep()或join()或发出I/O请求时,线程会进入阻塞状态。当sleep()状态超时、join()等待线程终止或超时、或者I/O处理完毕,线程重新转入就绪状态。
死亡(Dead):线程执行完毕或者因异常退出run()方法,线程生命周期结束。
Java创建多线程
java创建多线程有三种方式:
- 继承Thread类,重写run()方法
- 实现Runnable接口,重写run()方法
- 实现Callable接口,重写call()方法,有返回值
run()、call()方法中的逻辑代码是线程实际执行的逻辑。
创建线程对象,默认会有一个线程名,以 “Thread-” 开头,从0开始计数:Thread-0 , Thread-1.....
也可以手动给线程名赋值:thread.setName("ThreadName1") 或 Thread(String name) 或 Thread(ThreadGroup group, String name) 或 Thread(Runnable target, String name) 等构造方法在创建线程的时候设置线程名。
多线程内存模型:
多线程执行时,在栈内存中,其实每一个执行线程都有一片自己所属的栈内存空间。进行方法的压栈和弹栈。
当执行线程的任务结束了,线程自动在栈内存中释放了。但是当所有的执行线程都结束了,那么进程就结束了。
线程的其他概念:
线程组:ThreadGroup
构造:ThreadGroup(String name);
ThreadGroup(ThreadGroup parent, String name);
线程组的作用是:可以批量管理线程或线程组对象,有效地对线程或线程组对象进行组织,如:
- void interrupt() 批量终止线程
- void add(Thread t) 向线程组中添加线程
- void remove(Thread t) 移除线程组中线程
- void list() 显示线程组中所有线程
可以把线程归属到某一个线程组中,线程组中可以有线程对象,也可以有线程组,组中还可以有线程,这样的组织结构有点类似于树的形式,如图所示:
优先级:setPriority(int priority)
Java线程可以有优先级的设定,高优先级的线程比低优先级的线程有更高的几率得到执行,但是并不代表高优先级的线程一定先与低优先级的线程执行,只是高优先级的线程被执行的概率相对更高。
- 记住当线程的优先级没有指定时,所有线程都携带普通优先级——普通优先级为5。
- 优先级可以用从1到10的范围指定。10表示最高优先级,1表示最低优先级,5是普通优先级。
- 记住优先级最高的线程在执行时被给予优先。但是不能保证线程在启动时就进入运行状态。
- 与在线程池中等待运行机会的线程相比,当前正在运行的线程可能总是拥有更高的优先级。
- 由调度程序决定哪一个线程被执行。
- thread.setPriority()用来设定线程的优先级。
- 记住在线程开始方法被调用之前,线程的优先级应该被设定。
- 你可以使用常量,如MIN_PRIORITY,MAX_PRIORITY,NORM_PRIORITY来设定优先级。
守护线程:Daemon
所谓守护 线程,是指在程序运行的时候在后台提供一种通用服务的线程,比如垃圾回收线程就是一个很称职的守护者,并且这种线程并不属于程序中不可或缺的部分。因此,当所有的非守护线程结束时,程序也就终止了,同时会杀死进程中的所有守护线程。反过来说,只要任何非守护线程还在运行,程序就不会终止。
用户线程和守护线程两者几乎没有区别,唯一的不同之处就在于虚拟机的离开:如果用户线程已经全部退出运行了,只剩下守护线程存在了,虚拟机也就退出了。 因为没有了被守护者,守护线程也就没有工作可做了,也就没有继续运行程序的必要了。
在使用守护线程时需要注意一下几点:
- thread.setDaemon(true)必须在thread.start()之前设置,否则会跑出IllegalThreadStateException异常。你不能把正在运行的常规线程设置为守护线程。
- 在Daemon线程中产生的新线程也是Daemon的。
- 守护线程应该永远不去访问固有资源,如文件、数据库,因为它会在任何时候甚至在一个操作的中间发生中断。
yield和join:
- public static void yield() 暂停当前正在执行的线程对象,并执行其他线程,可能还会继续执行当前线程。
- public final void join() 等待该线程终止
- public final void join(long millis) 等待该线程终止的时间最长为 millis毫秒。超时为 0 意味着要一直等下去。
yield:
yield意味着放手,放弃,投降。一个调用yield()方法的线程告诉虚拟机它乐意让其他线程占用自己的位置。这表明该线程没有在做一些紧急的事情。注意,这仅是一个暗示,并不能保证不会产生任何影响。
- Yield是一个静态的原生(native)方法
- Yield告诉当前正在执行的线程把运行机会交给线程池中拥有相同优先级的线程。
- Yield不能保证使得当前正在运行的线程迅速转换到可运行的状态
- 它仅能使一个线程从运行状态转到可运行状态,而不是等待或阻塞状态
join:
如果join()方法在一个线程实例上调用,当前运行着的线程将阻塞直到这个线程实例完成了执行。在join()方法内设定超时,使得join()方法的影响在特定超时后无效。当超时时,主方法和任务线程申请运行的时候是平等的。然而,当涉及sleep时,join()方法依靠操作系统计时,所以你不应该假定join()方法将会等待你指定的时间。