Java并发编程(2)

1.线程运行的原理

我们都知道 JVM 中由堆、栈、方法区所组成,其中栈内存是给谁用的呢?
其实就是线程,每个线程启动后,虚拟机就会为其分配一块栈内存。
每个栈由多个栈帧(Frame)组成,对应着每次方法调用时所占用的内存
每个线程只能有一个活动栈帧,对应着当前正在执行的那个方法

2 线程之间的上下文切换

因为以下一些原因导致 cpu 不再执行当前的线程,转而执行另一个线程的代码
1.线程的 cpu 时间片用完
2.垃圾回收
3.有更高优先级的线程需要运行
4.线程自己调用了 sleep、yield、wait、join、park、synchronized、lock 等方法
当 Context Switch 发生时,需要由操作系统保存当前线程的状态,并恢复另一个线程的状态,Java 中对应的概念
就是程序计数器(Program Counter Register),它的作用是记住下一条 jvm 指令的执行地址,是线程私有的
状态包括程序计数器、虚拟机栈中每个栈帧的信息,如局部变量、操作数栈、返回地址等
Context Switch 频繁发生会影响性能

3.线程的状态

Java 中线程状态是用 6enum 表示,分别为:
NEW, RUNNABLE, BLOCKED, WAITING,
TIMED_WAITING, TERMINATED

4.线程常见的API

1. start()方法:启动线程,使线程进入就绪状态,等待CPU时间片分配,
   一旦得到分配,会执行代码,每个线程对象的
   start方法只能调用一次,如果调用了多次会出现
   IllegalThreadStateException
2.run()方法,线程启动后要执行的方法,如果在构造 Thread 对象时传递了 Runnable 参数,则
  线程启动后会调用 Runnable 中的 run 方法,否则默
  认不执行任何操作。但可以创建 Thread 的子类对象,
  来覆盖默认行为
3.join()方法 :等待线程运行结束
4.join(long n):等待线程运行结束,最多等待 n毫秒
5.getName():获取线程名
6.setName(String):修改线程名
7.getPriority():获取线程优先级
8.setPriority(int):java中规定线程优先级是1~10 的整数,较大的优先级
  能提高该线程被 CPU 调度的机率
9.getState() :获取线程状态
10.sleep(long n):让当前执行的线程休眠n毫秒,休眠时让出 cpu
   的时间片给其它线程

4.线程的start方法与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() 方法调用还是同步的

将上述代码的 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 中的代码

5. sleep 与 yield

sleep
1. 调用 sleep 会让当前线程从 Running 进入 Timed Waiting 状态(阻塞)
2. 其它线程可以使用 interrupt 方法打断正在睡眠的线程,这时 sleep 方法会抛出 InterruptedException
3. 睡眠结束后的线程未必会立刻得到执行
4. 建议用 TimeUnit 的 sleep 代替 Thread 的 sleep 来获得更好的可读性
yield
1. 调用 yield 会让当前线程从 Running 进入 Runnable 就绪状态,然后调度执行其它线程
2. 具体的实现依赖于操作系统的任务调度器

6.线程优先级
线程优先级会提示(hint)调度器优先调度该线程,但它仅仅是一个提示,调度器可以忽略它
如果 cpu 比较忙,那么优先级高的线程会获得更多的时间片,但 cpu 闲时,优先级几乎没作用;

7.join 方法详解

下面的代码执行,打印 r 是什么?static int r = 0;
public static void main(String[] args) throws InterruptedException {
 	test1();
}
private static void test1() throws InterruptedException {
		 log.debug("开始");
		 Thread 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 行不行?为什么?
用 join,加在 t1.start() 之后即可
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值