Thread类的基本用法

Thread

  • 小小的回顾
  • 初识线程
  • 创建线程
    • 继承Thread类
    • 构造方法中传入Runnable实例
    • 匿名内部类创建Thread子类
    • 匿名内部类实现Runnable接口实例
  • 线程休眠
  • 线程中断
  • 线程等待
  • 获取线程实例

小小的回顾

上一篇博客中,阿涛大概领着大家认识了一下进程.进程就是任务管理器中一个个打开的程序,比如我们现在打开了一个腾讯QQ,我们既需要用它来聊天,也需要用它来刷空间,显然这是两个不同的逻辑,我们需要通过不同的代码来实现这个逻辑.一个QQ是有好多好多的功能的,如果我们就按照代码的书写顺序来运行,那么极有可能同一时间能过实现的功能是极其有限的,此时此刻老祖宗留下的至理名言起到了关键作用:人多力量大!

初识线程

我们可以把进程想象成一个个工厂,那么线程就是工厂里面的一个个流水线,为了提高工厂的效率,我们就需要在条件允许的情况下设置流水线.我们现在的电脑都是多核心多线程的,核心越多线程越多,电脑并发编程的能力越强.
刚才说到的,进程也能做到线程能做到的事情,那么我们为什么不多搞几个线程呢?
其实线程本身不叫线程,线程的真名是"轻量级进程",只不过我们程序猿在学习工作的时候需要进行区分而已,"轻量级"主要体现在除了第一次创建线程我们需要有一定的开销,由于个个线程是共用一块地址空间的,所以之后的创建线程就有现成的地方了,同理,调用和销毁进程的开销是要大于调用和销毁线程的!
还是类比工厂和流水线,要建厂的开销一定比在厂里面增加一条流水线要来的大吧!!

创建线程

其实只要我们psvm了一下,我们就创建好了一个main线程,所以一般一个java程序一定至少会有一个线程.

继承Thread类

    static class MyThread extends Thread{
        @Override//继承Thread类并且需要重写里面的run方法,可以把run方法看成是Thread里面的main方法
        public void run(){
            System.out.println("hi,t");
        }
    }
    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        myThread.start();//创建好MyThread实例只是做好了准备工作,此时程序里面并没有真正创建好一个线程
        //就好比跑步比赛的预备,各就各位
        //只有在这里调用了start()方法,线程才是真正地创建了出来
        System.out.println("hi,main");
    }

构造方法中传入Runnable实例

    static class MyRunnable implements Runnable {
        @Override//创建MyRunnable类实现Runnable接口,这里的重写人家是会强制你去做的
        public void run() {
            System.out.println("hi,t");
        }
    }
    public static void main(String[] args) {
        Runnable runnable = new MyRunnable();
        Thread t = new Thread(runnable);//在构造方法里面传入MyRunnable实例
        t.start();//还是需要调用start方法的
        System.out.println("hi,main");
    }

匿名内部类创建Thread子类

    public static void main(String[] args) {
        Thread t = new Thread(){//这种写法相当于是创建了一个一次性的Thread类的子类,并且重写其中的run方法
            //匿名内部类主要就是应用于一次性使用的场景下,没有类名但是能且只能使用这一次
            @Override
            public void run(){
                System.out.println("hi,t");
            }
        };
        t.start();
        System.out.println("hi,main");
    }

匿名内部类实现Runnable接口实例

    public static void main(String[] args) {
        Thread t = new Thread(()->{
            System.out.println("hi,t"); 
        });
        t.start();
        System.out.println("hi,main");
    }

这个基本上就是最简洁的版本了,以后也是使用的比较多的一个版本.

线程休眠

休眠就是睡觉,睡觉就是sleep,休眠模式下面的线程是没有办法申请CPU资源的,当我们不希望线程快速上CPU时就会搞这一套:

    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(()->{
            System.out.println("hi,t");
        });

        t.start();
        Thread.sleep(100);//sleep后面加上一个休眠的时间,到点了线程就会起床干事情
        System.out.println("hi,main");
    }

Thread类的基本用法_第1张图片
之前我们说过,一般来说t线程是要在main线程之后才跑起来的,但是此时我们加上了Thread.sleep() ,一切就不一样了,在我们看来100ms可能就是白驹过隙,我打字的功夫就不知道过去多少个100ms了,但是在计算机看来100ms就是沧海桑田,等待这一会的功夫已经跑了N行代码了,所以在main线程休眠的这段时间里面,y线程跑起来并执行了里面的run方法

线程中断

想要中断一个进程,就是要让进程里面的run方法跑完,就以循环为例子:

    public static boolean flag = false;//如果这里不是一个成员变量,好像是不可以在lambda表达式里面使用的,所以要设置一个成员变量

    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(()->{
            while(!flag){
                System.out.println("hi,t");
            }
            System.out.println("线程结束");//如果循环结束了,跑到了这一行,只要打印完这一行那么线程也就结束了
        });

        t.start();
        Thread.sleep(100);//不加这个的话main线程执行的比t线程快,是不会有打印内容的
        flag = true;
        System.out.println("main结束");
    }

Thread类的基本用法_第2张图片
从结果上来看,确实达到了我们的预期效果,只不过这里我们是使用了外部变量来控制线程的,其实我们的Thread中是有内置的标志位的:

    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(()->{
           while(!currentThread().isInterrupted()){
               System.out.println("hi,t");
           }
            System.out.println("t结束");
        });

        t.start();
        Thread.sleep(10);
        t.isInterrupted();
        System.out.println("main结束");
    }

Thread类的基本用法_第3张图片
当我们需要interrupt一个正在sleep或者其他阻塞状态下的线程是,就会通过抛出异常的方式唤醒线程,有没有一点你永远没办法叫醒一个装睡的人的意思呢?

线程等待

 	public static int num = 0;
    //main是一个静态方法,非静态的属性方法是没法在静态函数中使用的
    //lambda表达式中是不可以使用普通变量的,所以我们把这里的变量给调整成成员变量
    public static void main(String[] args) {
        Thread t = new Thread(()->{
            for (int i = 0; i < 5; i++) {
                num++;
            }
        });
        t.start();
        System.out.println(num);
    }

在这里插入图片描述按照我们的预设,应该是在t线程里面完成了自增操作啊,为什么最后我们显示出来的还是0嘞?
我们之前应该说过,因为创建线程是需要一定的系统开销的,所以main线程一般情况是是要比其他线程提前执行的,所以我们在sout的时候,num还是0.要是想要达到我们的预期,就需要让main线程等待t线程执行完,这时候,就涉及到了线程的等待,这里我们先讲解一个join()方法,后期我们再讲解别的方法:

Thread类的基本用法_第4张图片
那么我们在main线程中调用了一个t.join()这就是让main线程等待t线程执行完然后再执行,这下子我们就得到了自己想要的.

获取线程实例

    public static void main(String[] args) {
        Thread t = new Thread(()->{
            System.out.println(Thread.currentThread());
        });
        t.start();

        System.out.println(Thread.currentThread());
    }

我们可以直接通过currentThread()这个静态方法来获取到当前线程的实例,在t线程中拿到的就是t线程,在main线程中拿到的就是main线程的实例:
Thread类的基本用法_第5张图片
看样子Thread类是重写了toString()方法,同时我们可以看到实例方法还是有不少的,在学习的过程中可以随时使用.
好了,那么今天关于线程的学习就到这里了,希望我的这篇博客可以帮助到大家.
百年大道,你我共勉!

你可能感兴趣的:(java,jvm)