多线程系列
线程状态
一个线程对象在它的生命周期内,需要经历5个状态。
新生状态(New)
用new关键字建立一个线程对象后,该线程对象就处于新生状态。
处于新生状态的线程有自己的内存空间,通过调用start方法进入就绪状态。
就绪状态(Runnable)
处于就绪状态的线程已经具备了运行条件,但是还没有被分配到CPU,处于“线程就绪队列”,等待系统为其分配CPU。
就绪状态并不是执行状态,当系统选定一个等待执行的Thread对象后,它就会进入执行状态。一旦获得CPU,线程就进入运行状态并自动调用自己的run方法。有4中原因会导致线程进入就绪状态:
-
新建线程:调用start()方法,进入就绪状态;
-
阻塞线程:阻塞解除,进入就绪状态;
-
运行线程:调用yield()方法,直接进入就绪状态;
-
运行线程:JVM将CPU资源从本线程切换到其他线程。
运行状态(Running)
在运行状态的线程执行自己run方法中的代码,直到调用其他方法而终止或等待某资源而阻塞或完成任务而死亡。
如果在给定的时间片内没有执行结束,就会被系统给换下来回到就绪状态。也可能由于某些“导致阻塞的事件”而进入阻塞状态。
阻塞状态(Blocked)
阻塞指的是暂停一个线程的执行以等待某个条件发生(如某资源就绪)。
有4种原因会导致阻塞:
-
执行sleep(int millsecond)方法,使当前线程休眠,进入阻塞状态。当指定的时间到了后,线程进入就绪状态。
-
执行wait()方法,使当前线程进入阻塞状态。当使用nofity()方法唤醒这个线程后,它进入就绪状态。
-
线程运行时,某个操作进入阻塞状态,比如执行IO流操作(read()/write()方法本身就是阻塞的方法)。只有当引起该操作阻塞的原因消失后,线程进入就绪状态。
-
join()线程联合: 当某个线程等待另一个线程执行结束后,才能继续执行时,使用join()方法
死亡状态(Terminated)
死亡状态是线程生命周期中的最后一个阶段。线程死亡的原因有两个:
-
正常运行的线程完成了它run()方法内的全部工作
-
线程被强制终止,如通过执行stop()或destroy()方法来终止一个线程
(注:stop()/destroy()方法已经被JDK废弃,不推荐使用)。
当一个线程进入死亡状态以后,就不能再回到其它状态了。
终止线程
终止线程一般不使用JDK提供的stop()/destroy()方法(它们本身也被JDK废弃了)。
通常的做法是提供一个boolean型的终止变量,当这个变量置为false,则终止线程。
1 | package com.myThread; |
该程序中通过主线程控制live的值,当主线程把live置为 false
时,run()方法停止执行,子线程终止运行
暂停线程
暂停线程执行常用的方法有sleep()和yield()方法
这两个方法的区别是:
-
sleep()方法:可以让正在运行的线程进入阻塞状态,直到休眠时间满了,进入就绪状态。
注意:是休眠期到了后才会进入就绪状态,参与竞争获取CPU使用权
-
yield()方法:可以让正在运行的线程直接进入就绪状态,让出CPU的使用权。
注意:由于没有标出让出的时间,所以下一刻就会立即进入竞争获取CPU的就绪状态
线程基本信息获取
1 | isAlive(); |
注意:优先级低只是意味着获得调度的概率低。并不是绝对先调用优先级高的线程后调用优先级低的线程。
线程同步
线程同步其实就是一种等待机制,多个需要同时访问此对象的线程进入这个对象的等待池形成队列,等待前面的线程使用完毕后,下一个线程再使用。
由于同一进程的多个线程共享同一块存储空间,在带来方便的同时,也带来了访问冲突的问题。Java语言提供了专门机制以解决这种冲突,有效避免了同一个数据对象被多个线程同时访问造成的这种问题。
由于我们可以通过 private 关键字来保证数据对象只能被方法访问,所以我们只需针对方法提出一套机制,这套机制就是synchronized
关键字,它包括两种用法:
- synchronized 方法
- synchronized 块
synchronized 方法
1 | public synchronized void func(int a); |
synchronized 方法控制对“对象的类成员变量”的访问:每个对象对应一把锁,每个 synchronized 方法都必须获得调用该方法的对象的锁方能执行,否则所属线程阻塞,方法一旦执行,就独占该锁,直到从该方法返回时才将锁释放,此后被阻塞的线程方能获得该锁,重新进入可执行状态。
synchronized块
synchronized 方法的缺陷:若将一个大的方法声明为synchronized 将会大大影响效率。
Java 为我们提供了更好的解决办法,那就是 synchronized 块。 块可以让我们精确地控制到具体的“成员变量”,缩小同步的范围,提高效率。
synchronized 块:通过 synchronized关键字来声明synchronized 块,语法如下:
1 | synchronized(syncObject){ |
表示在操控 syncObject时,每次只能控制一次,如果多个线程同时执行到这一条语句,则同一时间只能有一个线程操控syncObject