栈与栈帧
在jvm之中是由堆、栈、方法区所组成,其中每一个线程启动,就会分配一块内存
- 每个栈是由多个栈帧组成,对应着每次调用的方法所占用的内存
- 每一个线程只有一个活动的线帧,对应着正在执行的那个方法
package juc;
public class TestFrame {
public static void main(String[] args) {
method1(10);
}
public static void method1(int x){
int y=x+1;
Object m=method2();
System.out.println(m);
}
public static Object method2(){
Object n=new Object();
return n;
}
}
在method1(10)哪里打开debug模式
执行了两步
最后一块内存被释放掉了,再依次释放
多个线程调用内存
package juc;
public class TestFrame {
public static void main(String[] args) {
Thread t1=new Thread(){
@Override
public void run(){
method1(20);
}
};
t1.setName("t1");
t1.start();
method1(10);
}
public static void method1(int x){
int y=x+1;
Object m=method2();
System.out.println(m);
}
public static Object method2(){
Object n=new Object();
return n;
}
}
改为模式,分别在两个method1()出进行debug,就可以看见两个线程的运行
线程的上下文切换
因为一些原因导致cpu不再执行当前的线程,转而执行另一个线程的代码
1.线程的cpu时间片用完
2.垃圾回收
3.有更高的优先级的线程需要运行
4.线程自己调用了 sleep、yield、wait、join、park、synchronized、lock等方法
(1)当发生上下文发生时,需要保存操作系统的当前线程的状态,并恢复另一个线程状态,java之中对应的概念就是程序计数器,它的作用是记住下一条jvm的指令执行地址,是线程私有的
(2)状态包括程序计数器,虚拟机每个栈帧的信息,如局部变量,操作数栈,返回地址等。
(3)线程上下文切换(Context Switch)频繁发生会影响性能
###线程数不是越高越好,当线程过多的时候会频繁切换,会影响性能
线程的方法
start()
package juc;
import lombok.extern.slf4j.Slf4j;
import sun.security.pkcs11.wrapper.Constants;
import java.io.FileReader;
@Slf4j(topic = "juc.test4")
public class Test4 {
public static void main(String[] args) {
Thread t1=new Thread("t1"){
@Override
public void run(){
log.debug("running....");
}
};
System.out.println(t1.getState());
t1.start();
System.out.println(t1.getState());
}
}
NEW
RUNNABLE
start()不能被多次调用
sleep()
1.调用sleep()会让当前的线程从running状态进入Timed waiting状态
2.其他线程可以使用interrupt方法打断正在睡眠的线程,这时sleep 方法会抛出InterrupteException
3.睡眠结束后的线程未必立刻得到执行
4.建议用TimeUnit的sleep代替thead的sleep来获得更好的可读性
package juc;
import lombok.extern.slf4j.Slf4j;
import sun.security.pkcs11.wrapper.Constants;
import java.io.FileReader;
@Slf4j(topic = "juc.test4")
public class Test4 {
public static void main(String[] args) {
Thread t1=new Thread("t1"){
@Override
public void run(){
try {
Thread.sleep(2000);
}catch (InterruptedException e){
e.printStackTrace();
}
}
};
t1.start();
log.debug("t1 state:{}",t1.getState());
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.debug("t1.state:{}",t1.getState());
}
}
19:25:44.886 [main] DEBUG juc.test4 - t1 state:RUNNABLE
19:25:45.421 [main] DEBUG juc.test4 - t1.state:TIMED_WAITING
Thread.interrupt(); //线程中断
Timeunit可以指定睡眠单位
部分源码
public enum TimeUnit {
/**
* Time unit representing one thousandth of a microsecond
*/
NANOSECONDS {
public long toNanos(long d) { return d; }
public long toMicros(long d) { return d/(C1/C0); }
public long toMillis(long d) { return d/(C2/C0); }
public long toSeconds(long d) { return d/(C3/C0); }
public long toMinutes(long d) { return d/(C4/C0); }
public long toHours(long d) { return d/(C5/C0); }
public long toDays(long d) { return d/(C6/C0); }
public long convert(long d, TimeUnit u) { return u.toNanos(d); }
int excessNanos(long d, long m) { return (int)(d - (m*C2)); }
},
例如:TimeUnit.SECONDS.sleep(1000);
yield()
1.调用yield会让当前线程从running进入runable就绪状态,然后调度其他线程
2.具体的实现依赖于操作系统任务调度器
任务调度器可能再次分配此线程
线程优先级
源码:
public final static int MIN_PRIORITY = 1;
/**
* The default priority that is assigned to a thread.
*/
public final static int NORM_PRIORITY = 5;
/**
* The maximum priority that a thread can have.
*/
public final static int MAX_PRIORITY = 10;
线程的优先级最低为MIN_PRIORITY 最高MAX_PRIORITY 默认NORM_PRIORITY
分配到优先级提示调度器调度该线程,但它仅仅只是一个提示,调度器可以忽略它
如果cpu比较忙,那么优先级高的线程会获得更多的时间片,但是cpu闲的时候,优先级几乎没有作用。(任务调度器决定)yeild如果让给其他线程,任务调度器可能会再次分给你任务,
package juc;
import lombok.extern.slf4j.Slf4j;
@Slf4j(topic = "juc.test6")
public class test6 {
public static void main(String[] args) {
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();
/**
* 不添加任何条件 数据很接近,线程调用的次数差不多
* 线程2加入yield() 绝大多数线程1在运行,线程2运行的时间很少
*设置优先级,注释yield() 大部分时间都是优先级高的t2再跑
*/
}
}
我用朋友的电脑,跑两个死循环就跑不动了,垃圾电脑**********
防止cpu 100%
while (true){
try {
Thread.sleep(50);
}catch (InterruptedException e){
e.printStackTrace();
}
}
在没有利用cpu的计算的时候,不要让while(true)浪费 cpu的时间,使用sleep或者yield方法来把使用权给与其他程序 ,sleep 适用于无锁环境,wait和条件变量也可以有同样的效果,不过都需要需要加锁
package juc;
import lombok.extern.slf4j.Slf4j;
@Slf4j(topic = "juc.test7")
public class test7 {
static int r=0;
public static void main(String[] args) throws InterruptedException {
method1();
}
private static void method1() throws InterruptedException {
log.debug("开始");
Thread t1=new Thread(()->{
log.debug("开始");
try {
Thread.sleep(1);
log.debug("结束");
r=10;
} catch (InterruptedException e) {
e.printStackTrace();
}
},"t1");
t1.start();
log.debug("结果为{}",r);
log.debug("结束");
Thread.sleep(10);
log.debug("结果为{}",r);
}
}
运行结果
00:21:13.978 [main] DEBUG juc.test7 - 开始
00:21:14.447 [main] DEBUG juc.test7 - 结果为0
00:21:14.451 [main] DEBUG juc.test7 - 结束
00:21:14.581 [main] DEBUG juc.test7 - 结果为0
00:21:14.622 [t1] DEBUG juc.test7 - 开始
00:21:14.739 [t1] DEBUG juc.test7 - 结束
当把主线程sleep改为1000时
00:25:06.085 [main] DEBUG juc.test7 - 开始
00:25:06.332 [main] DEBUG juc.test7 - 结果为0
00:25:06.339 [main] DEBUG juc.test7 - 结束
00:25:06.416 [t1] DEBUG juc.test7 - 开始
00:25:06.419 [t1] DEBUG juc.test7 - 结束
00:25:07.387 [main] DEBUG juc.test7 - 结果为10
分析
因为主线程和线程t1并行执行的,t1需要在1s之后才能计算出r=10
而主线程只能打印出r=0;
jion()
把其他线程加入当前线程,等加入线程之后完成之后,才执行当前线程。(这尼玛不就是老好人)
改一些代码:
t1.start();
t1.join();
log.debug("结果为{}",r);
log.debug("结束");
// Thread.sleep(1000);
log.debug("结果为{}",r);
结果
00:30:50.921 [main] DEBUG juc.test7 - 开始
00:30:51.059 [t1] DEBUG juc.test7 - 开始
00:30:51.077 [t1] DEBUG juc.test7 - 结束
00:30:51.077 [main] DEBUG juc.test7 - 结果为10
00:30:51.081 [main] DEBUG juc.test7 - 结束
00:30:51.081 [main] DEBUG juc.test7 - 结果为10
mian线程开始结束之后,t1线程才开始运行。
jion(long n)
@Slf4j(topic = "juc.test8")
public class test8 {
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(()->{
try {
Thread.sleep(2000);
r1=10;
} catch (InterruptedException e) {
e.printStackTrace();
}
});
long start=System.currentTimeMillis();
log.debug("jion begin");
t1.join(1500);
long end=System.currentTimeMillis();
log.debug("r1:{} r2:{} cost:{}",r1,r2,end-start);
}
}
13:47:46.612 [main] DEBUG juc.test8 - jion begin
13:47:46.616 [main] DEBUG juc.test8 - r1:0 r2:0 cost:7