基本概念

  • 进程: 一个正在运行的程序, 其中至少包含一个线程.
  • 线程: 独立的执行路径
  • 在程序运行时, 即使自己没有创建线程,后台也会有多个线程, 如主线程, 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. 能抛出异常.