多线程之线程实现和状态

多线程

1.多线程Thread概述

1.1线程简介

  • 多任务: 同时做多件事情(一遍看电视一遍吃饭);不过,看起来是多个任务同时在做,其实本质上我们的大脑在同一时间依旧是只做了一件事(速度太快了)

  • 多线程: 高速收费口,只有一个收费口,车太多了会导致排队堵塞,通过效率太低了;为了解决堵塞,又新开了多个收费口,可以同时缴费通过。

  • 进程(Process): 执行程序的一次执行过程,动态概念 包含若干个线程,至少一个。操作系统中运行的程序就是进程比如:QQ,播放器,游戏,IDE…;

  • 普通方法调用和多线程示例图

    • 执行过程中,调用主线程后,主线程调用start方法执行子线程,子线程执行run方法,主线程和子线程同时并行交替执行
      hhh
  • Process 进程 和 Thread 线程

    • 说起进程,就不得不说下程序。程序是指令和数据的有序集合,其本身没有任何运行的含义,是一个静态的概念。

    • 而进程则是执行程序的依次执行过程,它是一个动态的概念。是系统资源分配的单位。

    • 通常在一个进程中可以包含若干个线程,当然一个进程中至少有一个线程,不然没有存在的意义。线程是CPU调度和执行的单位

    • 注意: 很多多线程是模拟出来的,真正的多线程是指有多个cpu,即多核,如服务器。如果是模拟出来的多线程,即在一个cpu的情况下,在同一个时间点,cpu只能执行一个代码,因为切换的很快,所以就有同时执行的错局。

  • 核心概念:

    • 线程就是独立的执行路径
    • 在程序运行时,即使没有自己创建线程,后台也会有多个线程,比如主线程,GC线程
    • main()称之为主线程,为系统的入口,用于执行整个程序

    • 在一个进程中,如果开辟了多个线程,线程的运行是由调度器(cpu)安排调度的,调度器是与操作系统紧密相关的,先后顺序是不能人为干预的

    • 对同一份资源操作时,会存在资源抢夺的问题,需要加入并发控制

    • 线程会带来额外的开销,如CPU调度时间,并发控制开销

    • 每个线程在自己的工作内存交互,内存控制不当会造成数据不一致

2. 线程实现

2.1 线程三种创建方式

  • 创建线程的三种方式:
    • (1)继承Thread类,重写run方法。
    • (2)实现Runnable接口。
    • (3)实现Callable接口
  • 三种创建方式
    iii

2.2 线程实现->继承Thread类

  • 自定义线程类继承Thread类
  • 重写run()方法,编写线程执行体
  • 创建线程对象,调用start()方法启动线程
  • 注意: 线程不一定立即执行,CPU安排调度(这个方式不建议使用,存在面向对象OOP单继承局限性)
2.21 继承Thread类创建线程代码演示
package threads.demo01;
//创建线程方式一:继承thread 类,重写run() 方法,调用start 开启线程
public class TestThread extends Thread{
    @Override
    public void run() {

        //run 方法线程体
        for (int i = 0; i < 10; i++) {
            System.out.println("我再测试run方法"+i);

        }

    }

    public static void main(String[] args) {
        //main 线程,主线程

        //创建一个线程对象
        TestThread testThread = new TestThread();
        //调用start()方法开启线程
        testThread.start();

        for (int i = 0; i < 10; i++) {
            System.out.println("我在学习多线程"+i);
        }

    }
}


运行结果
我在学习多线程0
我再测试run方法0
我在学习多线程1
我再测试run方法1
我在学习多线程2
我再测试run方法2
我在学习多线程3
我在学习多线程4
我在学习多线程5
我在学习多线程6
我再测试run方法3
我再测试run方法4
我再测试run方法5
我再测试run方法6
我再测试run方法7
我再测试run方法8
我再测试run方法9
我在学习多线程7
我在学习多线程8
我在学习多线程9


2.22 实现多线程同步下载图片代码演示

package threads.demo01;

import org.apache.commons.io.FileUtils;

import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
//练习thread ,实现多线程同步下载图片

public class TestThread1 extends Thread{
    private String url;//网络图片地址
    private String name;//保存的文件名

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

    @Override
    //下载图片线程的执行体
    public void run() {//(2)使用多线程方法下载

        // 进入线程后,创建一个下载器,下载器通过 downloader 方法,传入 url 和 name 下载相应的资源
        WebDownloard webDownloard = new WebDownloard();
        webDownloard.dewnloard(url,name);
        System.out.println("下载的文件名:"+name);
    }

    public static void main(String[] args) {

        //(3)主方法内填入网址及名字,并运行
        TestThread1 testThread1 = new TestThread1("https://img6.bdstatic.com/img/image/pcindex/sunjunpchuazhoutu.JPG","test1.jpg");
        TestThread1 testThread2 = new TestThread1("https://img6.bdstatic.com/img/image/pcindex/sunjunpchuazhoutu.JPG","test2.jpg");
        TestThread1 testThread3 = new TestThread1("https://img6.bdstatic.com/img/image/pcindex/sunjunpchuazhoutu.JPG","test3.jpg");

             testThread1.start();
             testThread2.start();
             testThread3.start();

    }


}

//下载器(1)
class WebDownloard{
    //下载方法
    public void dewnloard(String url,String name)  {


        try {
            FileUtils.copyURLToFile(new URL(url),new File(name));
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("IO 异常,图片copy失败");
        }
    }

}


运行结果
下载的文件名:test3.jpg
下载的文件名:test2.jpg
下载的文件名:test1.jpg


2.3 线程实现->实现Runnable接口

  • 自定义线程类实现Runnable接口
  • 实现run()方法,编写线程执行体
  • 创建线程对象,调用start()方法启动对象
  • 推荐使用 Runnable,因为 Java 单继承的局限性,方便 一个对象被多个线程使用

  • Thread和Runnable对比

  • 继承Thred类:

    • 子类继承Thread类具备多线程能力
    • 启动线程:子类对象.start()
    • 不建议使用:避免OOP单继承局限性
  • 实现Runnable接口:

    • 实现接口Runnable具有多线程能力
    • 启动线程:传入目标对象+Thread对象.start()
    • 推荐使用:避免单继承局限性,灵活方便,方便同一个对象被多个线程使用
2.31 实现Runnable接口接口创建线程代码演示
package threads.demo01;
//实现Runnable:实现Runnable接口,重写run方法,执行线程需要丢入Runnable接口实现类,调用start方法
public class RunnableTest implements Runnable{
    @Override
    public void run() {
        //run方法线程体
        for (int i = 0; i < 10; i++) {
            System.out.println("线程的run方法");
        }

    }


    public static void main(String[] args) {
        //main线程,主线程

        for (int i = 0; i < 10; i++) {
            System.out.println("我在学习多线程");
        }

        //创建一个线程对象
        RunnableTest runnableTest = new RunnableTest();
        //创建线程对象,通过线程对象来开启我们的多线程,代理
        // Thread thread = new Thread(runnableTest);
        // thread.start();


        //简写
        //调用start()开启线程
        new Thread(runnableTest).start();
    }
}


运行结果
我在学习多线程
我在学习多线程
我在学习多线程
我在学习多线程
我在学习多线程
我在学习多线程
我在学习多线程
我在学习多线程
我在学习多线程
我在学习多线程
线程的run方法
线程的run方法
线程的run方法
线程的run方法
线程的run方法
线程的run方法
线程的run方法
线程的run方法
线程的run方法
线程的run方法



2.32 实现Runnable接口copy图片代码演示

package threads.demo01;

import org.apache.commons.io.FileUtils;

import java.io.File;
import java.io.IOException;
import java.net.URL;
//使用实现Runnable接口,重写run方法,copy图片
public class RunnableCopyTest implements Runnable{

    private String url;
    private String name;

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

    @Override
    public void run() {
        DownloaderUrl downloaderUrl = new DownloaderUrl();
        downloaderUrl.download(url,name);
        System.out.println("文件名称为:"+name);

    }


    public static void main(String[] args) {
        RunnableCopyTest t1 = new RunnableCopyTest("https://img6.bdstatic.com/img/image/pcindex/sunjunpchuazhoutu.JPG", "amage1.jpg");
        RunnableCopyTest t2 = new RunnableCopyTest("https://img6.bdstatic.com/img/image/pcindex/sunjunpchuazhoutu.JPG", "amage2.jpg");
        RunnableCopyTest t3 = new RunnableCopyTest("https://img6.bdstatic.com/img/image/pcindex/sunjunpchuazhoutu.JPG", "amage3.jpg");

        new Thread(t1).start();
        new Thread(t2).start();
        new Thread(t3).start();

    }

}

class DownloaderUrl{

    public void download(String url,String name){

        try {
            FileUtils.copyURLToFile(new URL(url),new File(name));
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("IO异常,文件复制失败");
        }
    }
}


运行结果
文件名称为:amage3.jpg
文件名称为:amage2.jpg
文件名称为:amage1.jpg



2.33 并发问题买火车票案例代码演示

package threads.demo01;
//多线程同时操作同一个对象
//买火车票的例子

// 问题:产生了并发问题,多个线程同时对一个资源进行操作,线程不安全,发生了数据紊乱
public class TicketRunnableTest implements Runnable{

    // 票数
    private int ticketNumb=20;

    @Override
    public void run() {

        while (true){
            if(ticketNumb<=0){
                break;
            }
            // 模拟演示
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"抢到了第"+ticketNumb--+"张火车票");
        }

    }

    public static void main(String[] args) {
        TicketRunnableTest t1 = new TicketRunnableTest();
        new Thread(t1,"张三").start();
        new Thread(t1,"李四").start();
        new Thread(t1,"黄牛党").start();

    }

}


运行结果
黄牛党抢到了第20张火车票
张三抢到了第19张火车票
李四抢到了第18张火车票
黄牛党抢到了第17张火车票
张三抢到了第16张火车票
李四抢到了第15张火车票
黄牛党抢到了第14张火车票
张三抢到了第13张火车票
李四抢到了第12张火车票
黄牛党抢到了第11张火车票
张三抢到了第10张火车票
李四抢到了第9张火车票
黄牛党抢到了第8张火车票
张三抢到了第7张火车票
李四抢到了第6张火车票
黄牛党抢到了第5张火车票
张三抢到了第4张火车票
李四抢到了第3张火车票
黄牛党抢到了第2张火车票
张三抢到了第1张火车票
李四抢到了第0张火车票
黄牛党抢到了第-1张火车票


2.34 模拟龟兔赛跑案例代码演示

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

/*
1.定义一个赢家
2.设定赛道
3.判断输赢
4.确定比赛是否结束

 */
package threads.demo01;
// 模拟龟兔赛跑
public class RaceTest implements Runnable{

    // 胜利者
    private static String winner;// static 保证在调用时只有一个 winner

    @Override
    public void run() {
        for (int i = 0; i <= 100; i++) {//赛道

             // 模拟兔子休息
            if(Thread.currentThread().getName().equals("兔子")&&i%10==0){

                try {
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }


            // 判断比赛是否结束
            Boolean flag = gameOver(i);
            // 如果比赛结束 停止程序
            if (flag){
                break;
            }
            System.out.println(Thread.currentThread().getName()+"跑了"+i+"步");

        }

    }
    //判断输赢
    //假如winner不为空就说明已经有冠军
    //假如steps>=100,则产生冠军
    //否则比赛还没结束

    //判断是否完成比赛
    public Boolean  gameOver(int step){
        // 判断是否有胜利者
        if (winner!=null){// 已经存在胜利者
            return true;
        }
        {//这里{}只是一个代码块,可以不写,直接if语句,方便理解。写了美观
            if (step >= 100) {
                winner = Thread.currentThread().getName();
                System.out.println("胜利者是:" + winner);
                return true;
            }
        }
        return false;

    }


    public static void main(String[] args) {

        RaceTest raceTest = new RaceTest();
        new Thread(raceTest,"兔子").start();
        new Thread(raceTest,"乌龟").start();
    }


}



运行结果
乌龟跑了0步
乌龟跑了1步
乌龟跑了2步
乌龟跑了3步
乌龟跑了4步
乌龟跑了5步
乌龟跑了6步
乌龟跑了7步
乌龟跑了8步
乌龟跑了9步
乌龟跑了10步
乌龟跑了11步
乌龟跑了12步
乌龟跑了13步
乌龟跑了14步
乌龟跑了15步
乌龟跑了16步
乌龟跑了17步
乌龟跑了18步
乌龟跑了19步
乌龟跑了20步
乌龟跑了21步
乌龟跑了22步
乌龟跑了23步
乌龟跑了24步
乌龟跑了25步
乌龟跑了26步
乌龟跑了27步
乌龟跑了28步
乌龟跑了29步
乌龟跑了30步
乌龟跑了31步
乌龟跑了32步
乌龟跑了33步
乌龟跑了34步
乌龟跑了35步
乌龟跑了36步
乌龟跑了37步
乌龟跑了38步
乌龟跑了39步
乌龟跑了40步
乌龟跑了41步
乌龟跑了42步
乌龟跑了43步
乌龟跑了44步
兔子跑了0步
乌龟跑了45步
兔子跑了1步
乌龟跑了46步
兔子跑了2步
兔子跑了3步
乌龟跑了47步
兔子跑了4步
乌龟跑了48步
兔子跑了5步
乌龟跑了49步
兔子跑了6步
乌龟跑了50步
兔子跑了7步
兔子跑了8步
乌龟跑了51步
兔子跑了9步
乌龟跑了52步
乌龟跑了53步
乌龟跑了54步
乌龟跑了55步
乌龟跑了56步
乌龟跑了57步
乌龟跑了58步
乌龟跑了59步
乌龟跑了60步
乌龟跑了61步
乌龟跑了62步
乌龟跑了63步
乌龟跑了64步
乌龟跑了65步
乌龟跑了66步
乌龟跑了67步
乌龟跑了68步
乌龟跑了69步
乌龟跑了70步
乌龟跑了71步
乌龟跑了72步
乌龟跑了73步
乌龟跑了74步
乌龟跑了75步
乌龟跑了76步
乌龟跑了77步
乌龟跑了78步
乌龟跑了79步
乌龟跑了80步
乌龟跑了81步
乌龟跑了82步
乌龟跑了83步
乌龟跑了84步
乌龟跑了85步
乌龟跑了86步
乌龟跑了87步
乌龟跑了88步
乌龟跑了89步
兔子跑了10步
乌龟跑了90步
兔子跑了11步
乌龟跑了91步
乌龟跑了92步
乌龟跑了93步
乌龟跑了94步
乌龟跑了95步
乌龟跑了96步
乌龟跑了97步
兔子跑了12步
乌龟跑了98步
兔子跑了13步
乌龟跑了99步
兔子跑了14步
胜利者是:乌龟



2.4 线程实现->实现Callable接口(了解即可)

  • 1.实现Callable接口,需要返回值类型
  • 2.重写call方法,需要抛出异常
  • 3.创建目标对象
  • 4.创建执行服务: ExecutorService ser = Executors.newFixedThreadPool(1);
  • 5.提交执行: Future< Boolean > result1 = ser.submit(t1);
  • 6.获取结果: boolean r1 = result1.get();
  • 7.关闭服务: ser.shutdownNow();
2.41 利用callable 改造下载图片案例代码演示
package threads.demo01;

import org.apache.commons.io.FileUtils;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.concurrent.*;
//线程创建方式三:实现calladle接口
//重写实现的是call方法,并需要设置返回Boolean值(也可以设置其他类型返回值)
/*
callable的好处
  1.可以定义返回值
  2.可以抛出异常
*/
/*
    1. 实现Callable接口
    2.实现call方法
    3.创建执行服务
    4.提交执行
    5.获取结果
    6.关闭服

 */
//利用callable 改造下载图片案例
public class TestCallable implements Callable<Boolean> {

    private  String url;//网络图片地址
    private  String name;//保存的文件名

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

    //下载图片线程的执行体
    @Override
    public Boolean call() {
        WebDownLoader1 webDownLoader1 = new WebDownLoader1();
        webDownLoader1.DownLoader(ur
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值