• 微信公众号:美女很有趣。 工作之余,放松一下,关注即送10G+美女照片!

多线程(Thread)

开发技术 开发技术 4小时前 3次浏览

多线程(Thread)

线程简介

普通方法调用和多线程调用

多线程(Thread)

程序、线程、进程的区别

  • 程序是数据和指令的有序集合,其本身没有任何运行的含义,是一个静态概念。
  • 而进程则是执行一次程序的过程,他是一个动态的过程。是系统资源分配的单位。
  • 通常一个进程有若干个线程,至少也有一个线程,不然没有存在的意义。线程是CPU的调度和执行的单位。

核心概念

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

线程实现(重点)

多线程(Thread)

Thread

  • 自定义线程类继承Thread类
  • 重写run()方法,编写线程执行体
  • 创建线程对象,调用start()方法
//创建线程方式一:继承Thread类,重写run()方法,调用start开启线程
//注意线程开启不一定执行,由CPU调度执行	
public class TestTread01 extends Thread{
    @Override
    public void run() {
        //run方法线程体
        for (int i = 0; i < 20; i++) {
            System.out.println("我在看代码---------"+i);
        }
    }

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

        //创建一个线程对象
        TestTread01 testTread01 = new TestTread01();
        //调用start开启线程
        testTread01.start();
        for (int i = 0; i < 20; i++) {
            System.out.println("我在学习多线程=================="+i);
        }
    }
}

案例:网图下载

  • commons-io-2.6下载地址:https://mvnrepository.com/artifact/commons-io/commons-io/2.6

  • 新建lib文件夹,右键Add as library

    //练习Thread,实现多线程同步下载图片
    public class TestThread02 extends Thread {
        private String name; //网络图片名称
        private String url; //网络图片地址
    
        public TestThread02(String name,String url){
            this.name =name;
            this.url = url;
        }
        @Override
        public void run() {
            WebDownlaoder webDownlaoder = new WebDownlaoder();
            webDownlaoder.dowloader(name,url);
            System.out.println("下载了文件名为:"+name+"文件路径为:"+url);
    
        }
        public static void main(String[] args) {
            TestThread02 t1 = new TestThread02("p1","https://i0.hdslb.com/bfs/sycp/creative_img/202106/ed7a38f96b6fc524ca1885b5f666c01a.jpg");
            TestThread02 t2 = new TestThread02("p2","https://i0.hdslb.com/bfs/sycp/creative_img/202106/c7b90812175dff93fa54cbabefb589ec.jpg");
            TestThread02 t3 = new TestThread02("p3","https://i0.hdslb.com/bfs/feed-admin/3ea111ff41f6850b8e679c49128054bfd0cc5166.jpg");
            t1.start();
            t2.start();
            t3.start();
        }
    }
    //下载器
    class WebDownlaoder{
        //下载方式
        public void dowloader(String name,String url){
            try {
                FileUtils.copyURLToFile(new URL(url), new File(name));
            } catch (IOException e) {
                System.out.println("IO异常,downloader方法出现问题");
            }
        }
    }
    

Runnable

  • 定义MyRunable类实现Runable接口

  • 实现run()方法,编写线程执行体

  • 创建线程对象,调用start()方法启动线程

    //创建线程方式2:实现runable接口,重写run()方法,执行线程需要丢入runable接口实现类,调用start方法
    public class TestRunable03 implements Runnable{
        @Override
        public void run() {
            //run方法线程体
            for (int i = 0; i < 200; i++) {
                System.out.println("我在看代码--------------"+i);
            }
        }
    
        public static void main(String[] args) {
    
            //创建runable接口的实现类对象
            TestRunable03 testRunable03 = new TestRunable03();
            //创建线程对象,通过线程对象来开启我们的线程,代理
    //        Thread thread = new Thread(testRunable03);
    //        thread.start();
            new Thread(testRunable03).start();
    
            for (int i = 0; i < 200; i++) {
                System.out.println("我在学习多线程============="+i);
            }
        }
    }
    
  • 小结

  • 继承Thread类

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

    • 实现接口Runnable具有多线程能力
    • 启动线程:传入目标对象+Thread对象.start()
    • 推荐使用:避免单继承局限性,灵活方便,方便同一个对象被多个线程使用

初始并发问题

案例:购买火车票

/多个线程同时操作同一个对象
//买火车票的例子
//发现问题,多个线程操作同一个对象的时候线程不安全,出现紊乱
public class TestThread04 implements Runnable{
    //票数
    private int tickNums  = 100;

    @Override
    public void run() {
        while (true){
            if (tickNums <= 0) {
                break;
            }
            //模拟延时
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                System.out.println("休息一会!!!");
            }
            System.out.println(Thread.currentThread().getName()+"-->拿到了第"+tickNums--+"票");
        }
    }

    public static void main(String[] args) {
        TestThread04 ticket = new TestThread04();
        new Thread(ticket,"小明").start();
        new Thread(ticket,"教师").start();
        new Thread(ticket,"黄牛").start();
    }
}

案例:龟兔赛跑

  • 首先来个赛道距离,然后要距离终点越来越近
  • 判断比赛是否结束
  • 打印出胜利者
  • 龟兔赛跑开始
  • 故事中是乌龟赢得,兔子需要先睡觉,所以我们来模拟兔子睡觉
  • 乌龟赢得比赛
//模拟龟兔赛跑
public class TestThread05 implements Runnable{
    //胜利者
    private static  String 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) {
                    System.out.println("兔子疯了!!!");
                }
            }
            //判断比赛是否结束
            boolean falg =gameOver(i);
            //如果比赛结束,就停止了
            if (falg) {
                break;
            }

            System.out.println(Thread.currentThread().getName()+"跑了"+i+"步");
        }
    }
    //判断是否完成比赛
    private 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) {
        TestThread05 testThread05 = new TestThread05();
        new Thread(testThread05,"兔子").start();
        new Thread(testThread05,"乌龟").start();
    }
}

Callable

  • 实现Callable接口,需要返回值
  • 重写call方法,需要抛出异常
  • 创建目标对象
  • 创建执行服务:ExecutorService ser = Executors.newFixedThreadPool(1);
  • 提交执行:Future result1 = ser.submit(t1);
  • 获取结果:boolean rs1 = result1.get();
  • 关闭任务:ser.shutdownNow();
import org.apache.commons.io.FileUtils;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;


public class Callable implements java.util.concurrent.Callable<Boolean> {

    private String name; //网络图片名称
    private String url; //网络图片地址

    public Callable(String name,String url){
        this.name =name;
        this.url = url;
    }
    //下载图片线程执行体
    @Override
    public Boolean call() {
        WebDownlaoder webDownlaoder = new WebDownlaoder();
        webDownlaoder.dowloader(name,url);
        System.out.println("下载了文件名为:"+name+"文件路径为:"+url);
        return true;
    }
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        Callable t1 = new Callable("p1","https://i0.hdslb.com/bfs/sycp/creative_img/202106/ed7a38f96b6fc524ca1885b5f666c01a.jpg");
        Callable t2 = new Callable("p2","https://i0.hdslb.com/bfs/sycp/creative_img/202106/c7b90812175dff93fa54cbabefb589ec.jpg");
        Callable t3 = new Callable("p3","https://i0.hdslb.com/bfs/feed-admin/3ea111ff41f6850b8e679c49128054bfd0cc5166.jpg");
        ExecutorService ser = Executors.newFixedThreadPool(1);
        Future<Boolean> result1 = ser.submit(t1);
        Future<Boolean> result2 = ser.submit(t2);
        Future<Boolean> result3 = ser.submit(t3);
        boolean rs1 = result1.get();
        boolean rs2 = result2.get();
        boolean rs3 = result3.get();
        ser.shutdownNow();
    }
}

//下载器
class WebDownlaoder{
    //下载方式
    public void dowloader(String name,String url){
        try {
            FileUtils.copyURLToFile(new URL(url), new File(name));
        } catch (IOException e) {
            System.out.println("IO异常,downloader方法出现问题");
        }
    }
}

静态代理

  • 真实对象和代理对象要继承同一个接口
  • 代理对象要代理真实角色
public class Statice {
    public static void main(String[] args) {
        Wed wed = new Wed(new You());
        wed.HappyMarry();
    }
}

interface Marry{
    void HappyMarry();
}
//真实角色,你去结婚
class You implements Marry{

    @Override
    public void HappyMarry() {
        System.out.println("超开心!!!");
    }
}
//代理角色
class Wed implements Marry{
    private Marry target;

    public Wed(Marry target){
        this.target = target;
    }

    @Override
    public void HappyMarry() {
        before();
        this.target.HappyMarry();
        after();
    }

    private void after() {
        System.out.println("结婚之后,收尾款");
    }

    private void before() {
        System.out.println("结婚之前,布置现场");
    }
}

线程状态

线程同步(重点)

线程通信问题

高级主题


程序员灯塔
转载请注明原文链接:多线程(Thread)
喜欢 (0)