线程状态
线程状态-
新建状态
new出一个线程,线程还没有开始运行,当线程处于新建状态时,程序还没有运行线程中的代码。
-
就绪状态
当线程对象调用start()方法即启动了线程,start()方法创建线程运行的系统资源,并调度线程运行run()方法。当start()方法返回后,线程就处于就绪状态。处于就绪状态的线程并不一定立即运行run()方法,线程还必须同其他线程竞争CPU时间,只有获得CPU时间才可以运行线程。对处于就绪状态的线程是由Java运行时系统的线程调度程序(thread scheduler)来调度的。
-
运行状态
当线程获得CPU时间后,它才进入运行状态,真正开始执行run()方法。
-
阻塞状态
当一个线程试图获取一个内部的对象锁(非java.util.concurrent库中的锁),而该锁被其他线程持有,则该线程进入阻塞状态。
-
等待状态
处于等待状态的线程正在等待另一个线程执行特定操作,例如Object.wait()等待一个线程notify,Thread.join()等待另一个线程执行结束。 -
死亡状态
有两个原因会导致线程死亡:
(1)run方法正常退出而自然死亡,
(2)一个未捕获的异常终止了run方法而使线程猝死。
线程调度
线程休眠
线程睡眠是线程主动让出CPU让其他线程执行,线程从运行状态切换为阻塞状态,线程休眠是其他线程执行的有效方法,但是休眠时间到休眠线程未必执行,它的执行依靠CPU的调度。但是线程在休眠的时候仍然占有锁,而且sleep()是静态方法,只能控制当前正在运行的线程。
/**
* 睡眠
* 一个计数器,计数到 100,在每个数字之间暂停 1 秒,每隔 10 个数字输出一个字符串
*/
public class ThreadSleep {
public static void main(String[] args) {
new Thread(new Runnable() {
public void run() {
for(int i = 0; i < 100; i++){
if((i) % 10 ==0 ){
System.out.println("************");
}
try {
Thread.sleep(1);
System.out.println("i的值是:" + i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
}
}
线程让步
运行状态的线程让出CPU资源,但是然给谁不知道,仅仅是让出,也有可能是自己,线程从运行状态切换为阻塞状态。
public class ThreadYield {
public static void main(String[] args) {
Thread t1 = new MyThread1();
Thread t2 = new Thread(new MyRunnable());
t2.start();
t1.start();
}
static class MyThread1 extends Thread {
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("线程1第" + i + "次执行!");
}
}
}
static class MyRunnable implements Runnable {
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("线程2第" + i + "次执行!");
Thread.yield();
}
}
}
}
//执行结果(可以看出线程线程1执行完后线程1开始执行)
线程2第0次执行!
线程1第0次执行!
线程1第1次执行!
线程1第2次执行!
线程1第3次执行!
线程1第4次执行!
线程1第5次执行!
线程1第6次执行!
线程1第7次执行!
线程1第8次执行!
线程1第9次执行!
线程2第1次执行!
线程2第2次执行!
线程2第3次执行!
线程2第4次执行!
线程2第5次执行!
线程2第6次执行!
线程2第7次执行!
线程2第8次执行!
线程2第9次执行!
线程合并
现在有线程A、线程B,线程合并是将线程A、B合二为一,线程A先执行,然后调用join方法,则线程B开始执行,等B执行完成后A再接着上次继续执行。应用场景是当一个线程必须等待另一个线程执行完毕才能执行时可以使用join方法。
public class ThreadJoin extends Thread {
public static void main(String[] args) {
Thread t1 = new MyThread2();
t1.start();
for (int i = 0; i < 10; i++) {
System.out.println("主线程第" + i + "次执行!");
if (i > 2) try {
//t1 线程合并到主线程中,主线程停止执行过程,转而执行 t1 线程, 直到 t1 执行完毕后继续。
t1.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class MyThread2 extends Thread {
public void run() {
for (int i = 0; i < 4; i++) {
System.out.println("线程 1 第" + i + "次执行!");
}
}
}
//执行结果
主线程第0次执行!
主线程第1次执行!
主线程第2次执行!
主线程第3次执行!
线程 1 第0次执行!
线程 1 第1次执行!
线程 1 第2次执行!
线程 1 第3次执行!
主线程第4次执行!
主线程第5次执行!
主线程第6次执行!
主线程第7次执行!
主线程第8次执行!
主线程第9次执行!
线程守候
守护线程在没有用户线程可服务时自动离开,在Java虚拟机中当正在运行的线程都是守护线程时,Java虚拟机退出。调用线程对象的方法setDaemon(true),则可以将其设置为守护线程,该方法必须在线程调用前执行。
public class ThreadDaemon {
public static void main(String[] args) {
Thread t1 = new MyCommon();
Thread t2 = new MyDaemon();
t2.setDaemon(true);
t1.start();
t2.start();
}
}
class MyCommon extends Thread {
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println("线程MyCommon第" + i + "次执行!");
try {
Thread.sleep(7);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class MyDaemon extends Thread {
public void run() {
for (int i = 0; i < 400; i++) {
System.out.println("线程 MyDaemon 第" + i + "次执行!");
try {
Thread.sleep(7);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
//执行结果(MyDaemon线程还没有执行完成便退出)
线程MyCommon第0次执行!
线程 MyDaemon 第0次执行!
线程 MyDaemon 第1次执行!
线程MyCommon第1次执行!
线程MyCommon第2次执行!
线程 MyDaemon 第2次执行!
线程MyCommon第3次执行!
线程 MyDaemon 第3次执行!
线程MyCommon第4次执行!
线程 MyDaemon 第4次执行!
线程 MyDaemon 第5次执行!
线程优先级
线程的有限执行顺序,优先级1~10,数值越大优先级越高。在一个线程中开启另外一个新线程,则新开线程称为该线程的子线程,子线程初始优先级与父线程相同。
注意:线程的优先级仍然无法保障线程的执行次序。只不过,优先级高的线程获取CPU资源的概率较大,优先级低的并非没机会执行。
public class ThreadPriority {
public static void main(String[] args) {
Thread threadA = new Thread(new ThreadA());
Thread threadB = new Thread(new ThreadB());
threadA.setPriority(10);
threadB.setPriority(1);
threadB.start();
threadA.start();
}
}
class ThreadA extends Thread {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(" A线程第" + i + "次执行");
}
}
}
class ThreadB extends Thread {
@Override
public void run() {
for (int j = 0; j < 10; j++) {
System.out.println("B线程第" + j + "次执行");
}
}
}
//执行结果
B线程第0次执行
B线程第1次执行
A线程第0次执行
B线程第2次执行
A线程第1次执行
A线程第2次执行
B线程第3次执行
B线程第4次执行
B线程第5次执行
B线程第6次执行
B线程第7次执行
B线程第8次执行
B线程第9次执行
A线程第3次执行
问题
- 为什么synchronized等到获取锁是阻塞,而ReentrantLock是阻塞。
代码验证:
public class StatusDemo {
static Object lock = new Object();
public static void read() {
synchronized (lock) {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("读数据");
}
}
public static void write() {
synchronized (lock) {
System.out.println("写数据");
}
}
public static void main(String[] args) throws InterruptedException {
new Thread(new Runnable() {
@Override
public void run() {
StatusDemo.read();
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
StatusDemo.write();
}
}).start();
}
}
public class StatusDemo {
static ReentrantLock lock = new ReentrantLock();
public static void read() {
lock.lock();
try {
System.out.println(Thread.currentThread().getName()+"读数据");
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public static void write() {
lock.lock();
try {
System.out.println(Thread.currentThread().getName()+"写数据");
}finally {
lock.unlock();
}
}
public static void main(String[] args) throws InterruptedException {
new Thread(new Runnable() {
@Override
public void run() {
StatusDemo1.read();
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
StatusDemo1.write();
}
}).start();
}
}
为什么ReentrantLock是阻塞呢,看ReentrantLock的源码你会发现,其实ReentrantLock是AQS实现的,而AQS中使用了park()和unpark().
网友评论