java多线程:线程创建

多线程

简介

  • 进程process和线程thread:
    • 线程是指程序的一次相对独立的运行过程,是系统调度的最小单位动态概念)
    • 进程是指系统分配的基本对象,是拥有资源的最小实体,在传统os中也是调度的最小单位
  • 一个进程可以有多个线程
  • 线程就是独立的执行路径
  • 在程序运行时,即使没有自己创建线程,后台也会有多个线程,如主线程,gc线程
  • main()主线程:系统的入口,用于执行整个程序
  • 在一个进程中,如果开辟了多个线程,线程的运行由调度器安排调度,调度器是与操作系统紧密相关的,先后顺序是不能人为干预的
  • 对同一份资源操作时,会存在资源抢夺的问题,需要加入并发控制
  • 线程会带来额外的开销,如cpu调度时间,并发控制开销
  • 每个线程在自己的工作内存交互,内存控制不当会造成数据不一致

线程创建

  • 三种创建方式:
    • Thread class:继承Thread类
    • Runnable接口
    • Callable接口

1. 继承Thread类

  1. 自定义线程类继承Thread类
  2. 重写run()方法,编写线程执行体
  3. 创建线程对象,调用start()方法启动线程
  • 总结:注意线程开启不一定立即执行,由cpu调度执行

    public class TestThread01 extends Thread{
        @Override
        public void run() {
            //run方法线程体
            for (int i = 0; i < 20; i++) {
                System.out.println("我在run方法"+i);
            }
        }
    
        public static void main(String[] args) {
            //main线程,主线程
            //创建一个线程对象
            TestThread01 testThread01 = new TestThread01();
            //调用start()方法开启线程
            testThread01.start();
            for (int i = 0; i < 20; i++) {
                System.out.println("我在学习多线程"+i);
            }
        }
    }
    
    
  • 练习Thread,实现多线程同步下载图片

  • Commons IO jar包 是针对开发IO流功能的工具类库

    //练习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 t1 = new TestThread02("https://images.cnblogs.com/cnblogs_com/wu-myblog/1903726/o_201220131410%E7%82%8E%E6%9F%B1.jpg", "1.jpg");
            TestThread02 t2 = new TestThread02("https://images.cnblogs.com/cnblogs_com/wu-myblog/1903726/o_201220141715cfa7f6a80b881192c0eh.jpg", "2.jpg");
            t1.start();
            t2.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方法出现问题");
            }
        }
    }
    //下载了文件名为:2.jpg
    //下载了文件名为:1.jpg
    

2. 实现Runnable接口

  1. 定义一个类实现Runnable接口
  2. 实现run()方法,编写线程执行体
  3. 创建线程对象,调用start()方法启动线程
  • 推荐使用Runnable接口

    public class TestThread03 implements Runnable{
        @Override
        public void run() {
            //run方法线程体
            for (int i = 0; i < 20; i++) {
                System.out.println("我在run方法"+i);
            }
        }
    
        public static void main(String[] args) {
            //main线程,主线程
            //创建一个Runnbale接口的实现类对象
            TestThread03 testThread03 = new TestThread03();
            //创建线程对象,通过线程对象来开启线程: 代理
    //        Thread thread = new Thread(testThread03);
    //        thread.start();
            //简写:
            new Thread(testThread03).start();
    
            for (int i = 0; i < 20; i++) {
                System.out.println("我在学习多线程"+i);
            }
        }
    }
    
    

对比

继承Thread类

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

实现Runnable接口

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

实例:抢车票(并发问题)

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

    //多个线程同时操作一个对象
    //买火车票的例子
    //多个线程操作同一个资源的情况下,线程不安全,数据紊乱
    public class TestThread04 extends Thread{
        //票的数量
        private int ticketNums=10;
        @Override
        public void run() {
            while (true) {
                if (ticketNums <= 0) {
                    break;
                }
                //模拟延时
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+"-->拿到了"+ticketNums--+"票");
            }
        }
    
        public static void main(String[] args) {
            TestThread04 testThread04 = new TestThread04();
            new Thread(testThread04,"aa").start();
            new Thread(testThread04,"bb").start();
            new Thread(testThread04,"cc").start();
        }
    }
    //bb-->拿到了10票
    //cc-->拿到了8票
    //aa-->拿到了9票
    //cc-->拿到了7票
    //aa-->拿到了6票
    //bb-->拿到了6票
    //cc-->拿到了5票
    //aa-->拿到了4票
    //bb-->拿到了3票
    //bb-->拿到了2票
    //aa-->拿到了1票
    //cc-->拿到了0票
    

3. 实现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();
  • Callable的好处:

    • 可以定义返回值
    • 可以抛出异常
    public class TestCallable implements Callable<Boolean> {
        private String url;//网络图片地址
        private String name;//保存的文件名
    
        public TestCallable(String url, String name) {
            this.url = url;
            this.name = name;
        }
    
        @Override
        public Boolean call() {
            WebDownloader2 webDownloader = new WebDownloader2();
            webDownloader.downloader(url,name);
            System.out.println("下载了文件名为:"+name);
            return true;
        }
    
        public static void main(String[] args) throws ExecutionException, InterruptedException {
            TestCallable t1 = new TestCallable("https://images.cnblogs.com/cnblogs_com/wu-myblog/1903726/o_201220131410%E7%82%8E%E6%9F%B1.jpg", "1.jpg");
            TestCallable t2 = new TestCallable("https://images.cnblogs.com/cnblogs_com/wu-myblog/1903726/o_201220141715cfa7f6a80b881192c0eh.jpg", "2.jpg");
            //创建执行服务
            ExecutorService ser = Executors.newFixedThreadPool(2);
            //提交执行
            Future<Boolean> r1 = ser.submit(t1);
            Future<Boolean> r2 = ser.submit(t2);
            //获取结果
            boolean rs1 = r1.get();
            boolean rs2 = r2.get();
            //关闭服务
            ser.shutdownNow();
    
        }
    }
    
    //下载器
    class WebDownloader2{
        //下载方法
        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方法出现问题");
            }
        }
    }
    //下载了文件名为:2.jpg
    //下载了文件名为:1.jpg
    
posted @ 2021-01-21 11:05  迪迦是真的  阅读(132)  评论(0)    收藏  举报
//复制代码按钮 //代码行号 //评论