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<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.线程池其实不算是实现方式(有些人会把这个也算是实现方式),它更像是一种管理多线程的方式

 

 

  • 2
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Java多线程实现三种方式: 1. 继承Thread类:创建一个继承自Thread类的子类,重写run()方法,在run()方法中定义线程要执行的任务。然后创建该子类的对象,并调用start()方法启动线程。 ```java class MyThread extends Thread { public void run() { // 线程要执行的任务 } } public class Main { public static void main(String[] args) { MyThread thread = new MyThread(); thread.start(); } } ``` 2. 实现Runnable接口:创建一个实现了Runnable接口的类,实现run()方法,在run()方法中定义线程要执行的任务。然后创建该类的对象,并将其作为参数传递给Thread类的构造方法,最后调用start()方法启动线程。 ```java class MyRunnable implements Runnable { public void run() { // 线程要执行的任务 } } public class Main { public static void main(String[] args) { MyRunnable myRunnable = new MyRunnable(); Thread thread = new Thread(myRunnable); thread.start(); } } ``` 3. 使用Callable和Future:创建一个实现了Callable接口的类,实现call()方法,在call()方法中定义线程要执行的任务,并返回一个结果。然后使用ExecutorService的submit()方法提交Callable任务,得到一个Future对象,通过调用Future对象的get()方法可以获取线程执行的结果。 ```java import java.util.concurrent.*; class MyCallable implements Callable<Integer> { public Integer call() throws Exception { // 线程要执行的任务,并返回一个结果 return 42; } } public class Main { public static void main(String[] args) throws ExecutionException, InterruptedException { ExecutorService executorService = Executors.newSingleThreadExecutor(); MyCallable myCallable = new MyCallable(); Future<Integer> future = executorService.submit(myCallable); Integer result = future.get(); System.out.println(result); executorService.shutdown(); } } ``` 这三种方式都可以实现多线程,每种方式都有自己的优势和适用场景,选择合适的方式取决于具体的需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值