• Java 多线程


    基本概念

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

    多线程实现方式

    • 方式一: 继承Thread类
    package com.smile.test.thread;
    
    import org.apache.commons.io.FileUtils;
    
    import java.io.File;
    import java.io.IOException;
    import java.net.URL;
    
    public class TestThread extends Thread{
        private String url;
        private String fileName;
    
        public TestThread(String url, String fileName){
            this.url = url;
            this.fileName = fileName;
        }
        @Override
        public void run() {
            Downloader downloader = new Downloader();
            downloader.downloanImage(url,fileName);
        }
    
        class Downloader{
            private void downloanImage(String url, String fileName) {
                try {
                    FileUtils.copyURLToFile(new URL(url), new File(fileName));
                    System.out.println(fileName + "  has already downloaded");
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        public static void main(String[] args) {
            TestThread t1 = new TestThread("http://www.4kbizhi.com/d/file/2020/06/05/small155838zgByl1591343918.jpg","one.jpg");
            TestThread t2 = new TestThread("http://pic.netbian.com/uploads/allimg/190824/212516-1566653116f355.jpg","two.jpg");
            TestThread t3 = new TestThread("http://pic.netbian.com/uploads/allimg/200604/001849-15912011292fcb.jpg","three.jpg");
            t1.start();
            t2.start();
            t3.start();
        }
    }
    
    输出:
    two.jpg  has already downloaded
    one.jpg  has already downloaded
    three.jpg  has already downloaded
    
    Process finished with exit code 0
    
    • 方式二: 实现Runnable接口
    package com.smile.test.thread;
    
    import org.apache.commons.io.FileUtils;
    
    import java.io.File;
    import java.io.IOException;
    import java.net.URL;
    
    public class TestThread implements Runnable{
        private String url;
        private String fileName;
    
        public TestThread(String url, String fileName){
            this.url = url;
            this.fileName = fileName;
        }
        @Override
        public void run() {
            Downloader downloader = new Downloader();
            downloader.downloanImage(url,fileName);
        }
    
        class Downloader{
            private void downloanImage(String url, String fileName) {
                try {
                    FileUtils.copyURLToFile(new URL(url), new File(fileName));
                    System.out.println(fileName + "  has already downloaded");
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        public static void main(String[] args) {
            TestThread t1 = new TestThread("http://www.4kbizhi.com/d/file/2020/06/05/small155838zgByl1591343918.jpg","one.jpg");
            TestThread t2 = new TestThread("http://pic.netbian.com/uploads/allimg/190824/212516-1566653116f355.jpg","two.jpg");
            TestThread t3 = new TestThread("http://pic.netbian.com/uploads/allimg/200604/001849-15912011292fcb.jpg","three.jpg");
            new Thread(t1).start();
            new Thread(t2).start();
            new Thread(t3).start();
        }
    }
    
    

    龟兔赛跑问题:

    package com.smile.test.thread;
    
    public class Race implements Runnable {
        private static String winner;
        @Override
        public void run() {
            for (int i = 0; i <= 1000; i++) {
                // 让兔子每50步睡1ms
                if (Thread.currentThread().getName().equals("兔子") && i%50 == 0) {
                    try {
                        Thread.sleep(1);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                // 判断赛跑是否已经结束
                if (gameOver(i)) {
                    break;
                }
                System.out.println(Thread.currentThread().getName() + "跑了" + i + '步');
            }
        }
    
        public static void main(String[] args) {
            Race race = new Race();
            new Thread(race, "兔子").start();
            new Thread(race, "乌龟").start();
        }
        // 判断赛跑是否已经结束
        private boolean gameOver(int step) {
            if (winner != null){
                return true;
            } else if (step >= 1000){
                winner = Thread.currentThread().getName();
                System.out.println("比赛胜利者是" + winner);
                return true;
            } else {
                return false;
            }
        }
    }
    
    • 方式三: 实现Callable接口
    package com.smile.test.thread;
    
    import org.apache.commons.io.FileUtils;
    
    import java.io.File;
    import java.io.IOException;
    import java.net.URL;
    import java.util.concurrent.*;
    // 实现Callable接口,需要定义返回值类型.
    public class TestCallable implements Callable<Boolean> {
        private String url;
        private String fileName;
        // 构造方法
        public TestCallable(String url, String fileName){
            this.url = url;
            this.fileName = fileName;
        }
        // 重写call()
        @Override
        public Boolean call() {
            TestThread.Downloader downloader = new TestThread.Downloader();
            downloader.downloadImage(url,fileName);
            return true;
        }
        class Downloader{
            private void downloadImage(String url, String fileName) {
                try {
                    FileUtils.copyURLToFile(new URL(url), new File(fileName));
                    System.out.println(fileName + "  has already downloaded");
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        public static void main(String[] args) throws ExecutionException, InterruptedException {
            TestCallable t1 = new TestCallable("http://www.4kbizhi.com/d/file/2020/06/05/small155838zgByl1591343918.jpg","one.jpg");
            TestCallable t2 = new TestCallable("http://pic.netbian.com/uploads/allimg/190824/212516-1566653116f355.jpg","two.jpg");
            TestCallable t3 = new TestCallable("http://pic.netbian.com/uploads/allimg/200604/001849-15912011292fcb.jpg","three.jpg");
    
            // 创建执行服务
            ExecutorService service = Executors.newFixedThreadPool(3);
            // 提交执行
            Future<Boolean> submit1 = service.submit(t1);
            Future<Boolean> submit2 = service.submit(t2);
            Future<Boolean> submit3 = service.submit(t3);
            //获取返回结果
            System.out.println(submit1.get());
            System.out.println(submit2.get());
            System.out.println(submit3.get());
            // 关闭执行服务
            service.shutdownNow();
        }
    }
    
    输出结果:
    two.jpg  has already downloaded
    one.jpg  has already downloaded
    true
    true
    three.jpg  has already downloaded
    true
    
    Process finished with exit code 0
    

    好处:

    1. 能定义返回类型,获取返回值.
    2. 能抛出异常.
  • 相关阅读:
    vscode常用插件
    2019前端面试总结
    用户注册登录的逻辑
    Vue项目各个文件夹的作用
    Gulp & webpack 配置详解
    Webpack 配置入门
    开始一个React项目(一)一个最简单的webpack配置
    资源加载过程
    关于Netty Pipeline中Handler的执行顺序问题
    解压版中文乱码问题MYSQL中文乱码
  • 原文地址:https://www.cnblogs.com/lvzl/p/14665663.html
Copyright © 2020-2023  润新知