start和run
start方法
- 功能说明:启动一个新线程,在新的线程运行run方法中的代码
- start方法只是让线程进入就绪,里面代码不一定立刻运行,每个start方法只能调用一次,多次调用会出现(java.lang.IllegalThreadStateException)异常
start方法
- 功能说明:新线程启动后会调用的方法
- 如果在构造Thread对象中传递Runnable参数,则线程启动后会调用Runnable中的run方法,否则默认不执行任何操作。可以创建Thread的子类对象,来覆盖默认行为。
@Slf4j(topic = "ants.TestStart")
public class TestStart {
public static void main(String[] args) {
Thread t = new Thread("t1"){
@Override
public void run(){
log.debug("执行run");
}
};
t.start();
log.debug("main 执行");
}
}
sleep和yield
sleep
- 调用sleep会让当前线程从Running进入Timed Waiting状态
- 其他线程可以使用interrupt方法打断正在睡眠的线程,这时sleep方法会抛出InterruptedException
- 睡眠结束后的线程未必会like得到执行
- 建议用TimeUnit的sleep代替Thread的sleep来获得更好的可读性
@Slf4j(topic = "ants.TestSleep")
public class TestSleep {
public static void main(String[] args) {
//测试线程sleep
Thread t1 = new Thread("t1") {
@SneakyThrows
@Override
public void run() {
Thread.sleep(2000);
log.debug("t1线程停留2秒后执行");
}
};
t1.start();
log.debug("t1线程状态:{}",t1.getState());
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.debug("t1线程状态:{}",t1.getState());
}
}
@Slf4j(topic = "ants.TestSleepInterrupt")
public class TestSleepInterrupt {
public static void main(String[] args) throws InterruptedException {
//睡眠线程调用interrupt,会抛出(InterruptedException)异常
Thread t1 = new Thread("t1") {
@Override
public void run() {
//建议使用 TimeUnit.SECONDS.sleep,睡眠时间更直观
log.debug("enter sleep ...");
try {
TimeUnit.SECONDS.sleep(2);
log.debug("t1线程停留2秒后执行");
} catch (InterruptedException e) {
log.debug("sleep interrupt ...");
e.printStackTrace();
}
}
};
t1.start();
Thread.sleep(1000);
log.debug(" t1 interrupt...");
t1.interrupt();
}
}
yield
- 调用yield会让当前线程从Running进入Runnable状态,然后调度执行其他同优先级的线程。如果这时没有同优先级的线程,name不能保证让当前线程暂停的效果。
- 具体的实现依赖于操作系统的任务调度器
- yield代码测试没有那么明显,首先保证两个线程优先级一致,但是t1线程执行yield方法后,虽然从Running进入Runnable状态,但是很有可能再次获得CPU时间片,下面这段代码理想状态下是,线程在执行0的倍数时,暂停当前线程,执行其他线程。比如t1执行到(i=10),t1不在执行下一次打印,让t2打印。
@Slf4j(topic = "ants.TestYield")
public class TestYield {
public static void main(String[] args) {
Test t1 = new Test("t1");
Test t2 = new Test("t2");
t1.start();
t2.start();
}
}
@Slf4j(topic = "ants.Test")
class Test extends Thread{
public Test(String name){
super(name);
}
@Override
public void run() {
for(int i = 1;i <=100; i++) {
log.debug("t1线程执行第{}循环",i);
if(i == 0)
Thread.yield();
}
}
}
sleep与yield对比
- 让其他线程执行:sleep会明确在睡眠时间让其他线程执行,yield会和runnable状态线程得到执行可能
- 状态不同:执行sleep后线程状态变为timed waiting,执行yield后线程状态变为runnable
- 明确的时间:sleep会有明确时间(睡眠时长)不会执行,因为任务调度器不会把CPU时间片分给状态timed waiting;yield可能会得到时间片,立即执行。
线程优先级
- 线程优先级会提示调度器优先调度该线程,但仅仅是一个提示,调度器可以忽略
- 如果cpu比较忙,那么优先级高的线程将会获得更多的而时间片,但cpu空闲时,优先级几乎没作用
@Slf4j(topic = "ants.TestPriority")
public class TestPriority {
public static void main(String[] args) {
Thread t1 = new Thread("t1") {
@Override
public void run() {
int count=0;
while(true){
log.debug("t1线程count值:{}",count++);
}
}
};
Thread t2 = new Thread("t2") {
@Override
public void run() {
int count=0;
while(true){
//yield(); //注释该行,发现打印结果count值接近,打开后t1线程获得CPU时间片多,count值大
log.debug("t2线程count值:{}",count++);
}
}
};
//t1.setPriority(Thread.MAX_PRIORITY);//单核CPU测试线程优先级效果明显
//t2.setPriority(Thread.MIN_PRIORITY);
t1.start();
t2.start();
}
}
防止CPU只用100%
- sleep线程睡眠,适合无锁场景
- wait或条件变量控制,都需要加锁,需要相关唤醒操作,一般用户同步场景
join
- 调用该线程的线程同步,程序才会继续向下执行
- join可以做限时通过,join(1000)1000毫秒后,如果线程没结束,继续向下执行
interrupt、isInterrupted、interrupt
- interrupt设置打断标记,正常线程调用方法后,打断状态是true,调用wait、sleep、join线程的打断标记是false
- isInterrupted 查看线程状态
- interrupted 查看线程状态,并设置打断标记为false
@Slf4j(topic = "ants.TestInterrupt")
public class TestInterrupt {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread("t1") {
//进入sleep wait 、join线程,异常标记为false
@Override
public void run() {
log.debug("t1 interrupt");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
log.debug("Thread.currentThread().interrupt() before:{}",Thread.currentThread().isInterrupted());
e.printStackTrace();
Thread.currentThread().interrupt();
log.debug("Thread.currentThread().interrupt():{}",Thread.currentThread().isInterrupted());
}
}
};
Thread t2 = new Thread("t2") {
//正常执行的线程,打断标记为true
@Override
public void run() {
log.debug("t2 interrupt");
while(true){
}
}
};
Thread t3 = new Thread("t3") {
//线程优雅关闭
@Override
public void run() {
boolean flag = true;
while(flag){
if(Thread.currentThread().isInterrupted()){
log.debug("t3 interrupt");
flag=false;
}
}
//处理退出循环后续逻辑
log.debug("t3 退出循环");
}
};
t1.start();
Thread.sleep(1000);
t1.interrupt();
Thread.sleep(1000);
log.debug("t1线程的打断标记:{}",t1.isInterrupted());
log.debug("-------------------");
t2.start();
Thread.sleep(1000);
t2.interrupt();
Thread.sleep(1000);
log.debug("t2线程的打断标记:{}",t2.isInterrupted());
log.debug("-------------------");
t3.start();
Thread.sleep(1000);
t3.interrupt();
Thread.sleep(1000);
log.debug("t3线程执行结束后打断标记:{}",t3.isInterrupted());
}
}
park unpark
- 线程调用LockSupport中的park方法,当前线程会停下来执行,调用线程的interrupt,将打断标记置为false后,继续执行后续代码
@Slf4j(topic = "ants.TestPark")
public class TestPark {
public static void main(String[] args) {
Thread t1 = new Thread("t1") {
@Override
public void run() {
log.debug("t1 线程执行中-----");
LockSupport.park();
//log.debug("t1打断标记:{}",Thread.currentThread().isInterrupted());//不改变打断标记值
log.debug("t1打断标记:{}",Thread.interrupted());//将打断标记置为false
log.debug("t1 park 后面代码-----");
LockSupport.park();
log.debug("t1 再次 park 后面代码-----");
}
};
t1.start();
t1.interrupt();//打断正在执行的park线程
}
}