juc并发编程(二)

栈与栈帧
在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
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值