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

前言:

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 {
    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 f1 = new FutureTask(myCall_1);
        FutureTask f2 = new FutureTask(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 {
    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 f1 = service.submit(myCall_1);
        Future f2 = service.submit(myCall_2);
        System.out.println(f1.get());    // 获取返回
        System.out.println(f2.get());    // 获取返回
        service.shutdown();
    }
}

六、四种方式总结

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

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

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

 

 

你可能感兴趣的:(Java,多线程,java)