概述
1、start()
:启动一个新线程,在新的线程中运行run
方法。注意:该方法只是让线程进入就绪状态,里面代码不一定立刻执行(CPU时间片还没分给它)。
2、join()
:等待线程运行结束,写上t1.join()
,程序运行到这句话时会等待线程t1运行结束后再执行后续操作,该方法提供了线程间运行顺序的控制,假设t2线程需要等待t1线程运行完才能执行,则可以在t2线程前加上t1.join()
。
static int r = 0;
@Test
public void test1() throws InterruptedException {
log.debug("开始");
Thread t1 = new Thread(() -> {
log.debug("开始");
sleep(1);
log.debug("结束");
r = 10;
},"t1");
t1.start();
t1.join(); // 等待t1运行完后,修改了r的值为10,再运行后续程序
log.debug("结果为:{}", r);
log.debug("结束");
}
3、join(long n)
:等待线程运行结束,最多等待n毫秒,称为有时效的等待。
4、setPriority(),getPriority()
:设置和获取优先级
quad
优先级从1-10,默认为5。线程优先级会提示调度器优先调度该线程,但其仅仅是一个提示,调度器可以忽略。如果CPU忙,那么优先级高的线程会获得更多时间片,但闲时,优先级失效。
Runnable task1 = () -> {
int count = 0;
while(true){
System.out.println("---->1 " + count);
count ++ ;
}
};
Runnable task2 = () -> {
int count = 0;
while(true){
System.out.println("---->2 " + count);
count ++ ;
}
};
Thread t1 = new Thread(task1, "t1");
Thread t2 = new Thread(task2, "t2");
t1.setPriority(1); t2.setPriority(10);
t1.start(); t2.start();
quad
通过给t1设置最低的优先级,给t2设置最高的优先级,可以发现打印2的次数远远大于1。
5、getState()
:获取线程状态
Thread t1 = new Thread("t1") {
@Override
public void run() {
log.debug("running...");
}
};
System.out.println(t1.getState()); // NEW(新建状态,还没被CPU运行)
t1.start();
System.out.println(t1.getState()); // RUNNABLE(运行状态)
6、interrupt()
:打断阻塞的线程(可能是由于使用sleep,join,wait导致线程阻塞);也可以打断正在运行的线程。
Thread t1 = new Thread(()->{
log.debug("sleep");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "t1");
t1.start();
Thread.sleep(1000);
log.debug("interrupt");
t1.interrupt();
log.debug("打断标记: {}", t1.isInterrupted());
quad 当一个线程被打断,如果没有在该线程内判断是否被打断,该线程还会一直运行下去,因此我们可以在该线程运行逻辑前加入该线程是否被打断的判断,一旦该线程被打断了,就结束该线程。
Thread t1 = new Thread(() -> {
while(true) {
boolean interrupted = Thread.currentThread().isInterrupted();
if(interrupted) {
log.debug("被打断了, 退出循环");
break;
}
}
}, "t1");
t1.start();
Thread.sleep(1000);
log.debug("interrupt");
t1.interrupt();
7、isAlive()
:线程是否存活
8、sleep()
:调用sleep会让当前线程从running进行timewaiting状态,注意:Thread.sleep()
在哪个线程里被调用就是让哪个线程等待。
Thread t1 = new Thread("t1"){
@SneakyThrows
@Override
public void run() {
Thread.sleep(2000);
}
};
t1.start();
// t1刚刚启动,还未到sleep处
log.debug("t1 state: {}", t1.getState()); // t1 state: RUNNABLE
Thread.sleep(1000);
// t1启动后一小段时间,t1进入sleep
log.debug("t1 state: {}", t1.getState()); // t1 state: TIMED_WAITING
quad
有一种情况是,我们不想线程睡一个固定时间,我希望能在程序中打断线程,可以结合interrupt()
方法使用:
Thread t1 = new Thread("t1"){
@Override
public void run() {
log.debug("enter sleep...");
try{
Thread.sleep(2000);
}catch (InterruptedException e){
log.debug("wake up...");
e.printStackTrace();
}
}
};
t1.start();
Thread.sleep(1000);
log.debug("interrupt...");
t1.interrupt(); // 不要睡眠了
quad 可以看到,刚开始时t1进入睡眠,经过了1s对t1线程进行打算。
20:40:20.977 c.Test5 [t1] - enter sleep...
20:40:21.976 c.Test5 [main] - interrupt...
20:40:21.976 c.Test5 [t1] - wake up...
java.lang.InterruptedException: sleep interrupted
at java.base/java.lang.Thread.sleep(Native Method)
at cn.itcast.test.Test5$4.run(Test5.java:61)
quad sleep经典应用场景:假设我们的某个线程需要不停的运行,但是又不希望这个线程一直把CPU全部占用,就可以使用sleep,每隔一小段时间运行下就行,如下:
while (true) {
try {
System.out.println("该线程一直运行");
Thread.sleep(100); // 腾出CPU资源
} catch (InterruptedException e) {
e.printStackTrace();
}
}
9、yield()
:让当前线程让出CPU使用权,会使得当前线程从Running进入Runnable就绪状态,然后调度执行其他线程,具体的实现依赖于操作系统的任务调度器,即可能存在无其他线程、让不出去的情况。
Runnable task1 = () -> {
int count = 0;
while(true){
System.out.println("---->1 " + count);
count ++ ;
}
};
Runnable task2 = () -> {
int count = 0;
while(true){
Thread.yield();
System.out.println("---->2 " + count);
count ++ ;
}
};
Thread t1 = new Thread(task1, "t1");
Thread t2 = new Thread(task2, "t2");
t1.start(); t2.start();
quad
对线程t2使用yield后,明显CPU给t2时间片少了很多,打印t2的次数只有t1的
1
3
frac{1}{3}
31。
10、不推荐的方法,这些方法已经过时,容易破坏同步代码块,造成线程死锁。
方法名 | 功能说明 |
---|---|
stop() | 停止线程运行 |
suspend() | 挂起(暂停)线程运行 |
resume() | 恢复线程运行 |
主线程和守护线程
quad
默认情况下,java进程需要等待所有线程都运行结束才会结束,有一种特殊的线程叫做守护线程,只要其他非守护线程结束了,即使守护线程未执行完,也会强制结束。例如垃圾回收器就是一种守护线程。使用t1.setDaemon(true)
可以将线程t1设置为守护线程,示例如下:
Thread t1 = new Thread(() -> {
while (true) {
if (Thread.currentThread().isInterrupted()) {
break;
}
}
log.debug("结束");
}, "t1");
// 设置为守护线程,当主线程结束后,它也会强制结束,否则该线程是while循环,会一直进行下去。
t1.setDaemon(true);
t1.start();
Thread.sleep(1000);
log.debug("结束");
如何在一个线程t1中优雅终止线程t2,给t2一个料理后事的机会。
错误思路:使用t2线程的stop方法杀死t2。如果t2锁住了共享资源,t2被杀死后再也没有机会释放锁,其他线程将永远无法获取锁。
正确思路:两阶段终止模式
线程状态
从操作系统层面分为5中状态:
- 初始状态:在语言层面创建了该线程,还未与操作系统线程关联,例如
Thread t1 = new Thread();
- 可运行状态:也称为就绪状态,调用
t1.start()
后,CPU时间片还未分到t1线程,该线程等待运行 - 运行状态:线程获得了CPU时间片,从可运行状态变为运行状态,当CPU时间片用完,又会从运行状态转换至可运行状态,会导致线程的上下文切换。
- 阻塞状态:加入对一个处于运行状态的线程使用
t1.sleep(1000)
会让其处于阻塞状态,等sleep时间结束后又会唤醒阻塞的线程,转换至可运行状态。例如如果调用了阻塞API读写文件,此时该线程实际不会用到CPU,会导致上下文切换,进入阻塞状态,等读写操作完成后,会由操作系统唤醒阻塞的现车给,转换到可运行状态。注意:CPU时间片不会分给阻塞状态线程,需要唤醒。 - 终止状态:线程执行完毕
从java API上分为6种状态
public enum State {
/**
* Thread state for a thread which has not yet started.
*/
NEW,
/**
* Thread state for a runnable thread. A thread in the runnable
* state is executing in the Java virtual machine but it may
* be waiting for other resources from the operating system
* such as processor.
*/
RUNNABLE,
/**
* Thread state for a thread blocked waiting for a monitor lock.
* A thread in the blocked state is waiting for a monitor lock
* to enter a synchronized block/method or
* reenter a synchronized block/method after calling
* {@link Object#wait() Object.wait}.
*/
BLOCKED,
/**
* Thread state for a waiting thread.
* A thread is in the waiting state due to calling one of the
* following methods:
* <ul>
* <li>{@link Object#wait() Object.wait} with no timeout</li>
* <li>{@link #join() Thread.join} with no timeout</li>
* <li>{@link LockSupport#park() LockSupport.park}</li>
* </ul>
*
* <p>A thread in the waiting state is waiting for another thread to
* perform a particular action.
*
* For example, a thread that has called {@code Object.wait()}
* on an object is waiting for another thread to call
* {@code Object.notify()} or {@code Object.notifyAll()} on
* that object. A thread that has called {@code Thread.join()}
* is waiting for a specified thread to terminate.
*/
WAITING,
/**
* Thread state for a waiting thread with a specified waiting time.
* A thread is in the timed waiting state due to calling one of
* the following methods with a specified positive waiting time:
* <ul>
* <li>{@link #sleep Thread.sleep}</li>
* <li>{@link Object#wait(long) Object.wait} with timeout</li>
* <li>{@link #join(long) Thread.join} with timeout</li>
* <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li>
* <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li>
* </ul>
*/
TIMED_WAITING,
/**
* Thread state for a terminated thread.
* The thread has completed execution.
*/
TERMINATED;
}
NEW
RUNNABLE
:运行状态、可运行状态和操作系统层面的阻塞状态都称为RUNNABLE,因此调用t1.start()
后线程就进入运行状态BLOCKED
:java中三种阻塞状态之一,后续讲完锁后会详解WATING
:java中三种阻塞状态之一,后续讲完锁后会详解TIMED_WAITING
:java中三种阻塞状态之一,t1.sleep()
会让线程进入该状态RERMINATED
下面用个程序演示这6种状态:
public class TestState2 {
public static void main(String[] args) throws InterruptedException {
new Thread(() -> {
FileReader.read(Constants.MP4_FULL_PATH);
while(true){
}
}, "t1").start();
System.out.println("ok");
}
@Test
public void test1() throws IOException {
Thread t1 = new Thread("t1") {
@Override
public void run() {
log.debug("running...");
}
};
Thread t2 = new Thread("t2") {
@Override
public void run() {
while(true) { // runnable
}
}
};
t2.start();
Thread t3 = new Thread("t3") {
@Override
public void run() {
log.debug("running...");
}
};
t3.start();
Thread t4 = new Thread("t4") {
@Override
public void run() {
synchronized (TestState2.class) {
try {
Thread.sleep(1000000); // timed_waiting,有等待时限
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
t4.start();
Thread t5 = new Thread("t5") {
@Override
public void run() {
try {
t2.join(); // waiting 没有等待时限
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
t5.start();
Thread t6 = new Thread("t6") {
@Override
public void run() {
// t4给对象TestState2上了锁,导致t6拿不到锁,陷入无限等待状态
synchronized (TestState2.class) { // blocked
try {
Thread.sleep(1000000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
t6.start();
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.debug("t1 state {}", t1.getState());
log.debug("t2 state {}", t2.getState());
log.debug("t3 state {}", t3.getState());
log.debug("t4 state {}", t4.getState());
log.debug("t5 state {}", t5.getState());
log.debug("t6 state {}", t6.getState());
System.in.read();
}
}
09:54:06.026 c.TestState2 [t3] - running...
09:54:06.527 c.TestState2 [main] - t1 state NEW
09:54:06.528 c.TestState2 [main] - t2 state RUNNABLE
09:54:06.528 c.TestState2 [main] - t3 state TERMINATED
09:54:06.528 c.TestState2 [main] - t4 state TIMED_WAITING
09:54:06.528 c.TestState2 [main] - t5 state WAITING
09:54:06.528 c.TestState2 [main] - t6 state BLOCKED
最后
以上就是聪慧早晨为你收集整理的Java并发编程(二)——线程中的常见方法(join,yield,sleep等)和线程状态的全部内容,希望文章能够帮你解决Java并发编程(二)——线程中的常见方法(join,yield,sleep等)和线程状态所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复