欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

java 多线程实现的三种方式区别

程序员文章站 2022-03-07 11:53:24
前言:java多线程其实在工作中接触的并不是很多,偶尔用一下,但是这个特性又是开发工程走向大牛必须要掌握的知识点,所以花几天时间整理了一下,一方便梳理知识点,另一方面也是为了以后更好地使用。一. 线程和进程线程可以理解是一个程序中可以独立执行的模块,一个程序在一个时间段内同时做好几件事(起码表面看起来是的)就是多线程最好的应用;进程是一次计算机的执行活动,可以是整个程序也可以是部分程序的动态执行;从概念上看,进程是包含线程的,一个进程至少包含一个线程。区别:1. 系统资源管理区别...

前言:

java多线程其实在工作中接触的并不是很多,偶尔用一下,但是这个特性又是开发工程师走向大牛必须要掌握的知识点,所以花几天时间整理了一下,一方便梳理知识点,另一方面也是为了以后更好地使用。

一. 线程和进程

线程可以理解是一个程序中可以独立执行的模块,一个程序在一个时间段内同时做好几件事(起码表面看起来是的)就是多线程最明显的表征;

进程是一次计算机的执行活动,可以是整个程序也可以是部分程序的动态执行;

从概念上看,进程是包含线程的,一个进程至少包含一个线程。

区别:

1. 系统资源管理区别

进程是在操作系统上运行的,有独立的地址空间;线程是运行在进程内部的,一个进程可以有多个线程;在一个进程内多线程可以交替切换,提高系统的并发度。

2. 通信行为区别

进程通过操作系统转发指令,是拥有独立资源的单位;线程不拥有系统资源,只能访问进程的资源;同一个进程的多个线程可以共享这个进程的所有资源;线程拥有自己的栈空间,拥有独立的执行序列。

3. 系统开销区别

创建进程时系统需要分配内存区域;在切换进程时需要保存当前进程的CPU环境并且要为被调度运行的进程设置CPU环境;线程创建不需要分配内存,使用的是所属的进程资源;切换线程时也只需要保存和设置少量寄存器的内容,不涉及存储器管理方面操作;线程消耗的资源远小于进程。

二、线程的生命周期

1. 线程状态

新建状态

可运行状态

运行状态

阻塞状态

结束状态

三、实现多线程的两个基础方法

1. 继承Thread重写run方法

public class MyThread extends Thread {
    private int num;
    private String threadName;
    private long result;

    public MyThread(int num, String threadName) {
        this.threadName = threadName;
        this.num = num;
    }

    public void run() {
        for (int i = 0; i < num; i++) {
            result += i;
        }
    }

    public String getThreadName() {
        return threadName;
    }

    public void setResult(long result) {
        this.result = result;
    }

    public long getResult() {
        return result;
    }
}

2. 实现Runnable 接口重写run方法

public class MyRunnable implements Runnable {
    private int num;
    private String threadName;
    private long result;

    public MyRunnable(int num, String threadName) {
        this.threadName = threadName;
        this.num = num;
    }

    public void run() {
        for (int i = 0; i < num; i++) {
            result += i;
        }
    }

    public String getThreadName() {
        return threadName;
    }

    public void setResult(long result) {
        this.result = result;
    }


    public long getResult() {
        return result;
    }
}

3. 测试两种方法

package thread;


public class Main {
    public static void main(String[] args) {
        threadTest();
//        runnableTest();
    }

    private static void threadTest() {
        MyThread myThread_1 = new MyThread(10, "thread_1");
        MyThread myThread_2 = new MyThread(10000, "thread_2");
        myThread_1.setResult(10);
        myThread_1.start();
        myThread_2.start();

        do {
            System.out.println("--------------------------------------------------");
            System.out.println("thread name: " + myThread_1.getThreadName() + ", status:  " + myThread_1.isAlive() + ",result: " + myThread_1.getResult());
            System.out.println("thread name: " + myThread_2.getThreadName() + ", status:  " + myThread_2.isAlive() + ",result: " + myThread_2.getResult());
        } while (myThread_1.isAlive() || myThread_2.isAlive());
    }

    private static void runnableTest() {
        MyRunnable myRunnable_1 = new MyRunnable(10, "runnable_1");
        MyRunnable myRunnable_2 = new MyRunnable(10000, "runnable_2");

        Thread thread_1 = new Thread(myRunnable_1);
        Thread thread_2 = new Thread(myRunnable_2);
        thread_1.start();
        thread_2.start();

        do {
            System.out.println("--------------------------------------------------");
            System.out.println("thread name: " + myRunnable_1.getThreadName() + ", status:  " + thread_1.isAlive() + ",result: " + myRunnable_1.getResult());
            System.out.println("thread name: " + myRunnable_2.getThreadName() + ", status:  " + thread_2.isAlive() + ",result: " + myRunnable_2.getResult());
        } while (thread_1.isAlive() || thread_2.isAlive());
    }

}

4. 两种方法的比较

如果非要说区别,其实就是实现接口和继承的区别,看开发者的使用习惯

另外一点,两种方法的run都是不能传参数的,只能通过类的方法设置参数使用

四、实现Callable接口重写call()方法实现多线程

上述两种基础方法的run都是void类型,想获取返回只能另加逻辑,而实现Callable接口重写call(的好处是允许call函数有返回,下面举例

import java.util.concurrent.Callable;

public class MyCall implements Callable<Long> {
    private int num;
    private String threadName;
    private long result;

    public MyCall(int num, String threadName) {
        this.threadName = threadName;
        this.num = num;
    }

    public Long call() throws Exception {

        for (int i = 0; i < num; i++) {
            result += i;
        }
        return result;
    }
}


// 下面是测试

import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

public class Main {
    public static void main(String[] args) {
        try {
            callTest();
        } catch (ExecutionException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }


    private static void callTest() throws ExecutionException, InterruptedException {
        MyCall myCall_1 = new MyCall(10, "call_1");
        MyCall myCall_2 = new MyCall(10000, "call_2");
        FutureTask<Long> f1 = new FutureTask<Long>(myCall_1);
        FutureTask<Long> f2 = new FutureTask<Long>(myCall_2);
        Thread thread_1 = new Thread(f1);
        Thread thread_2 = new Thread(f2);
        thread_1.start();
        thread_2.start();
        System.out.println(f1.get());    // 获取返回
        System.out.println(f2.get());    // 获取返回
    }

}

五、线程池管理多线程

给一个简单的样例

import java.util.concurrent.Callable;

public class MyCall implements Callable<Long> {
    private int num;
    private String threadName;
    private long result;

    public MyCall(int num, String threadName) {
        this.threadName = threadName;
        this.num = num;
    }

    public Long call() throws Exception {

        for (int i = 0; i < num; i++) {
            result += i;
        }
        return result;
    }
}



// 线程池方式测试

import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class ThreadPool {


    public static void main(String[] args) throws ExecutionException, InterruptedException {
        MyCall myCall_1 = new MyCall(10, "call_1");
        MyCall myCall_2 = new MyCall(10000, "call_2");
        ExecutorService service = Executors.newFixedThreadPool(5);
        Future<Long> f1 = service.submit(myCall_1);
        Future<Long> f2 = service.submit(myCall_2);
        System.out.println(f1.get());    // 获取返回
        System.out.println(f2.get());    // 获取返回
        service.shutdown();
    }
}

六、四种方式总结

1. 继承Thread和实现Runnable使用起来比较接近,唯一区别就是Runnable避免了单一继承的缺点

2. 有返回的情况下建议使用Callable,而且可以抛出异常方便定位问题

3.线程池其实不算是实现方式(有些人会把这个也算是实现方式),它更像是一种管理多线程的方式

 

 

本文地址:https://blog.csdn.net/qingquanyingyue/article/details/112030797

相关标签: Java 多线程