如何创建并使用线程?

一个进程正在运行时至少会有一个线程在运行。每个线程都是一个独立的执行流,多个线程之间是“并发执行的”。

我们看第一个多线程程序:

public class Main {
    public static void main(String[] args) {
       while(true){
           System.out.println(Thread.currentThread().getName());
       }
    }
}

运行结果:

这里打印出的main其实就是一个名称为main的线程在执行main()方法中的代码。

下面我们可以使用jconsole命令来观察该线程,使用方法如下:

 

1,在控制台点击Terminal窗口直接输入jconsole命令

2,选择本地进程,选择并连接

3,查看要观测的线程

创建线程

1. 继承Thread类

java的JDK开发包自带了对多线程技术的支持,通过它可以便捷的进行多线程编程,继承Thread类是其中的一种重要方式,另一种则是实现Runnable接口。

我们首先创建一个自定义的线程类MyThread,此类应该继承Thread,并且重写其中的run方法,在run()方法中添加线程要执行的任务代码如下:

public class Main {
    static class MyThread extends Thread{
        @Override
        public void run() {
            System.out.println("线程运行!");
        }
    }
    public static void main(String[] args) {
       MyThread thread = new MyThread();
       thread.start();
        System.out.println("运行结束!");
    }
}

运行结果为:

在上述代码中使用start()方法来启动一个线程,在线程启动后会自动调用线程对象中的run()方法,他就是线程执行任务的入口。而就运行结果来看MyThread类中的run()方法执行时间要比输出“运行结束”的时间晚,这是因为start()方法在被调用时执行了多个步骤,其中还需要操作系统开辟内存而main线程在执行start()方法时不必等待这些步骤都执行完毕,而是立即执行start()方法之后的代码,由于输出“运行结束!”耗时比较少,所以在大多数情况下先输出“运行结束!”。

2. 实现Runnable接口

1.自定义MyRunable类实现Runable接口;

2.创建Thread类实例,调用Thread的构造方法时将MyRunnable对象作为target参数;

3.调用start()方法;

代码如下:

public class Main {
    static class MyRunnable implements Runnable{
        @Override
        public void run() {
            System.out.println("线程运行!");
        }
    }
    public static void main(String[] args){
        Thread thread = new Thread(new MyRunnable());
        thread.start();
    }
}

 3.对比这两中创建线程的方式:

1,继承Thread类,直接使用this就表示当前线程对像的引用;

2,实现Runnable接口,this表示的是MyRunnable的引用,要想获得当前线程对象的引用需要使用Thread.currentThread()方法;

3,由于Java的单继承模式,使用继承Thread类的方式来创建线程是有局限性的,为了打破这种限制就可以使用实现Runable接口的方式来实现多线程技术。

4.其他变形

4.1 匿名内部类创建Thread子类对象

代码:

public class Main {
    public static void main(String[] args){
        Thread t1 = new Thread(){
            @Override
            public void run() {
                System.out.println("线程运行!");
            }
        };
        t1.start();
    }
}

运行结果:

4.2 匿名内部类创建Runnable子类对象 

代码:

 Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("Runnable!");
            }
        });

4.3 lambda表达式创建Runnable子类对象

代码:

Thread t3 = new Thread(() -> System.out.println("lambda"));

Thread类及常见方法

每个执行流也需要有一个对象来描述,而Thread类对象就是用来描述一个线程的执行流的,jvm会将这些Thread对象组织起来,用于线程调度,线程管理。每一个线程都有一个唯一的Thread对象与之关联。

1.Thread的常见构造方法

使用Runnable对象作为target参数来创建线程我们已在上文中做了演示这里我们不再赘述,而String name参数就是为创建的线程命名。

2.Thread的几个常见属性 

 

1.ID是线程的唯一表示,不同的线程不会重复;

2.Name会在各种调试工具中被用到;

3.优先级高的线程理论上来说更容易被调度到;

4.状态表示当前线程所处的情况,我们在后文中详细解读;

5~6.线程的中断问题,我们在后文中举例说明;

7.是否存活指的是系统中的线程是否被销毁,可以简单的理解为run方法是否运行结束;

8.是否为守护线程即后台线程,jvm会在一个线程的所有非后台线程结束后才结束运行。

看下面这段代码和其运行结果:

public class Main {
    static class MyRunnable implements Runnable{
        @Override
        public void run() {
            for (int i = 0; i < 100; i++) {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    public static void main(String[] args){
        Thread thread = new Thread(new MyRunnable(),"线程1");
        thread.start();
        while(thread.isAlive()){
           System.out.println("线程ID:" + Thread.currentThread().getId() + "线程名字:" + Thread.currentThread().getName()
            + "状态:" + Thread.currentThread().getState());
        }
    }
}

 

2.启动一个线程的方法start() 

 当我们调用start方法时,程序首先通过jvm告诉操作系统创建Thread,操作系统再开辟内存并创建Thread线程对象,随后操作系统对Thread对象进行调度,以确定执行时机,最后Thread在操作系统中被成功执行,既让线程执行具体的任务,具有随机顺序执行的效果。如果调用run方法,直接由main主线程来调用run方法,也就是必须等run方法中的代码执行完毕后才可以执行后面的代码。

3.中断一个线程

1.自定义变量作为标志位(加上volatile关键字)

代码如下:

public class Main {
    static class MyRunnable implements Runnable{
        public volatile boolean isQuit = false;
        @Override
        public void run() {
            while(!isQuit){
                System.out.println("线程运行中");
            }
        }
    }
    public static void main(String[] args) throws InterruptedException {
        MyRunnable myRunnable = new MyRunnable();
        Thread thread = new Thread(myRunnable);
        thread.start();
        Thread.sleep(2000);
        myRunnable.isQuit = true;
        System.out.println("线程停止");
    }
}

运行结果:

2.使用Thread对象的interrupt()方法通知线程结束

①isInterrupted是用来判断对象关联的线程标志位是否被设置,调用后不清楚标志位

代码:

public class Main {
    static class MyRunnable implements Runnable{
        @Override
        public void run() {
            for (int i = 0; i < 10; i++) {
                //System.out.println(Thread.interrupted() + "" + i);
                System.out.println(Thread.currentThread().isInterrupted()  + "" + i);
            }
        }
    }
    public static void main(String[] args){
        MyRunnable myRunnable = new MyRunnable();
        Thread thread = new Thread(myRunnable);
        thread.start();
       thread.interrupt();
    }
}

运行结果为:

该线程被中断之后相当于按下了弹不起来的开关这种情况被称为‘‘不清除标志位”

②interrupted是用来判断当前线程的中断标志位是否被设置,是一个静态方法调用后清楚标志位,相当于按下开关后,开关又自动弹起来了;

示例代码:

public class Main {
    static class MyRunnable implements Runnable{
        @Override
        public void run() {
            for (int i = 0; i < 10; i++) {
                System.out.println(Thread.interrupted() + "" + i);
               // System.out.println(Thread.currentThread().isInterrupted()  + "" + i);
            }
        }
    }
    public static void main(String[] args){
        MyRunnable myRunnable = new MyRunnable();
        Thread thread = new Thread(myRunnable);
        thread.start();
       thread.interrupt();
    }
}

 运行结果:

 4.等待一个线程join()

join()的作用是等待线程对象的销毁,先看下面这段代码:

public class Main {
    static class MyRunnable implements Runnable{
        @Override
        public void run() {
            for (int i = 0; i < 10; i++) {
                System.out.print( i + " ");
            }
        }
    }
    public static void main(String[] args) throws InterruptedException {
        MyRunnable myRunnable = new MyRunnable();
        Thread thread1 = new Thread(myRunnable);
        Thread thread2 = new Thread(new MyRunnable());
       thread1.start();
       thread1.join();
        System.out.println("thread1结束工作");
        thread2.start();
        thread2.join();
        System.out.println("thread2结束工作");
    }
}

运行结果为:

 以这段代码来说,thread1.join()的作用是使所属的线程对象thread1正常执行run()方法中的任务,而使当前线程无限期阻塞,等待thread1线程销毁后再继续执行当前线程之后的代码,具有串联执行的效果,thread2.join()方法的作用也是如此。

 第二个重载的方法如图所示,它的涵义就是等待线程结束,但是最多等millis毫秒。

需要特别注意的是:在使用join()方法的过程中,如果当前线程对象被中断,则当前线程出现异常并且与先后顺序无关。


版权声明:本文为m0_58887749原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。