目录
三、wait()、sleep()、notify()和yield()等方法介绍
一、线程状态
为了方便理解,线程运行状态大致可分为:初始状态、就绪状态、运行状态、阻塞状态、睡眠状态、等待状态和消亡状态。
1.当一个线程被创建的时候处于初始状态;
2.线程对象创建后,其他线程(如main线程)调用start()方法,该线程处于就绪状态。该状态的线程位于可运行线程池中,等待被线程调度选中,获取CPU执行权。就绪状态的线程在获的CPU时间片后变成运行状态。
3.线程进入synchronized关键字修饰的方法或者代码块的状态(获得锁)为阻塞状态。处于该状态的线程具备运行资格,但没有执行权。
4.当线程调用sleep(time)方法变成睡眠状态,即放弃了执行资格,在达到一定时间time后会被自动唤醒变成阻塞状态等待CPU执行。
5.当线程调用wait()方法变成等待状态,即放弃了执行资格,只有当调用notify()显示唤醒,才会使其处于一个阻塞状态等待CPU执行,否则将无限期的等待。
6.当线程调用stop()方法或者run()方法执行完毕,线程变成消亡状态。线程一旦终止了,无法复生。如果在终止的线程中调用start(),会抛出java.lang.IllegalThreadStateException异常。
二、创建线程的方式
创建线程的方式有两种,分别是继承Thread类和实现Runnable接口。
2.1 继承Thread类
继承Thread类,覆盖run()方法。
public class Ticket extends Thread {
private int tick = 100;
@Override
public void run() {
while (true) {
if (tick > 0) {
System.out.println(Thread.currentThread().getName()+" sale ticket: " + (tick--));
}
}
}
}
// 创建线程对象
Ticket t1= new Ticket();
//调用start()方法
t1.start();
2.2 实现Runnable接口
步骤:
- 定义类实现Runnable接口;
- 覆盖Runnable中的run()方法;
- 通过Thread类建立线程对象;
- 将Runnable接口中的子类对象作为参数传递给Thread类的构造函数。(自定义run()方法所属的对象是Runnable接口子类对象,需要让线程去指定对象的run()方法,就必须明确该run()方法的所属对象)
- 调用Thread类的start()方法开启线程并调用Runnable接口的run()方法。
public class TicketRunnable implements Runnable {
// 问题:当多条语句在操作同一个线程共享数据的时候,一个线程对多个语句只执行了一部分,还没有执行完, 另一个程序就参与了进来执行,导致共享数据错误。
// 解决方案:对多条语句操作共享数据时,只能让一个线程都执行完,在执行过程中,其他线程不能执行 同步代码块synchronized
private int tick = 100;
@Override
public void run() {
while (true) {
synchronized (new Object()){
if (tick > 0) {
try{
Thread.sleep(30);
} catch (Exception e){
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+" sale ticket: " + (tick--));
}
}
}
}
}
TicketRunnable ticketRunnable = new TicketRunnable();
Thread thread= new Thread(ticketRunnable);
Thread thread2= new Thread(ticketRunnable);
thread.start();
thread2.start();
三、wait()、sleep()、notify()和yield()等方法介绍
wait()、notify()和notifyAll()属于对象,而sleep()和yield()属于Thread类。
wait()、notify()和notifyAll()必须放在同步代码块中执行,wait()是线程从运行状态变成阻塞状态并且释放同步锁,只有当调用notify()或者notifyAll()唤醒一个正在等待该对象锁的线程,然后继续往下执行,直到执行完退出对象锁锁住的区域(synchronize修饰的代码块)后再释放锁。
sleep()使线程从运行状态变成阻塞状态但不会释放锁,等待休眠时间到,线程重新被唤醒,由阻塞状态变成就绪状态。
yield()使当前线程从运行状态变成就绪状态但不会释放锁,让其他同等优先级的线程获得执行权。