Java笔记整理 —— 线程基础知识

线程相关概念

程序

是为了完成特定任务,用某种语言编写的一组指令的集合。简单地说:就是我们写的代码。

进程

线程

其它

    public static void main(String[] args) {
        Runtime runtime = Runtime.getRuntime();
        //获取当前电脑的cpu数量
        int cpuNums = runtime.availableProcessors();
        System.out.println(cpuNums);
    }

线程基本使用

创建线程的两种方式

1. 继承Thread 类,重写 run方法。

    当一个类继承了 Thread 类,该类就可以当作线程使用。Thread类实现了 Runnable接口的run方法,我们会重写run,写上自己的业务代码。 下面是Thread类的run方法:

class AA extends Thread{
    @Override
    public void run() { //重写run方法
        while(true){
            System.out.println("线程进行中");
            try{
                //线程每输出一次,休眠一秒(不用try-catch会报异常)
                Thread.sleep(1000);
            }catch (InterruptedException e){
                e.printStackTrace();
            }
        }
    }
}
    public static void main(String[] args) throws InterruptedException { //main线程
        AA aa = new AA();
        aa.start(); //启动子线程 Thread-0  下面会解释为什么不调用run
        //说明:当main线程启动一个子线程 Thread-0,主线程不会阻塞,会继续执行。
        //这时,主线程和子线程是交替进行的
        System.out.println("主线程继续执行" + Thread.currentThread().getName());
        //获取线程名
        for(int i=0;i<10;i++){
            System.out.println("主线程" + i);
            Thread.sleep(1000); //alt + enter可以抛出异常
        }
    }

 注:子线程也可以开启另一个子线程。

    这里有一个值得注意的问题:AA类重写的是run方法,但是在main中调用的却是start,这是为什么呢? 因为只有start方法才能启动线程,进而执行AA类的run方法。这时main和Thread-0是同步执行的。  如果调用run方法,这就是一个普通的方法,没有真正启动一个线程,因此会把run方法执行完再执行下面的内容,造成阻塞。 

    start会调用一个start0方法(这才是启动线程的方法),下面是start的流程图。

2. Runnable创造线程

   当一个类已经有继承的父类时,就不能再继承Thread类了,这时可以实现Runnable接口来创造一个线程。

3. 当有多个线程共享资源时,一般用Runnable创造线程。 

class AA implements Runnable{
    @Override
    public void run() {
        while(true){
            System.out.println("线程进行中");
            try{
                //线程每输出一次,休眠一秒(不用try-catch会报异常)
                Thread.sleep(1000);
            }catch (InterruptedException e){
                e.printStackTrace();
            }
        }
    }
}
    public static void main(String[] args) throws InterruptedException {
        AA aa = new AA();
        //aa.strat();  报错,因为没有继承Thread,不能直接调用start。
        Thread thread1 = new Thread(aa);//创建一个Thread,把类放入
        Thread thread2 = new Thread(aa);//多个资源共享线程(同一个对象创造多个线程)
        thread1.start();
        thread2.start();
    }

模拟Thread类:

class ThreadProxy implements Runnable{
    private Runnable target = null; //类型是Runnable
    @Override
    public void run() {
        if(target!=null){
            target.run();
        }
    }

    public ThreadProxy(Runnable target) {
        this.target = target;
    }

    public void start(){
        start0();
    }
    public void start0(){
        run();
    }
}

JConsole

这个工具可以监控线程执行情况。

1. 运行main线程,然后打开Terminal(左下角),然后输入 JConsole(不区分大小写)打开工具。

2. 选择对应类的本地进程(可能有好多个,选第一个就行了)。

3. 选择不安全的连接。 

4. 这样可以看到,有main线程与Thread-0线程。 

对线程的理解 

 

  P587:经典多线程售票问题。到最后会出现票剩-2张等情况。这是因为各线程在判断是否票数为0时,基本上是同时判断的,因此可能票有1张,有三个线程,都认为票没卖完,又出售了三张,导致最后剩-2张。

线程终止

    所谓使用变量,其实就是用变量控制run方法的循环(比如t = true),然后在main方法中对其进行修改。 

class AA implements Runnable{
    private boolean loop = true;  //控制run的进行
    @Override
    public void run() {
        while(loop){
            System.out.println("青眼白龙");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public void setLoop(boolean loop) { //loop的Setter的方法
        this.loop = loop;
    }
}
    public static void main(String[] args) throws InterruptedException {
        AA aa = new AA();
        new Thread(aa).start(); //开启线程
        Thread.sleep(3000);
        aa.setLoop(false); //休息3秒后修改loop导致线程终止
    }

线程常用方法

注:这些都是Thread类的方法。

    有几个需要注意的点:1. start底层(start0)会创建新的线程调用run,run只是一个普通方法,不会启动新线程。 2. sleep是静态方法。3. 线程优先级的范围:

  4. 重点理解 interrupt 唤醒线程(注意不是中断线程!):它并不会真正的结束线程,一般用于中断正在休眠的线程(相当于吵醒睡觉中的线程)。 InterruptedException就是用来接收 interrupt的。

class AA implements Runnable{
    private boolean loop = true;
    @Override
    public void run() {
        while(loop){
            System.out.println("青眼白龙");
            try {
                Thread.sleep(10000); //休眠10秒
            } catch (InterruptedException e) {//InterruptedException是捕获到一个中断异常
           //当该线程执行到一个interrupt方法时,就会catch一个异常,可以加入自己的业务代码
                System.out.println(Thread.currentThread().getName() + "被interrupt了");
            }
        }
    }

    public void setLoop(boolean loop) {
        this.loop = loop;
    }
}

 Thread.currentThread().getName()  获取当前线程名 

    public static void main(String[] args) throws InterruptedException {
        AA aa = new AA();
        Thread thread = new Thread(aa);
        thread.start();
        Thread.sleep(3000);
        thread.interrupt(); //主线程休息3秒后吵醒线程
    }

 

注:如果CPU资源充足,yield就不会成功,而join一旦调用,CPU就全力执行调用的线程,其它线程就停止了,所以一定成功。 

    public static void main(String[] args) throws InterruptedException {
        AA aa = new AA();
        Thread thread = new Thread(aa);
        thread.start();
        for(int i=0;i<100;i++){
            System.out.println("主线程执行第" + i +"次");
            if(i==10){
                thread.join();  //这时全力执行 aa对应的线程
            }
        }
    }

用户线程和守护线程

把一个线程设定成守护线程:  这样当其他用户线程都结束后,守护线程自动结束。

        AA aa = new AA();
        Thread thread = new Thread(aa);
        thread.setDaemon(true); //将子线程设置为守护线程

线程的状态

   原本线程的状态总共有六个,但是可以被细分成七个。 也就是 Runnable状态被分成了 Ready和Running两个状态。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值