Home >Common Problem >One article to understand how to understand threads in Java

One article to understand how to understand threads in Java

WBOY
WBOYforward
2022-07-18 13:45:581241browse

This article brings you relevant knowledge about java, which mainly organizes issues related to threads. A thread (thread) is an execution path within a program, and we are familiar with the main The method is actually a separate execution path. If there is only one execution path in the program, then the program is a single-threaded program. Let's take a look at it. I hope it will be helpful to everyone.

One article to understand how to understand threads in Java

Recommended study: "java Video Tutorial"

Thread is an execution path within a program. What we call The familiar main method is actually a separate execution path. If there is only one execution path in the program, then the program is single-threaded program; since there is a single thread, there will also be multiple threads. The literal meaning is understandable. It is a "technology that executes multiple processes on software and hardware relative to a single thread". The advantage of multi-threading is to increase CPU utilization. In a multi-threaded program, when one thread must wait, the CPU can run other threads instead of waiting, greatly improving the efficiency of the program.

Creation of multi-threads

Method 1: Inherit the Thread class

Method 1 creation process:

  • Define a subclass MyThread Inherit the thread class java.lang.Thread and override the run() method;

  • Create an object of the MyThread class;

  • Call the thread object The start() method starts the thread (the run() method is still executed after startup);

    public class ThreadDemo01 {
        public static void main(String[] args) {
            MyThread myThread1 = new MyThread();
            myThread1.start();
            for (int i = 0; i < 3; i++) {
                System.out.println("主线程正在执行~~");
            }
        }
    }
    class MyThread extends Thread{
        @Override
        public void run() {
            for (int i = 0; i < 3; i++) {
                System.out.println("子线程正在执行~~");
            }
    
        }
    }
    //输出结果(不唯一):
    //主线程正在执行~~
    //主线程正在执行~~
    //主线程正在执行~~
    //子线程正在执行~~
    //子线程正在执行~~
    //子线程正在执行~~
  • There are two threads executing in the above code, which are the main thread and thread of the main method. The child thread started by calling start() on object mythread. But why is the output result not unique? The reason is that CPU preemption will occur between the two threads during execution, and whoever seizes it first will execute first.

So why don’t we directly use the thread object to call the run() method? If run() is called directly, it is just a normal calling method, that is, a single thread, while the start() method is used to start child threads, so that multi-threading can occur.

Method 1 Advantages and Disadvantages:

  • Advantages: Simple coding;
  • Disadvantages: The thread class has inherited Thread and cannot inherit other classes, which is not conducive to expansion;

Method 2: Implement the Runnable interface

Method 2 creation process:

1. Define a thread task class MyRunnable to implement the Runnable interface and override the run() method;

2. Create a MyRunnable object;

3. Hand the MyRunnable task object to Thread for processing;

4. Call the start() method of the thread object to start the thread;

Thread constructorMethod
public Thread (String name)can be the current thread Specify the name
public Thread (Runnable target)Encapsulate the Runnable object to become a thread object
public Thread (Runnable target, String name)Encapsulate the Runnable object into a thread object and specify the thread name
public class ThreadDemo02 {
    public static void main(String[] args) {
        MyRunnable target = new MyRunnable();
        Thread thread = new Thread(target);
        thread.start();
        for (int i = 0; i < 3; i++) {
            System.out.println("主线程正在执行~~");
        }
    }
}
class MyRunnable implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 3; i++) {
            System.out.println("子线程正在执行~~");
        }

    }
}
//输出结果(不唯一):
//主线程正在执行~~
//子线程正在执行~~
//子线程正在执行~~
//子线程正在执行~~
//主线程正在执行~~
//主线程正在执行~~

该代码与方式一的不同之处在于需要将MyRunnable任务对象封装在Thread中,其他的地方是基本上是没有变化的。

方式二优缺点:

优点:线程任务类只是实现接口,可以继续继承类和实现接口,扩展性强;

缺点:编程多一层对象包装,如果线程有执行结果是不可以直接返回的。

接下来我们同样使用实现Runnable接口(匿名内部类形式)来实现多线程的创建:

1、创建Runnable匿名内部类对象;

2、交给Thread处理;

3、调用线程对象的start()启动线程;

//正常版:
public class ThreadDemo01 {
    public static void main(String[] args) {
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 3; i++) {
                    System.out.println("子线程正在执行~~");
                }
            }
        });
        thread.start();
        for (int i = 0; i < 3; i++) {
            System.out.println("主线程正在执行~~");
        }
    }
}

//lambda简化版:
new Thread(()-> {
                for (int i = 0; i < 3; i++) {
                    System.out.println("子线程正在执行~~");
                }
        }).start();

该种方法从本质上其实并没有太大的区别只不过是一个需要创建线程对象,而另一个则是通过匿名内部类实现的多线程。并且该块代码也可以通过lambda表达式进行精简,不知道大家是否还对这个知识点有印象呢?若忘记了可以看一下这篇文章:Java中的lambda表达式如何理解——精简

方式三:实现Callable接口

在学习过前面两种创建多线程的方式以后,我们会发现存在一个问题:1、重写的run()方法不能直接返回结果;2、不适合需要返回线程执行结果的业务场景。因此,我们需要第三种方式来解决这些问题。

方式三创建过程:

1、定义类实现Callable接口,重写call()方法,封装要做的事情;

2、用FutureTask把Callable对象封装成线程任务对象;

3、把线程任务对象交给Thread处理;

4、调用Thread的start()方法启动线程,执行任务;

5、线程执行完毕后,通过FutureTask的get()方法获取任务执行的结果。

public class ThreadDemo03 {
    public static void main(String[] args) throws Exception {
        MyCallable myCallable = new MyCallable();
        FutureTask<String> futureTask = new FutureTask<>(myCallable);
        Thread thread = new Thread(futureTask);
        thread.start();
        int sum= 0;
        for (int i = 0; i < 3; i++) {
            sum+=i;
        }
        System.out.println(sum);
        String s =futureTask.get();
        System.out.println(s);
    }
}
class MyCallable implements Callable<String > {
    @Override
    public String call(){
        int sum=0;
        for (int i = 0; i < 3; i++) {
            sum+=i;
        }
        return "子线程计算结果:"+sum;
    }
}
//输出结果:
//3
//子线程计算结果:3

方式三优缺点:

优点:

线程任务类只是实现接口,可以继续继承类和实现接口,扩展性强;

可以在线程执行完毕后去获取 线程执行的结果;

缺点:

编码复杂一点;

总结 

方法名称说明
public FutureTask<>(Callable call)把Callable对象封装成FutureTask对象
public V get() throws Exception 获取线程执行call方法返回的结果
方式 优点 缺点
继承Thread类 编程比较简单,可以直接使用Thread类中的方法 扩展性较差,不能再继承其他的类,不能返回线程执行的结果
实现Runnable接口 扩展性强,实现该接口的同时还可以继承其他的类 编程相对复杂,不能返回线程执行的结果
实现Callable接口 扩展性强,实现该接口的同时还可以继承其他的类,可以得到线程的执行结果 编程相对复杂

常用方法 

Thread获取和设置线程名称 

方法名称 说明
String getName() 获取当前线程的名称,默认线程名称是Thread-索引
void setName(String name)

将此线程更改为指定的名称,通过构造器也可以设置线程名称

 简单地通过一段代码让大家能够清晰地了解这个代码该如何使用:

public class ThreadDemo04 {
    public static void main(String[] args) throws Exception {
        thread thread1 = new thread();
        thread1.setName("1号子线程");
        thread1.start();
        thread thread2 = new thread();
        thread2.setName("2号子线程");
        thread2.start();
    }
}
class thread extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 3; i++) {
            System.out.println(this.getName()+"正在执行任务"+i);
        }
    }
}
//输出结果:
//2号子线程正在执行任务0
//1号子线程正在执行任务0
//2号子线程正在执行任务1
//1号子线程正在执行任务1
//2号子线程正在执行任务2
//1号子线程正在执行任务2

Thread类的线程休眠方法 

方法名称 说明
public static void sleep(long time)  让当前线程休眠指定的时间后再继续执行,单位为毫秒
public class ThreadDemo05 {
    public static void main(String[] args) throws Exception {
        for (int i = 0; i < 5; i++) {
            System.out.println(i);
            if (i==3){
                Thread.sleep(5000);
            }
        }
    }
}
//输出结果:
//1
//2
//3
//在输出过3以后,等待5秒之后再进行输出
//4

推荐学习:《java视频教程

The above is the detailed content of One article to understand how to understand threads in Java. For more information, please follow other related articles on the PHP Chinese website!

Statement:
This article is reproduced at:csdn.net. If there is any infringement, please contact admin@php.cn delete