实现方式
- 继承Thread类,并重写run方法
- 实现Runnable,并作为参数构造Thread
两种方式最终都是通过Thread的run方法进行调用
Thread.java
@Override
public void run() {
//target为传入的Runnable对象
if (target != null) {
target.run();
}
}
复制代码
线程开启后会执行其中的run方法,执行完后线程结束,分别对应了线程的NEW、RUNNABLE、TERMINATED状态
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
//运行中,state为RUNNABLE
System.out.println(Thread.currentThread().getName() + "--state:" + Thread.currentThread().getState());
}
}, "Thread1");
//创建未start,state为NEW
System.out.println(t1.getName() + "--state:" + t1.getState());
t1.start();
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
//自动结束,state为TERMINATED
System.out.println(t1.getName() + "--state:" + t1.getState());
复制代码
打印结果:
Thread1--state:NEW Thread1--state:RUNNABLE Thread1--state:TERMINATED
线程的操作
常用的线程操作方法有:
wait、notify、notifyAll
这三个方法是定义在Object类中的,即所有的类都可以作为lock
当一段代码块以lock对象为锁时,即意味着访问该代码段的所有线程需要先获得lock锁才能执行其中代码,下面代码中Thread1、Thread2都调用了objectLock(),Thread1先拿到了锁,Thread2只能等到其释放再获取并执行
输出如下:
Thread1--start Thread1--get lock Thread2--start Thread1--release lock Thread2--get lock Thread2--release lock
private Object lock = new Object();
private void testLock() {
new Thread(new Runnable() {
@Override
public void run() {
objectLock();
}
}, "Thread1").start();
System.out.println("Thread1" + "--start");
new Thread(new Runnable() {
@Override
public void run() {
objectLock();
}
}, "Thread2").start();
System.out.println("Thread2" + "--start");
}
private void objectLock() {
synchronized (lock) {
System.out.println(Thread.currentThread().getName() + "--get lock");
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "--release lock");
}
}
复制代码
在锁的基础上,当线程拿到锁执行到一定阶段,需要让其他线程拿到锁进行一些前置操作,如初始化、同步信息等再继续执行时,可通过锁的wait方法挂起当前线程,并释放锁。待其他线程处理结束后通过锁的notify、notifyAll唤醒wait线程继续执行
输入:
Thread1--start Thread1--invoke doWork Thread1--get lock Thread1--wait Thread2--start Thread2--invoke prepare Thread2--get lock Thread2--notify Thread2--release lock Thread1--continue Thread1--release lock
可见线程1先拿到了锁,但却后结束。
private void testWaitNotify() {
new Thread(new Runnable() {
@Override
public void run() {
doWork();
}
}, "Thread1").start();
System.out.println("Thread1" + "--start");
new Thread(new Runnable() {
@Override
public void run() {
prepare();
}
}, "Thread2").start();
System.out.println("Thread2" + "--start");
}
boolean isInit = false;
private void doWork() {
System.out.println(Thread.currentThread().getName() + "--invoke doWork");
synchronized (lock) {
System.out.println(Thread.currentThread().getName() + "--get lock");
try {
if (!isInit) {
System.out.println(Thread.currentThread().getName() + "--wait");
lock.wait();
System.out.println(Thread.currentThread().getName() + "--continue");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "--release lock");
}
}
private void prepare() {
System.out.println(Thread.currentThread().getName() + "--invoke prepare");
synchronized (lock) {
System.out.println(Thread.currentThread().getName() + "--get lock");
try {
Thread.sleep(200);
System.out.println(Thread.currentThread().getName() + "--notify");
lock.notify();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "--release lock");
}
}
复制代码
join
正常情况下两个线程开始执行,其中Thread1有耗时操作,肯定是Thread2先执行完。
输出:
Thread1--start Thread1--doWork Thread2--start Thread2--stop Thread1--stop
private void testJoin(){
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
try {
System.out.println(Thread.currentThread().getName() + "--doWork");
Thread.sleep(200);
System.out.println(Thread.currentThread().getName() + "--stop");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "Thread1");
t1.start();
System.out.println("Thread1" + "--start");
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "--stop");
}
}, "Thread2");
t2.start();
System.out.println("Thread2" + "--start");
}
复制代码
当在Thread2的run方法中加入后,会先执行完Thread1才会执行Thread2的后续逻辑
输出:
Thread1--start Thread1--doWork Thread2--start Thread2--Thread1 join Thread1--stop Thread2--stop
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
try {
System.out.println(Thread.currentThread().getName() + "--Thread1 join");
t1.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "--stop");
}
}, "Thread2");
复制代码
yield
它让掉当前线程 CPU 的时间片,使正在运行中的线程重新变成就绪状态,并重新竞争 CPU 的调度权, 注意:不释放锁
yield 和 sleep 的异同
1)yield, sleep 都能暂停当前线程,sleep 可以指定具体休眠的时间,而 yield 则依赖 CPU 的时间片划分。
2)yield, sleep 两个在暂停过程中,如已经持有锁,则都不会释放锁资源。
3)yield 不能被中断,而 sleep 则可以接受中断。
interrupt
线程的中断,两种方式
1.设置标记位,通过改变标记值结束线程
方式1较为简单,但需要在线程中的关键点判断标志位的值
2.调用线程的interrupt方法
可以响应interrupt的情况:线程处于sleep、wait状态
各方法调用对应的线程state
Thread.sleep(long millis) -> TIMED_WAITING
wait(long millis) -> TIMED_WAITING
wait() -> WAITING
join() -> WAITING
也就是说线程状态为WAITING/TIMED_WAITING的线程可通过interrupt中断并抛出InterruptedException
注意:当前线程被中断后会重置中断状态
if any thread has interrupted the current thread. The
<i>interrupted status</i> of the current thread is
cleared when this exception is thrown.
复制代码
private void stopThread() {
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
while (!Thread.interrupted()) {
try {
System.out.println(Thread.currentThread().getName() + "--doWork");
Thread.sleep(1000);
} catch (InterruptedException e) {
System.out.println(Thread.currentThread().getName() + "--throw InterruptedException");
// 当sleep/wait被中断会抛出异常,并清除当前线程的中断状态,没有这行会一直执行
Thread.currentThread().interrupt();
}
}
System.out.println(Thread.currentThread().getName() + "--stop");
}
}, "Thread1");
t1.start();
System.out.println("Thread1" + "--start");
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(300);
t1.interrupt();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "--stop");
}
}, "Thread2");
t2.start();
System.out.println("Thread2" + "--start");
}
复制代码
Thread2在开始300ms后向Thread1发送中断信号,Thread1当时在TIMED_WAITING状态(sleep),抛出InterruptedException异常,被捕获后再次设置为中断状态,通过Thread.interrupted()判断后退出循环。
输出:
Thread1--start Thread1--doWork Thread2--start Thread2--stop Thread1--throw InterruptedException Thread1--stop
即通过interrupt停止线程有两种方式,
1.以InterruptedException的方式终止
2.线程中代码逻辑多环节判断Thread.interrupted(),为true则退出线程
异常
java.lang.IllegalMonitorStateException
* Thrown to indicate that a thread has attempted to wait on an
* object's monitor or to notify other threads waiting on an object's
* monitor without owning the specified monitor.
复制代码
当一个线程在没有成为object的monitor的owner时,也就是没在以object为锁的synchronized代码块内调用
该object的wait则会抛出此异常。