多线程 - 三种实现

本文详细介绍了Java中的线程核心概念,包括线程的创建方式(Thread、Runnable、Callable),并发控制的重要性以及并发下载图片的示例。通过龟兔赛跑的案例展示了线程的同步问题,并探讨了Callable接口的优势。最后,提供了使用Callable接口改造下载图片案例的代码示例。
摘要由CSDN通过智能技术生成

线程的核心概念

  • 线程就是独立的执行路径
  • 在程序运行时,即使没有自己创建线程,后台也会有多个线程,如main 线程,GC线程;
  • main()称之为主线程,为系统的入口,用于执行整个程序;
  • 在一个进程中,如果开辟了多个线程,线程的运行由调度器安排调度,调度器是与操作系统紧密相关的,先后顺序是不能认为的干预。
  • 对同一份资源操作时,会存在资源抢夺的问题,需要加入并发控制;
  • 线程会带来额外的开销,如CPU调度时间,并发控制开销。
  • 每个线程在自己的工作内存交互,内存控制不当会造成数据不一致

线程创建方式

Thread 方法

  • 自定义 Thread 类继承 Thread
  • 重写 run() 方法,编写线程执行体
  • 创建线程对象,调用 start() 方法启动线程

继承 Thread

//创建线程方式1 ,继承Thread ,重写 run 方法。调用start 开启先成功
public class TestThread01  extends Thread{
    @Override
    public void run() {
        //线程的run 方法
        for (int i = 0; i < 200; i++) {
            System.out.println("TestThread01 run: " +i);
        }
    }
}

main方法测试

 public static void main(String[] args) {
        //main 线程 , 主线程
        //创建一个线程对象
        TestThread01 thread01 = new TestThread01();
        //调度一个start 方法开启线程
        // 会是交叉执行  不存在先后执行  是由cpu 决定执行的
        thread01.start();
        // 如果为run方法 和 从上到下执行没啥区别
        //thread01.run(); 
        for (int i = 0; i < 2000; i++) {
            System.out.println("TestThread main : " +i);
        }
    }

可以查看执行结果 run 方法 和从上到下调用方法的结果一直,而 start 方法的执行结果则是交叉执行。

模拟多线程下载图片

导入 下载文件的工具类

    <dependencies>
        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.6</version>
        </dependency>
    </dependencies>

具体实现代码

package com.mayb.demo01;

import org.apache.commons.io.FileUtils;

import java.io.File;
import java.io.IOException;
import java.net.URL;

//练习Thread ,实现多线程同步下载图片
public class TestThread02 extends Thread{

    private String url; //网络文件路径
    private String name;//需要保存为什么文件名称

    public   TestThread02(String url,String name){
        this.url = url;
        this.name = name;
    }


    @Override
    public void run() {
        webDownLoader webDownLoader = new webDownLoader();
        webDownLoader.downLoader(url,name);
        System.out.println("下载了文件 :"  + name);
    }

    public static void main(String[] args) {
        TestThread02 thread01 = new TestThread02("图片路径"  ,"1.jpg");
        TestThread02 thread02 = new TestThread02("图片路径"  ,"2.jpg");
        TestThread02 thread03 = new TestThread02("图片路径"  ,"3.jpg");
        TestThread02 thread04 = new TestThread02("图片路径"  ,"4.jpg");
        thread01.start();
        thread02.start();
        thread03.start();
        thread04.start();
    }
}

//下载器
class webDownLoader{
    //下载方法
    public  void downLoader(String url,String name){
        try {
            FileUtils.copyURLToFile(new URL(url),new File(name));
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("IO downLoader 方法异常。");
        }
    }
}

总结

注意,线程在开启不一定立即执行,由cpu调度。

Runnable 方法

  • 定义 Runnable 类实现 Runnable 接口
  • 实现 run() 方法,编写线程执行体
  • 创建线程对象,调用 start() 方法启动线程
package com.mayb.demo01;

//创建线程方式2,实现runnable接口, 重写run方法,执行线程需要丢入 runnable 接口实现类。调用start方法。
public class TestThread03 implements Runnable{

    public void run() {
        //线程的run 方法
        for (int i = 0; i < 200; i++) {
            System.out.println("TestThread03 run: " +i);
        }
    }

    public static void main(String[] args) {
        //main 线程 , 主线程
        //创建 Runnable 接口的实现对象
        TestThread03 thread03 = new TestThread03();
        //创建线程对象 通过线程对象来开启我们的线程 (代理)
        new Thread(thread03).start();

        for (int i = 0; i < 2000; i++) {
            System.out.println("TestThread main : " +i);
        }

    }
}

加强练习

龟兔赛跑的案例
  1. 首先来个赛道距离,然后要离终点越来越近
  2. 判断比赛是否结束
  3. 打印出胜利者
  4. 龟兔赛跑开始
  5. 故事中是乌龟赢的,兔子需要睡觉,所以我们来模拟兔子睡觉
  6. 终于,乌龟赢得比赛
//模拟龟兔赛跑
public class ARun  implements Runnable{
    //胜利者
    private static  String winner;

    public void run() {
        for (int i = 0; i <= 100; i++) {
            //模拟兔子休息 每跑10 步就休息200 ms
            if (Thread.currentThread().getName().equals("兔子") && i%10 == 0){
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            //判断比赛是否结束
            boolean flag = gameOver(i);
            // 如果为 true 就结束比赛
            if (flag){
                break;
            }
            System.out.println(Thread.currentThread().getName()+"--->跑了"+ i + "米");
        }
    }

    //判断是否已经结束比赛
    public boolean gameOver(int steps){
        //判断是否有胜利者
        if (winner != null){
            //已经存在胜利者
            return false;
        }else {
            if (steps >= 100){
                winner = Thread.currentThread().getName();
                System.out.println("winner is " + winner );
                return  true;
            }
        }
        return false;
    }


    //Test
    public static void main(String[] args) {
        ARun aRun = new ARun();
        new Thread(aRun,"兔子").start();
        new Thread(aRun,"乌龟").start();
    }
}

Callable 方法

1.实现Callable接口,需要返回值类型
2.重写call方法, 需要抛出异常
3.创建目标对象
4.创建执行服务: ExecutorService ser = Executors.newFixedThreadPool(1);
5.提交执行: Future result1 = ser.submit(t1);
6.获取结果: boolean r1 = result1.get()
7.关闭服务: ser.shutdownNow();

用 Callable 接口改造下载图片案例

import org.apache.commons.io.FileUtils;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.concurrent.*;

//创建线程3 实现Callable 接口
/*
callable 的好处
1、可以定义返回值
2、可以抛出异常
 */
public class TestCallable implements Callable<Boolean> {

    private String url; //网络文件路径
    private String name;// 需要保存为什么文件名称

    public   TestCallable(String url,String name){
        this.url = url;
        this.name = name;
    }



    public Boolean call() {
        webDownLoader webDownLoader = new webDownLoader();
        webDownLoader.downLoader(url,name);
        System.out.println("下载了文件 :"  + name);
        return true;
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        TestThread02 thread01 = new TestThread02("图片路径"  ,"1.jpg");
        TestThread02 thread02 = new TestThread02("图片路径"  ,"2.jpg");
        TestThread02 thread03 = new TestThread02("图片路径"  ,"3.jpg");
        //创建执行服务:  3 条线程
        ExecutorService ser = Executors.newFixedThreadPool(3);
        //提交执行:
        Future<Boolean> result1 = ser.submit(thread01);
        Future<Boolean> result2 = ser.submit(thread02);
        Future<Boolean> result3 = ser.submit(thread03);
        //获取结果: 就是call 方法 返回的值
        boolean r1 = result1.get();
        boolean r2= result2.get();
        boolean r3 = result3.get();
        //关闭服务
        ser.shutdownNow();
    }
}

//下载器
class webDownLoader{
    //下载方法
    public  void downLoader(String url, String name){

        try {
            FileUtils.copyURLToFile(new URL(url),new File(name));
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("IO downLoader 方法异常。");
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值