Java多线程01:线程的创建

程序是指令和数据的有序集合,其本身没有任何运行的含义,是一个静态的概念

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

线程是CPU调度和执行的单位,一个进程至少有一个线程

创建线程的三种方式

1、继承Thread类(不建议使用,避免单继承局限性)

/**
 * 1、自定义线程类继承Thread类
 */
public class Main extends Thread{

    /**
     * 2、重写run()方法,编写线程执行体,也是线程入口点
     */
    @Override
    public void run() {

        for (int i = 0; i < 1000; i++) {
            System.out.println("这是子线程" + i);
        }
    }

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

        /**
         * 3、子类创建线程对象,调用start()方法开启线程
         * main()方法和run()方法会同时运行(但是线程开启不一定立即执行,由CPU调度执行)
         * 如果只是调用thread.run()方法,那就是先执行run()方法再执行main()方法,那就是单线程了
         */
        Main thread = new Main();
        thread.start();

        for (int i = 0; i < 1000; i++) {
            System.out.println("这是主线程" + i);
        }
    }
}

练习:多线程下载图片

import org.apache.commons.io.FileUtils;

import java.io.File;
import java.io.IOException;
import java.net.URL;

/**
 * 使用多线程同步下载图片
 */
public class Main extends Thread{

    String url;
    String name;

    public Main(String url, String name) {

        this.url = url;
        this.name = name;
    }

    /**
     * 多线程
     */
    @Override
    public void run() {

        /**
         * 线程执行体,在这儿创建下载器对象,参数由创建线程对象时传入
         */
        new Download().download(url, name);
        System.out.println("下载文件名为:" + name);
    }

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

        Main h1 = new Main("https://img-blog.csdn.net/20160522165107051", "1.jpg");
        Main h2 = new Main("https://img-blog.csdn.net/20160522165107051", "2.jpg");
        Main h3 = new Main("https://img-blog.csdn.net/20160522165107051", "3.jpg");

        h1.start();
        h2.start();
        h3.start();
    }
}

/**
 * 创建下载器类
 */
class Download{

    public void download(String url, String name){

        try {

            /**
             * 使用common-io.jar包的FileUtils工具类下载,此处传入的参数,看JDK文档是URL类型,也就是URL类的对象
             */
            FileUtils.copyURLToFile(new URL(url), new File(name));
        } catch (IOException e) {

            e.printStackTrace();
            System.out.println("下载出现异常");
        }
    }
}

2、实现Runnable接口(推荐使用)

推荐使用Runnable接口实现类,不仅避免单继承的局限性,还方便同一个对象被多个线程使用

/**
 * 1、自定义线程类实现Runnable接口
 */
public class Main implements Runnable{

    /**
     * 2、重写run()方法
     */
    @Override
    public void run() {

        for (int i = 0; i < 1000; i++) {
            System.out.println("这是子线程" + i);
        }
    }

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

        /**
         * 3、先创建实现类对象
         * 再创建代理类对象,最后调用start()方法开启线程
         */
        Main main = new Main();
        Thread thread = new Thread(main);
        thread.start();

        for (int i = 0; i < 1000; i++) {
            System.out.println("这是主线程" + i);
        }
    }
}

练习:抢火车票

发现问题:多个线程操作同一个资源的情况下,线程不安全,数据紊乱

public class Main implements Runnable {

    /**
     * 火车票剩余数量
     */
    int num = 10;

    @Override
    public void run() {
        
        while (true) {

            if (num <= 0) {
                break;
            }

            /**
             * 模拟延时
             */
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            /**
             * Thread.currentThread().getName()方法获取当前线程的名字
             */
            System.out.println(Thread.currentThread().getName() + "抢到了第" + num-- +"张票");
        }
    }

    public static void main(String[] args) {

        Main main = new Main();

        /**
         * 实现Runnable接口,可以多个线程调用同一个对象
         */
        new Thread(main, "老师").start();
        new Thread(main, "学生").start();
        new Thread(main, "黄牛").start();
    }
}

练习:龟兔赛跑

public class Main implements Runnable {

    boolean flag = false;

    @Override
    public void run() {

        for (int i = 0; i <= 100; i++) {

            /**
             * 谁赢了就结束比赛
             */
            if (flag) {
                break;
            }

            /**
             * 延时模拟兔子每跑十步休息一次
             */
            if (Thread.currentThread().getName() == "兔子" && i % 10 == 0){

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

            System.out.println(Thread.currentThread().getName() + "----->跑了" + i + "步");

            if (i == 100) {

                flag = true;
                System.out.println(Thread.currentThread().getName() + "赢了!");
            }
        }
    }

    public static void main (String[]args){

        Main main = new Main();

        new Thread(main, "兔子").start();
        new Thread(main, "乌龟").start();
    }
}

实现Callable接口(了解即可)

posted @ 2021-09-14 13:56  振袖秋枫问红叶  阅读(52)  评论(0)    收藏  举报