02-Two ways to create threads: inherit the Thread class and implement the Runnable interface

Inherit the Thread class to create a thread

获取线程对象

Write a class继承Thread类并在重写的run方法中编写业务逻辑代码, then this class is a thread class

  • The run method of the Runnable interface does not throw any exceptions, so subclasses cannot throw any exceptions when overriding the run method. When exceptions are encountered during program execution, they can only be caught but not thrown.
// 重写方法抛出的异常范围不能大于被重写方法抛出的异常范围
@Override
public void run() {
    
    
    if (target != null) {
    
    
        target.run();
    }
}

Requirement: Start a sub-thread in the main thread, let the two threads output alternately on the console every second, end the main thread when the output is 60 times, and end the sub-thread when the output is 80 times

  • A Thread object can and can only represent one thread. If a Thread object calls the start() method twice, it will throwjava.lang.illegalThreadStateException异常
public class Thread01 {
    
    
    // main方法是由主线程调用在主栈中运行
    public static void main(String[] args) throws InterruptedException {
    
    
        // 创建线程对象
        MyThread cat = new MyThread();
        // 此时表示在主线程中启动了一个子线程,最终会执行线程对象的run方法
        t.start();
		// 线程每隔1秒在控制台输出
        for(int i = 0; i < 60; i++) {
    
    
            System.out.println("主线程 i=" + i);
            //让主线程休眠
            Thread.sleep(1000);

        }
    }

// 定义一个线程类
class MyThread extends Thread {
    
    
    int times = 0;
    // 重写run方法写上自己的业务逻辑,run方法由MyThread线程调用并在分支栈中运行
    @Override
    public void run() {
    
    
        while (true) {
    
    
            // 线程每隔1秒在控制台输出
            System.out.println("子线程 times=" + (++times));
            // 让当前线程休眠1秒
            try {
    
    
                Thread.sleep(1000);
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
            // 当times到80退出当前线程
            if(times == 80) {
    
    
                break;
            }
        }
    }
}

run方法和start()方法的区别

Directly in the main thread通过线程对象调用run方法 will not actually start a thread. The program will first execute the run method before executing it downwards, because at this time the program still only has one main thread
Insert image description here

通过线程对象调用start()方法A branch thread will be started, that is, a new stack space will be opened in the JVM. As long as the new stack space is opened, the start() method will end instantly. At this time, the thread will be started successfully.

  • A successfully started thread will automatically call the run method, which is the entry method, and the run method is at the bottom of the branch stack. Executing a thread will ultimately execute the code in the run() method of the thread.
  • run和main是平级的: The run method is the entry method of the sub-thread at the bottom of the branch stack, and the main method is the entry method of the main thread at the bottom of the main stack.

Insert image description here

Because there are many threads that want to seize the CPU, only the CPU that seizes the thread will be executed, so the runnable state of the thread is divided into ready state and running state.

  • 线程的就绪状态: The thread is qualified to seize the CPU but has not yet been executed.

  • 线程的运行状态: The thread has preempted the CPU and can be executed.

通过线程对象调用start()方法后It does not mean that the corresponding thread will be executed immediately, but it only changes the thread state to the ready state in the runnable state.底层还会调用start0()方法

Insert image description here

public synchronized void start() {
    
    
    //....
    // 真正实现多线程的方法,由JVM调用,底层是c/c++实现,在这个实现的过程中调用了线程对象run方法
    start0();
}

// start0会调用线程对象的run方法
private native void start0();

Implement the Runnable interface to create a thread

Since Java is single inheritance, if a class has inherited a certain parent class, it cannot create a thread by inheriting the Thread class. In this case, you need to implement the java.lang.Runnable interface and Realizerun方法

模拟线程代理类的功能实现

The Runnable interface has only one run method and no start method. When we execute the run method of the thread object, it通过执行线程代理对象的run方法然后间接调用要目标线程对象的run方法

  • 代理模式: Both the thread proxy class and the target thread class implement the Runnable interface. The thread proxy object must contain the target thread object to be proxied.

Step 1: Create a target class that requires proxying and implement theRunnableinterface

class Tiger extends Animal implements Runnable {
    
    
    @Override
    public void run() {
    
    
        System.out.println("老虎嗷嗷叫....");
    }
}

Step 2: Simulate the Thread class to create a线程代理类 also implements theRunnableinterface

public class ThreadProxy implements Runnable {
    
    
    // 内聚代理的目标对象,类型是Runnable类型
    private Runnable target = null;
    public ThreadProxy(Runnable target) {
    
    
         // 接收需要代理的目标对象
        this.target = target;
    }
    @Override
    public void run() {
    
    
        if (target != null) {
    
    
            // 动态绑定,对象的运行类型还是目标对象的类型
            target.run();
        }
    }
    public void start() {
    
    
        // 这个方法是真正实现多线程方法
        start0();
    }
    public void start0() {
    
    
        run();
    }
}

Step 3: Get the thread proxy object of the target object:线程代理对象的start方法---->start0方法---->线程代理对象的run方法---->目标线程对象的run方法

public class Thread02 {
    
    
    public static void main(String[] args) {
    
    
        Tiger tiger = new Tiger();
        // 调用线程代理对象的start方法,最终调用目标线程对象的run方法
        ThreadProxy threadProxy = new ThreadProxy(tiger);
        threadProxy.start();
    }
}

获取线程代理对象

Requirement: The completed program outputs hi to the console every second, and automatically exits after outputting 10 times.

public class Thread02 {
    
    
    public static void main(String[] args) {
    
    
        // 创建一个可运行的普通对象
        MyRunnable r = new MyRunnable();
        // 为可运行的对象生成一个线程代理对象,含有start方法
        Thread thread = new Thread(r); 
        // 调用线程代理对象的start方法,最终调用目标线程对象的run方法
        thread.start();
    }
}
// 创建一个可运行的类还不是线程类
class MyRunnable implements Runnable {
    
     
    int count = 0;
    @Override
    public void run() {
    
     //普通方法
        while (true) {
    
    
            System.out.println("hi" + (++count) + Thread.currentThread().getName());
            //休眠1秒
            try {
    
    
                Thread.sleep(1000);
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
            if (count == 10) {
    
    
                break;
            }
        }
    }
}

Create an implementation class object of the Runnable interface using an anonymous inner class and create a proxy object for it

public class ThreadTest04 {
    
    
    public static void main(String[] args) {
    
    
        // 获取线程的代理对象
        Thread t = new Thread(new Runnable(){
    
    
            @Override
            public void run() {
    
    
                for(int i = 0; i < 100; i++){
    
    
                    System.out.println("t线程---> " + i);
                }
            }
        });
        // 启动线程
        t.start();
        for(int i = 0; i < 100; i++){
    
    
            System.out.println("main线程---> " + i);
        }
    }
}

Start multiple sub-threads in the main thread. A Thread object can and can only represent one thread. If a Thread object calls the start() method twice, it will throwillegalThreadStateException异常

public class Thread03 {
    
    
    public static void main(String[] args) {
    
    
        T1 t1 = new T1();
        T2 t2 = new T2();
        Thread thread1 = new Thread(t1);
        Thread thread2 = new Thread(t2);
        thread1.start();//启动第1个线程
        thread2.start();//启动第2个线程
        //...
    }
}

class T1 implements Runnable {
    
    
    int count = 0;
    @Override
    public void run() {
    
    
        while (true) {
    
    
            // 每隔1秒输出 “hello,world”,输出10次
            System.out.println("hello,world " + (++count));
            try {
    
    
                Thread.sleep(1000);
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
            if(count == 60) {
    
    
                break;
            }
        }
    }
}
class T2 implements Runnable {
    
    
    int count = 0;
    @Override
    public void run() {
    
    
        // 每隔1秒输出 “hi”,输出5次
        while (true) {
    
    
            System.out.println("hi " + (++count));
            try {
    
    
                Thread.sleep(1000);
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
            if(count == 50) {
    
    
                break;
            }
        }
    }
}

Implement the Callable interface (8 new features)

获取线程对象

BecauseRunnable接口和Thread类中的run方法’s return value is void, so the thread return value cannot be obtained after the thread completes the task

If a thread delegated by the system executes a task, it will return a result. If you want to get the execution result of the thread, you need to implement it Callable接口的call方法(有返回值)Similar to the run method

  • Disadvantages: When obtaining thread execution results, the current thread is blocked, which reduces execution efficiency.
method name Function
public FutureTask (Callable interface implementation class object) Create a runnable future task class object and specify the business logic code of the thread execution method
Object get() Get the return result of the thread. Executing this method will cause the current thread to block.
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask; // JUC包下的java的并发包(新特性)
public class ThreadTest15 {
    
    
    public static void main(String[] args) throws Exception {
    
    
        // 创建一个可运行的未来任务类对象
        FutureTask task = new FutureTask(new Callable() {
    
    
            @Override
            public Object call() throws Exception {
    
     
                // 线程执行一个任务,执行之后可能会有一个执行结果
                System.out.println("call method begin");
                Thread.sleep(1000 * 10);
                System.out.println("call method end!");
                int a = 100;
                int b = 200;
                // 自动装箱(300结果变成Integer)
                return a + b; 
            }
        });
        // 为task对象创建线程代理对象
        Thread t = new Thread(task);
        // 启动线程
        t.start();
        // 执行task对象的get方法获取t线程的返回结果,但会导致当前线程阻塞
        Object obj = task.get();
        System.out.println("线程执行结果:" + obj);
        // get方法是为了拿t线程的执行结果,所以需要等t线程执行结束后,主线程才能继续执行
        System.out.println("hello world!");
    }
}

The difference between inheriting a class and implementing an interface

区别与应用

There is essentially no difference between creating threads by inheriting the Thread class or implementing the Runnable interface, because the Thread class itself implements the Runnable interface.

继承Thread类的线程类The run method of the same thread object is not ultimately executed. If you need to share variables, declare them as static.

  • 执行流程: Call the start method of the Thread class---->Call the start0 method of the Thread class (start the thread object)---->Call the run method of the thread object
SellTicket01 sellTicket01 = new SellTicket01();
SellTicket01 sellTicket02 = new SellTicket01();
SellTicket01 sellTicket03 = new SellTicket01();
//创建售票线程对象,最终调用线程类中run方法的对象是sellTicket01
sellTicket01.start();
//创建售票线程对象,最终调用线程类中run方法的对象是sellTicket02
sellTicket02.start();
//创建售票线程对象,最终调用线程类中run方法的对象是sellTicket03
sellTicket03.start();

实现Runnable接口的线程类Finally, the run method of the same thread object can be executed. If you need to share the object, you do not need to declare it as static.因为就一个对象可以实现多个线程共享一个资源

  • Pass the thread object to Thread----> Call the start method of the Thread class----> Call the start0 method of the Thread class (start the thread object)----> Call the run method of the Thread class-- -->Call the run method of the thread object
SellTicket02 sellTicket02 = new SellTicket02();
// 创建不同的线程代理对象,最终调用线程类中run方法的对象都是sellTicket02
new Thread(sellTicket02).start();//第1个线程-窗口
new Thread(sellTicket02).start();//第2个线程-窗口
new Thread(sellTicket02).start();//第3个线程-窗口

Guess you like

Origin blog.csdn.net/qq_57005976/article/details/134998881