多线程

多线程

线程的三种创建方式

  1. Thread class:继承Thread类

    1. 自定义线程类继承Threadlei类

    2. 重写run()方法,编写线程执行体

    3. 创建线程对象,调用start()启动线程

      package com.thread;
      //创建线程方式1:继承Thread类,重写run方法,调用start方法开启线程
      public class ThreadTest01 extends Thread {
      @Override
      public void run() {
      for (int i = 0; i < 20; i++) {
      System.out.println("i am watch code:" + i);
      }
      }
      public static void main(String[] args) {
      //main线程
      //创建一个线程对象
      ThreadTest01 threadTest01 = new ThreadTest01();
      //调用start()方法开启线程
      //start()方法是主线程和子线程一起执行
      //run()方法是先执行run()方法的子线程,再执行主线程
      //注意:线程开启不一定立即执行,需要等CPU的调度处理
      threadTest01.start();
      for (int i = 0; i < 20; i++) {
      System.out.println("我在学习多线程:" + i);
      }
      }
      }

      代码实现多线程下载图片

      package com.thread;
      import org.apache.commons.io.FileUtils;
      import java.io.File;
      import java.io.IOException;
      import java.net.URL;
      //利用thread实现多线程同步下载图片
      public class ThreadTest02 extends Thread {
      private String url;
      private String name;
      public ThreadTest02(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) {
      ThreadTest02 test01 = new ThreadTest02("https://gimg2.baidu.com/image_search/src=http%3A%2F%2F5b0988e595225.cdn.sohucs.com%2Fimages%2F20190513%2F7cb9afc55171450eaa17604c5e4fc765.jpeg&refer=http%3A%2F%2F5b0988e595225.cdn.sohucs.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1660887492&t=030319dc87f0a7ce129ba69ab5bda5e5", "1.jpg");
      ThreadTest02 test02 = new ThreadTest02("https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg.youxi369.com%2Farticle%2Fcontents%2F2021%2F01%2F07%2F2021010740735362.jpg&refer=http%3A%2F%2Fimg.youxi369.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1660887492&t=97f6180af2ab121b58f15c7f300a0ebb", "2.jpg");
      ThreadTest02 test03 = new ThreadTest02("https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fb-ssl.duitang.com%2Fuploads%2Fitem%2F201802%2F12%2F20180212094815_MWrv5.jpeg&refer=http%3A%2F%2Fb-ssl.duitang.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1660887492&t=316c717e8b3e03682f56422dfa92fba0", "3.jpg");
      test01.start();
      test02.start();
      test03.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方法出现异常");
      }
      }
      }

      结果:
      image

image

下载成功,注意:要先导入commons-IO包
验证可得:start()方法没有按程序顺序执行,先后顺序看CPU调度的执行情况
  1. Runnable接口 :实现Runnable接口

    package com.thread;
    //创建线程方式2:实现Runnable接口,重写run()方法,执行线程需要丢入Runnable接口实现类,调用start()方法
    public class ThreadTest03 implements Runnable{
    @Override
    public void run() {
    for (int i = 0; i < 20; i++) {
    System.out.println("i am watch code:" + i);
    }
    }
    public static void main(String[] args) {
    //创建Runnable接口实现类对象
    ThreadTest03 threadTest03 = new ThreadTest03();
    //创建线程对象,通过线程对象来开启我们的线程 代理
    new Thread(threadTest03).start();
    for (int i = 0; i < 20; i++) {
    System.out.println("我在学习多线程:" + i);
    }
    }
    }
  2. Callable接口:实现Callable接口

    package com.thread;
    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 CallableTest01 implements Callable<Boolean> {
    private String url;
    private String name;
    public CallableTest01(String url, String name) {
    this.url = url;
    this.name = name;
    }
    //下载图片线程的执行体
    @Override
    public Boolean call() {
    webDownloader01 webDownloader01 = new webDownloader01();
    webDownloader01.downloader(url, name);
    System.out.println("下载了文件名为:" + name);
    return true;
    }
    public static void main(String[] args) throws ExecutionException, InterruptedException {
    CallableTest01 t1 = new CallableTest01("https://gimg2.baidu.com/image_search/src=http%3A%2F%2F5b0988e595225.cdn.sohucs.com%2Fimages%2F20190513%2F7cb9afc55171450eaa17604c5e4fc765.jpeg&refer=http%3A%2F%2F5b0988e595225.cdn.sohucs.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1660887492&t=030319dc87f0a7ce129ba69ab5bda5e5", "1.jpg");
    CallableTest01 t2 = new CallableTest01("https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg.youxi369.com%2Farticle%2Fcontents%2F2021%2F01%2F07%2F2021010740735362.jpg&refer=http%3A%2F%2Fimg.youxi369.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1660887492&t=97f6180af2ab121b58f15c7f300a0ebb", "2.jpg");
    CallableTest01 t3 = new CallableTest01("https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fb-ssl.duitang.com%2Fuploads%2Fitem%2F201802%2F12%2F20180212094815_MWrv5.jpeg&refer=http%3A%2F%2Fb-ssl.duitang.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1660887492&t=316c717e8b3e03682f56422dfa92fba0", "3.jpg");
    //创建执行服务
    ExecutorService ser = Executors.newFixedThreadPool(3);
    //提交执行
    Future<Boolean> r1 = ser.submit(t1);
    Future<Boolean> r2 = ser.submit(t2);
    Future<Boolean> r3 = ser.submit(t3);
    //获取结果
    boolean rs1 = r1.get();
    boolean rs2 = r2.get();
    boolean rs3 = r3.get();
    //关闭服务
    ser.shutdownNow();
    }
    }
    //下载器
    class webDownloader01 {
    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方法出现异常");
    }
    }
    }

并发问题

买火车票模拟代码:

package com.thread;
import java.nio.channels.NonReadableChannelException;
//多个线程操作同一个对象
//买火车票的例子
public class TreadTest04 implements Runnable {
//火车票
private int ticketNum = 10;
@Override
public void run() {
while (true) {
if (ticketNum <= 0) {
break;
}
//模拟延时
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "-->拿到了第 " + ticketNum-- + " 张票");
}
}
public static void main(String[] args) {
TreadTest04 treadTest04 = new TreadTest04();
new Thread(treadTest04, "小明").start();
new Thread(treadTest04, "老师").start();
new Thread(treadTest04, "黄牛").start();
}
}

运行结果:
image

问题:多个线程操作同一个资源的情况下,线程不安全,数据紊乱


案例:龟兔赛跑

  1. 首先来个赛道距离,然后要离终点越来越近

  2. 判断比赛是否结束

  3. 打印出胜利者

  4. 龟兔赛跑开始

  5. 要保证乌龟胜利,兔子需要睡觉,用sleep来模拟 兔子睡觉

  6. 乌龟赢得比赛

    package com.thread;
    public class Race implements Runnable {
    //胜利者
    private static String winner;
    @Override
    public void run() {
    for (int i = 1; i <= 100; i++) {
    //模拟兔子睡觉
    if (Thread.currentThread().getName().equals("兔子") && i % 10 == 0) {
    try {
    Thread.sleep(10);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    }
    //判断比赛是否结束
    boolean flag = gameOver(i);
    if (flag) {
    break;
    }
    System.out.println(Thread.currentThread().getName() + "--> 跑了 " + i + " 步");
    }
    }
    //判断比赛是否结束
    public boolean gameOver(int steps) {
    //判断是否存在胜利者
    if (winner != null) {//胜利者已经存在
    return true;
    }
    {
    if (steps == 100) {
    winner = Thread.currentThread().getName();
    System.out.println("winner is " + winner);
    return true;
    }
    }
    return false;
    }
    public static void main(String[] args) {
    Race race = new Race();
    new Thread(race, "兔子").start();
    new Thread(race, "乌龟").start();
    }
    }
posted @   每年桃花开的时候  阅读(24)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示