第一个多线程程序

创建线程

假设银行大厅共有四台出号机,即有四个线程在工作。用程序模拟叫号的过程,约定当天最多受理8笔业务。

继承Thread

/**
 * 柜台叫号:继承Thread、重写run方法
 */
public class TicketWindow extends Thread { // 1.继承Thread类
    // 柜台名称
    private final String name;
    // 最多受理50笔业务
    private final int MAX = 8;

    private int index = 1;

    public TicketWindow(String name) {
        this.name = name;
    }

    // 2.重写run方法
    @Override
    public void run() {
        while (index <= MAX) {
            System.out.println("柜台:" + name + " 当前处理的业务号为 " + (index++));
        }
    }

    public static void main(String[] args) {
        // 3.创建实例
        TicketWindow ticketWindow1 = new TicketWindow("No.1");
        // 4.调用start,开启线程
        ticketWindow1.start();

        TicketWindow ticketWindow2 = new TicketWindow("No.2");
        ticketWindow2.start();

        TicketWindow ticketWindow3 = new TicketWindow("No.3");
        ticketWindow3.start();

        TicketWindow ticketWindow4 = new TicketWindow("No.4");
        ticketWindow4.start();
    }
}

输出结果:

柜台:No.2 当前处理的业务号为 1
...
柜台:No.2 当前处理的业务号为 5
柜台:No.3 当前处理的业务号为 1
...
柜台:No.3 当前处理的业务号为 6
柜台:No.4 当前处理的业务号为 1
柜台:No.1 当前处理的业务号为 1
柜台:No.1 当前处理的业务号为 2
柜台:No.4 当前处理的业务号为 2
...
柜台:No.4 当前处理的业务号为 8
柜台:No.3 当前处理的业务号为 7
柜台:No.2 当前处理的业务号为 6
柜台:No.2 当前处理的业务号为 7
柜台:No.2 当前处理的业务号为 8
柜台:No.3 当前处理的业务号为 8
柜台:No.1 当前处理的业务号为 3
..
柜台:No.1 当前处理的业务号为 8

Thread类的run方法是不能共享的,也就是说A线程不能把B线程的run方法当作自己的执行单元!由于ticketWindow1ticketWindow2...是不同的实例,index不共享,导致四个线程的index各自从1增至8

无论TicketWindow被实例化多少次,只需要保证index是唯一的即可,可以使用static去修饰index(只实例化一个)以达到目的:

private static int index = 1;

输出结果:

柜台:No.2 当前处理的业务号为 1
柜台:No.2 当前处理的业务号为 5
柜台:No.2 当前处理的业务号为 6
柜台:No.2 当前处理的业务号为 7
柜台:No.2 当前处理的业务号为 8
柜台:No.4 当前处理的业务号为 3
柜台:No.1 当前处理的业务号为 4
柜台:No.3 当前处理的业务号为 2

通过对index进行static修饰,做到了多线程下共享资源的唯一性

但是只有一个index共享资源,如果共享资源很多?共享资源要经过一些比较复杂的计算?

static修饰的变量生命周期很长,不可能都使用static修饰

实现Runnable接口

线程的控制业务逻辑的运行彻底分离开来!

Thread类的run方法是不能共享的,也就是说A线程不能把B线程的run方法当作自己的执行单元!而使用Runnable接口则很容易就能实现这一点,使用同一个Runnable的实例构造不同的Thread实例

/**
 * 实现Runnable接口
 */
public class TicketWindowRunnable implements Runnable {
    private int index = 1;
    private final static int MAX = 8;

    /**
     * 将Thread中run的代码逻辑抽取到Runnable的一个实现中
     */
    @Override
    public void run() {
        while (index <= MAX) {
            System.out.println(Thread.currentThread() + " 正在办理的业务号为 " + (index++));
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        // 将业务逻辑的运行
        final TicketWindowRunnable task = new TicketWindowRunnable();
        // 与线程的控制彻底分离开来!
        Thread thread1 = new Thread(task, "1");
        Thread thread2 = new Thread(task, "2");
        Thread thread3 = new Thread(task, "3");
        Thread thread4 = new Thread(task, "4");

        thread1.start();
        thread2.start();
        thread3.start();
        thread4.start();
    }
}

输出结果出现了线程不安全问题:

Thread[3,5,main] 正在办理的业务号为 1
Thread[4,5,main] 正在办理的业务号为 3
Thread[2,5,main] 正在办理的业务号为 2
Thread[1,5,main] 正在办理的业务号为 2
Thread[1,5,main] 正在办理的业务号为 4
Thread[3,5,main] 正在办理的业务号为 5
Thread[4,5,main] 正在办理的业务号为 7
Thread[2,5,main] 正在办理的业务号为 6
Thread[3,5,main] 正在办理的业务号为 8
Thread[2,5,main] 正在办理的业务号为 8
Thread[1,5,main] 正在办理的业务号为 8
Thread[4,5,main] 正在办理的业务号为 8

Thread源码

public class Thread implements Runnable {
    /**
     * Allocates a new Thread object.
     *
     * @param  target
     *         the object whose run method is invoked when this thread is started. 
     * 	       If null, this thread's run method is invoked.
     *
     * @param  name
     *         the name of the new thread
     */
    public Thread(Runnable target, String name) {
        init(null, target, name, 0);
    }
    
    public synchronized void start() {
        /**
         * A zero status value corresponds to state "NEW".
         */
        if (threadStatus != 0)
            throw new IllegalThreadStateException();

        /* Notify the group that this thread is about to be started
         * so that it can be added to the group's list of threads and the group's unstarted count can be decremented. */
        group.add(this);

        boolean started = false;
        try {
            start0();
            started = true;
        } finally {
            try {
                if (!started) {
                    group.threadStartFailed(this);
                }
            } catch (Throwable ignore) {
                /* do nothing. If start0 threw a Throwable then it will be passed up the call stack */
            }
        }
    }

    private native void start0();

    /**
     * If this thread was constructed using a separate Runnable run object, then that Runnable object's run method is called;
     * otherwise, this method does nothing and returns.
     * Thread(Runnable target, String name)
     * Subclasses of Thread should override this method.
     */
    @Override
    public void run() {
        if (target != null) {
            target.run();
        }
    }
}

创建线程只有一种方式那就是构造Thread,而实现线程的执行单元则有两种方式,第一种是重写Threadrun方法;第二种是实现Runnable接口的run方法,并且将Runnable实例用作构造Thread的参数

start()最核心的部分是start0这个本地方法,Thread被构造后的NEW状态,事实上threadStatus这个内部属性为0;不能两次启动Thread,否则就会出现IllegalThreadStateException异常;线程启动后将会被加入到一个ThreadGroup中;一个线程生命周期结束,也就是到了TERMINATED状态,再次调用start方法是不允许的。

Thread采用的模板设计模式

Thread的run()start()是一个比较典型的模板设计模式:父类编写算法结构代码,子类实现逻辑细节。

public class TemplateMethodThread {
    public final void startPrintMessage(String message) { // start()
        System.out.println("##########");
        runWrapPrint(message); // run()
        System.out.println("##########");
    }

    protected void runWrapPrint(String message) {
        System.out.println("runWrapPrint");
    }

    public static void main(String[] args) {
        TemplateMethodThread method1 = new TemplateMethodThread() {
            @Override
            protected void runWrapPrint(String message) {
                System.out.println("method1: Override runWrapPrint");
            }
        };
        method1.startPrintMessage("method1: Hello Thread");

        TemplateMethodThread method2 = new TemplateMethodThread() {
            @Override
            protected void runWrapPrint(String message) {
                System.out.println("method2: Override runWrapPrint");
            }
        };
        method2.startPrintMessage("method2: Hello Thread");
    }
}

使用匿名内部类与Lambda创建线程

// 使用匿名内部类的方式,实现多线程
new Thread(new Runnable() {
  @Override
  public void run() {
    System.out.println(Thread.currentThread().getName() + "新线程创建了!");
  }
}).start();

// 使用Lambda表达式,实现多线程
new Thread(() -> {
    System.out.println(Thread.currentThread().getName() + "新线程创建了!");
  }
).start();
posted @ 2021-03-13 23:47  chenzufeng  阅读(93)  评论(0)    收藏  举报