栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

多线程执行任务,Thread 类、Runable接口、Callable接口 区别

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

多线程执行任务,Thread 类、Runable接口、Callable接口 区别

线程的状态

方式1:

实现原理:
新建线程类 TestThread,继承 Thread 类,重写 run 方法,主线程实例化 TestThread 对象,调用对象的 start() 方法,开启新线程,新线程会自动执行 run 方法的代码块,新线程何时执行 run 方法,取决于CPU的调度

适用场景:
多个线程各自执行各自的任务,任务之间互不烦扰,任务所需参数,在线程类实例化对象时,通过 构造器 设置参数,如:多个厨师炒不同的菜

传统方式
package com.wei.thread;

import java.util.HashMap;
import java.util.Map;


public class TestThread extends Thread {

    private Map params;

    public TestThread(Map params) {
        this.params = params;
    }

    @Override
    public void run() {
        StringBuilder builder = new StringBuilder();
        params.forEach((k, v) -> {
            builder.append(v).append("|");
        });
        System.out.println(Thread.currentThread().getName() + "-> 使用材料 (" + builder.toString() + ") 做菜");
    }

    public static void main(String[] args) {
        // 定义菜谱食材
        HashMap params1 = new HashMap<>();
        HashMap params2 = new HashMap<>();
        params1.put("tomato", "番茄");
        params1.put("egg", "鸡蛋");
        params2.put("beef", "牛肉");
        // 线程类实例化对象时,传入参数
        TestThread t1 = new TestThread(params1);
        t1.setName("厨师1");
        TestThread t2 = new TestThread(params2);
        t2.setName("厨师2");
        t1.start();
        t2.start();
    }
}

Lambda方式
package com.wei.thread;

import java.util.HashMap;


public class TestThreadLambda {

    public static void main(String[] args) {
        HashMap params1 = new HashMap<>();
        HashMap params2 = new HashMap<>();
        params1.put("tomato", "番茄");
        params1.put("egg", "鸡蛋");
        params2.put("beef", "牛肉");
        Thread t1 = new Thread(() -> {
            StringBuilder builder = new StringBuilder();
            params1.forEach((k, v) -> {
                builder.append(v).append("|");
            });
            System.out.println(Thread.currentThread().getName() + "-> 使用材料 (" + builder.toString() + ") 做菜");
        },"厨师1");
        Thread t2 = new Thread(() -> {
            StringBuilder builder = new StringBuilder();
            params2.forEach((k, v) -> {
                builder.append(v).append("|");
            });
            System.out.println(Thread.currentThread().getName() + "-> 使用材料 (" + builder.toString() + ") 做菜");
        }, "厨师2");
        t1.start();
        t2.start();
    }
}

方式2:

实现原理:
新建 TestRunable 类,实现 Runable 接口,重写 run 方法,主线程实例化 TestRunable 对象;使用有参构造器,实例化 Thread 对象时,将 TestRunable 对象以参数形式传入,再调用 Thread 对象的 start() 方法,开启新线程,新线程会自动执行 run 方法的代码块,新线程何时执行 run 方法,取决于CPU的调度

适用场景:
多个线程共同操作一个任务,或共享共同的资源的情况,且不需要知道任务执行结果的场景。如:给偶像点赞

传统方式
package com.wei.thread;


public class TestRunable implements Runnable {

    String idol;

    public TestRunable(String idol) {
        this.idol = idol;
    }

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + " 给" + idol + " 点赞");
    }

    public static void main(String[] args) {
        // 给同一个idol点赞
        TestRunable tr = new TestRunable("周杰伦");
        // 用户给偶像点赞
        Thread t1 = new Thread(tr, "张三");
        Thread t2 = new Thread(tr, "李四");
        Thread t3 = new Thread(tr, "王五");
        t1.start();
        t2.start();
        t3.start();
    }
}

Lambda方式
package com.wei.thread;


public class TestRunableLambda{

    
    public static void dianZan(String idol) {
        System.out.println(Thread.currentThread().getName() + " 给" + idol + " 点赞");
    }

    public static void main(String[] args) {
        // 给偶像点赞
        Thread t1 = new Thread(()->{dianZan("周杰伦");}, "张三");
        Thread t2 = new Thread(()->{dianZan("周结论");}, "李四");
        Thread t3 = new Thread(()->{dianZan("周结论");}, "王五");
        t1.start();
        t2.start();
        t3.start();
    }
}

方式3:

实现原理:
新建 TestCallable 类,实现 Callable 接口,重写 call 方法,主线程实例化 TestCallable 对象;需要通过线程池进行代理调用,将 TestCallable 对象,提交到 线程池,线程池的线程会自动执行 call 方法的代码块,线程池中的线程何时执行 call 方法,取决于CPU的调度

适用场景:
多个线程共同操作一个任务,或共享同一个资源的情况,但需要返回任务执行结果的场景,如:用户抢票

传统方式
package com.wei.thread;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;


public class TestCallable implements Callable> {

    int ticket;

    public TestCallable(int ticket) {
        this.ticket = ticket;
    }

    @Override
    public Map call() {
        Map result = new HashMap<>();
        synchronized (this) {
            if (ticket <= 0) {
                result.put(Thread.currentThread().getName(), "票已售罄");
                return result;
            }
            result.put(Thread.currentThread().getName(), "买到票No:" + ticket);
            ticket--;
        }
        return result;
    }

    public static void main(String[] args) throws Exception{
        // 放票 2 张
        TestCallable tc = new TestCallable(2);
        ExecutorService threadPool = Executors.newFixedThreadPool(3);
        // 模拟3个用户抢票
        Future> user1 = threadPool.submit(tc);
        Future> user2 = threadPool.submit(tc);
        Future> user3 = threadPool.submit(tc);
        System.out.println(user1.get());
        System.out.println(user2.get());
        System.out.println(user3.get());
        threadPool.shutdown();
    }
}

Lambda方式
package com.wei.thread;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;


public class TestCallableLambda {

    private static int ticket = 2;

    public static synchronized Map buyTicket(String user) {
        Map result = new HashMap<>();
        if (ticket <= 0) {
            result.put(user, "票已售罄");
            return result;
        }
        result.put(user, "买到票No:" + ticket);
        ticket--;
        return result;
    }

    public static void main(String[] args) throws Exception {

        ExecutorService threadPool = Executors.newFixedThreadPool(3);
        // 模拟3个用户抢票
        Future> user1 = threadPool.submit(() -> {
            return buyTicket("张三");
        });
        Future> user2 = threadPool.submit(() -> {
            return buyTicket("李四");
        });
        Future> user3 = threadPool.submit(() -> {
            return buyTicket("王五");
        });
        System.out.println(user1.get());
        System.out.println(user2.get());
        System.out.println(user3.get());
        threadPool.shutdown();
    }
}

小结: 1、Thread 类、Runable接口、Callable接口 区别
  • 继承 Thread 类,实例化 Thread 对象,调用对象的 start() 方法即可开启线程
  • 实现 Runable接口 或 Callable接口 的类,实例化为对象后,需要将对象传入代理对象,由代理对象开启线程
2、Runable接口实现类、Callable接口实现类 区别
  • Runable接口实现类 的代理对象,可以是 Thread对象,也可以是 线程池对象
  • Callable接口实现类 的代理对象,只能是 线程池对象
  • Runable接口实现类,任务执行后 没有返回值
  • Callable接口实现类,任务执行后 有返回值
扩展 守护线程

虚拟机至少有两个线程,一个main线程,一个gc线程
用户线程: 应用层面实现业务逻辑的线程,如:main线程
守护线程: 在后台执行的线程,通过Daemon标记为true;如:gc线程
区别:
虚拟机需要等所有的用户线程执行完,才会退出
但不需要等待守护线程执行完毕,即可关闭资源

Thread.setDaemon(true);  // 默认false,表示都是用户线程
例子
package com.wei.thread;


public class TestDaemon {
    public static void main(String[] args) {
        God god = new God();
        You you = new You();
        Thread godThread = new Thread(god);
        Thread youThread = new Thread(you);
        godThread.setDaemon(true);
        godThread.start();
        youThread.start();
    }
}

class God implements Runnable {

    @Override
    public void run() {
        while (true) {
            System.out.println("God 守护着你");
        }
    }
}

class You implements Runnable{

    @Override
    public void run() {
        for (int i = 0; i < 36500; i++) {
            System.out.println("每天开心活着 ^_^");
        }
        System.out.println("再见word");
    }
}
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/856580.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号