Java线程常用方法分析

本文详细分析了Java线程的常用方法,包括start与run的区别,sleep与yield的使用,join的基本应用及其等待多个结果的场景,以及interrupt方法的作用。强调了直接调用run不会启动新线程,start则会;sleep会使线程进入等待状态,而yield让当前线程让出执行权;join用于同步,使主线程等待子线程完成;interrupt能中断线程,不同情况会清除或保留打断状态;并提醒避免使用已过时的线程方法。
摘要由CSDN通过智能技术生成

Java线程常用方法分析

1.start 与 run

1.run

//----------调用 run-----------------------
public static void main(String[] args) {
	Thread t1 = new Thread("t1") {
	@Override
	public void run() {
		log.debug(Thread.currentThread().getName());
		FileReader.read(Constants.MP4_FULL_PATH);
	}
};
t1.run();
log.debug("do other things ...");
}

//-------------------输出-----------------
19:39:14 [main] c.TestStart - main
19:39:14 [main] c.FileReader - read [1.mp4] start ...
19:39:18 [main] c.FileReader - read [1.mp4] end ... cost: 4227 ms
19:39:18 [main] c.TestStart - do other things ...

程序仍在 main 线程运行, FileReader.read() 方法调用还是同步

2.start

将上述代码的 t1.run() 改为

t1.start();

//----------输出----------------
19:41:30 [main] c.TestStart - do other things ...
19:41:30 [t1] c.TestStart - t1
19:41:30 [t1] c.FileReader - read [1.mp4] start ...
19:41:35 [t1] c.FileReader - read [1.mp4] end ... cost: 4542 ms

程序在 t1 线程运行, FileReader.read() 方法调用是异步

小结:

  • 直接调用 run 是在主线程中执行了 run,没有启动新的线程
  • 使用 start 是启动新的线程,通过新的线程间接执行 run 中的代码

2.sleep 与 yield

  1. sleep
  • 调用 sleep 会让当前线程从 Running 进入 Timed Waiting 状态(阻塞)
  • 其它线程可以使用 interrupt 方法打断正在睡眠的线程,这时 sleep 方法会抛出 InterruptedException
  • 睡眠结束后的线程未必会立刻得到执行,进入阻塞队列竞争cpu使用权
  • 建议用 TimeUnit 的 sleep 代替 Thread 的 sleep 来获得更好的可读性

2.yield

  • 调用 yield 会让当前线程从Running 进入 Runnable就绪状态,然后调度执行其它线程
  • 具体的实现依赖于操作系统的任务调度器

3.线程优先级

  • 线程优先级会提示(hint)调度器优先调度该线程,但它仅仅是一个提示,调度器可以忽略它
  • 如果 cpu 比较忙,那么优先级高的线程会获得更多的时间片,但 cpu 闲时,优先级几乎没作用
Runnable task1 = () -> {
	int count = 0;
	for (;;) {
		System.out.println("---->1 " + count++);
	}
};
Runnable task2 = () -> {
	int count = 0;
	for (;;) {
	// Thread.yield();
	System.out.println(" ---->2 " + count++);
	}
};
Thread t1 = new Thread(task1, "t1");
Thread t2 = new Thread(task2, "t2");
// t1.setPriority(Thread.MIN_PRIORITY);
// t2.setPriority(Thread.MAX_PRIORITY);
t1.start();
t2.start();

3.join

3.1基本使用

static int r = 0;
Thread t1;
public static void main(String[] args) throws InterruptedException {
	test1();
	t1.join();//等待t1线程执行结束
}
private static void test1() throws InterruptedException {
	log.debug("开始");
	t1 = new Thread(() -> {
	log.debug("开始");
	sleep(1);
	log.debug("结束");
	r = 10;
	});
	t1.start();
	log.debug("结果为:{}", r);
	log.debug("结束");
}

分析:

  • 因为主线程和线程 t1 是并行执行的,t1 线程需要 1 秒之后才能算出 r=10
  • 而主线程一开始就要打印 r 的结果,所以只能打印出 r=0

解决方法:

  • 用 sleep 行不行?为什么?
    • 不可行,如果让main睡眠,因为不知到t1要执行多长时间,没法设置.
  • 用 join,加在 t1.start() 之后即可

以调用方角度来讲,如果

  • 需要等待结果返回,才能继续运行就是同步
  • 不需要等待结果返回,就能继续运行就是异步
1s后
t1终止
main
t1.start
r=10
t1.join

3.2等待多个结果

static int r1 = 0;
static int r2 = 0;
public static void main(String[] args) throws InterruptedException {
	test2();
}
private static void test2() throws InterruptedException {
	Thread t1 = new Thread(() -> {
		sleep(1);
		r1 = 10;
	});
	Thread t2 = new Thread(() -> {
		sleep(2);
		r2 = 20;
	});
	long start = System.currentTimeMillis();
	t1.start();
	t2.start();
	t1.join();
	t2.join();
	long end = System.currentTimeMillis();
	log.debug("r1: {} r2: {} cost: {}", r1, r2, end - start);
}

分析如下
第一个 join:等待 t1 时, t2 并没有停止, 而在运行
第二个 join:1s 后, 执行到此, t2 也运行了 1s, 因此也只需再等待 1s

3.3有时效的join

1.等够时间

static int r1 = 0;
static int r2 = 0;
public static void main(String[] args) throws InterruptedException {
	test3();
}
public static void test3() throws InterruptedException {
	Thread t1 = new Thread(() -> {
		sleep(1);
		r1 = 10;
	});
	long start = System.currentTimeMillis();
	t1.start();
	// 线程执行结束会导致 join 结束
	t1.join(1500);
	long end = System.currentTimeMillis();
	log.debug("r1: {} r2: {} cost: {}", r1, r2, end - start);
}

//----------输出--------------
20:48:01.320 [main] c.TestJoin - r1: 10 r2: 0 cost: 1010

2.没有等够时间

static int r1 = 0;
static int r2 = 0;
public static void main(String[] args) throws InterruptedException {
	test3();
}
public static void test3() throws InterruptedException {
	Thread t1 = new Thread(() -> {
	sleep(2);
	r1 = 10;
});
long start = System.currentTimeMillis();
t1.start();
// 线程执行结束会导致 join 结束
t1.join(1500);
long end = System.currentTimeMillis();
log.debug("r1: {} r2: {} cost: {}", r1, r2, end - start);
}

//--------输出----------------------
20:52:15.623 [main] c.TestJoin - r1: 0 r2: 0 cost: 1502

4.interrupt 方法详解

1.打断 sleep,wait,join 的线程

这几个方法都会让线程进入阻塞状态
打断 sleep 的线程, 会清空打断状态,以 sleep 为例

private static void test1() throws InterruptedException {
Thread t1 = new Thread(()->{
	sleep(1);
}, "t1");
t1.start();
sleep(0.5);
t1.interrupt();
log.debug(" 打断状态: {}", t1.isInterrupted());
}

//---------输出---------------
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at java.lang.Thread.sleep(Thread.java:340)
at java.util.concurrent.TimeUnit.sleep(TimeUnit.java:386)
at cn.itcast.n2.util.Sleeper.sleep(Sleeper.java:8)
at cn.itcast.n4.TestInterrupt.lambda$test1$3(TestInterrupt.java:59)
at java.lang.Thread.run(Thread.java:745)
21:18:10.374 [main] c.TestInterrupt - 打断状态: false

2.打断正常运行的线程

打断正常运行的线程, 不会清空打断状态

private static void test2() throws InterruptedException {
Thread t2 = new Thread(()->{
	while(true) {
		Thread current = Thread.currentThread();
		boolean interrupted = current.isInterrupted();
		if(interrupted) {
			log.debug(" 打断状态: {}", interrupted);
		break;
	}
	}
}, "t2");
t2.start();
sleep(0.5);
t2.interrupt();
}

//---------输出---------------
20:57:37.964 [t2] c.TestInterrupt - 打断状态: true

3.打断 park 线程

打断 park 线程, 不会清空打断状态

private static void test3() throws InterruptedException {
	Thread t1 = new Thread(() -> {
		log.debug("park...");
		LockSupport.park();
		log.debug("unpark...");
		log.debug("打断状态:{}", Thread.currentThread().isInterrupted());
	}, "t1");
t1.start();
sleep(0.5);
t1.interrupt();
}

//------------输出------------------
21:11:52.795 [t1] c.TestInterrupt - park...
21:11:53.295 [t1] c.TestInterrupt - unpark...
21:11:53.295 [t1] c.TestInterrupt - 打断状态:true

如果打断标记已经是 true, 则 park 会失效

private static void test4() {
	Thread t1 = new Thread(() -> {
	for (int i = 0; i < 5; i++) {
		log.debug("park...");
		LockSupport.park();
		log.debug("打断状态:{}", Thread.currentThread().isInterrupted());
	}
});
t1.start();
sleep(1);
t1.interrupt();
}

//-----------输出----------------
21:13:48.783 [Thread-0] c.TestInterrupt - park...
21:13:49.809 [Thread-0] c.TestInterrupt - 打断状态:true
21:13:49.812 [Thread-0] c.TestInterrupt - park...
21:13:49.813 [Thread-0] c.TestInterrupt - 打断状态:true
21:13:49.813 [Thread-0] c.TestInterrupt - park...
21:13:49.813 [Thread-0] c.TestInterrupt - 打断状态:true
21:13:49.813 [Thread-0] c.TestInterrupt - park...
21:13:49.813 [Thread-0] c.TestInterrupt - 打断状态:true
21:13:49.813 [Thread-0] c.TestInterrupt - park...
21:13:49.813 [Thread-0] c.TestInterrupt - 打断状态:true

可以使用 Thread.interrupted()清除打断状态

5.不推荐的方法

还有一些不推荐使用的方法,这些方法已过时,容易破坏同步代码块,造成线程死锁

方法名static说明
stop()停止线程运行
suspend()挂起(暂停)线程
resume()恢复线程运行
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值