Java多线程 深入浅出(军队模拟示例)

1.线程与进程

定义

进程是程序(任务)的执行过程,持有资源(内存,共享文件)和线程。进程是系统进行资源分配和调度的一个独立单位。
线程是系统中最小的执行单元(是CPU调度和分派的基本单位),同一进程中有多个线程,线程共享进程的资源。一个时间点上一个cpu只有一个线程在运行

关系

一个进程是动态性的,是一个执行过程。
一个线程可以创建和撤销另一个线程;同一个进程中的多个线程之间可以并发执行。
相对进程而言,线程是一个更加接近于执行体的概念,它可以与同进程中的其他线程共享数据,但拥有自己的栈空间,拥有独立的执行序列。

区别

进程和线程的主要差别在于它们是不同的操作系统资源管理方式。进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程只是一个进程中的不同执行路径。线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间。所以多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差一些。但对于一些要求同时进行并且又要共享某些变量的并发操作,只能用线程,不能用进程。
1) 简而言之,一个程序至少有一个进程,一个进程至少有一个线程.2) 线程的划分尺度小于进程,使得多线程程序的并发性高。3) 另外,进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序的运行效率。4) 线程在执行过程中与进程还是有区别的。每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口。但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。5) 从逻辑角度来看,多线程的意义在于一个应用程序中,有多个执行部分可以同时执行。但操作系统并没有将多个线程看做多个独立的应用,来实现进程的调度和管理以及资源分配。这就是进程和线程的重要区别。

优缺点

线程和进程在使用上各有优缺点:线程执行开销小,但不利于资源的管理和保护;而进程正相反。同时,线程适合于在SMP机器上运行,而进程则可以跨机器迁移。
​多进程:在操作系统中能同时运行多个任务(程序)​
多线程:在同一应用程序中有多个顺序流同时进行​
java的线程是通过java.lang.Thread类来实现的。​
VM(虚拟机)启动是会有一个有主方法(public static void main(){})所定义的线程。同时通过创建Thread的实例来创建新的线程。​
每个线程都是通过某个特定Thread对象所对应的方法run()来完成其操作的,方法run()成为线程体。通过调用Thread类的start()方法来启动一个线程。​

2.线程的创建和启动​

Thread和Runable两种方式:

public class Actor extends Thread {

    @Override
    public void run() {
        System.out.println(getName() + "是一个演员!");
        int count = 0;
        boolean keepRunning = true;
        while (keepRunning) {
            System.out.println(getName() + "登台演出:" + (++count));
            if (count == 100) {
                keepRunning = false;
            }

            if (count % 10 == 0) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
        System.out.println(getName() + "的演出结束了!");
    }

    public static void main(String[] args) {
        Thread actor = new Actor();
        actor.setName("Mr.Thread");
        actor.start();

        Thread actressThread = new Thread(new Actress(), "Ms.Runnable");
        actressThread.start();
    }
}

class Actress implements Runnable {

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + "是一个演员!");
        int count = 0;
        boolean keepRunning = true;
        while (keepRunning) {
            System.out.println(Thread.currentThread().getName() + "登台演出:" + (++count));
            if (count == 100) {
                keepRunning = false;
            }

            if (count % 10 == 0) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
        System.out.println(Thread.currentThread().getName() + "的演出结束了!");
    }
}

隋唐演义的小示例:

public class ArmyRunnable implements Runnable {
    /**
     * volatile保住了线程可以正确的读取其他线程写入的值
     */
    volatile boolean keepRunning = true;

    @Override
    public void run() {
        while (keepRunning) {
            for (int i = 0; i < 5; i++) {
                System.out.println(Thread.currentThread().getName() + "进攻对方[" + i + "]");
                // 让出处理器时间,下次该谁进攻不一定
                Thread.yield();
            }
        }
        System.out.println(Thread.currentThread().getName() + "结束了战斗!");
    }
}
public class KeyPersonThread extends Thread {

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + "开始了战斗!");
        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName() + "左突右杀,击杀隋军!");
        }
        System.out.println(Thread.currentThread().getName() + "结束了战斗!");
    }
}
public class Stage extends Thread {

    @Override
    public void run() {
        System.out.println("欢迎观看隋唐演义!");

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

        System.out.println("大幕徐徐拉开!");

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

        System.out.println("话说隋朝末年,隋军与农民军杀得昏天黑地...");

        ArmyRunnable armyTaskOfSuiDynasty = new ArmyRunnable();
        ArmyRunnable armyTaskOfRevolt = new ArmyRunnable();
        // 使用Runnable接口创建线程
        Thread armyOfSuiDynasty = new Thread(armyTaskOfSuiDynasty, "隋军");
        Thread armyOfRevolt = new Thread(armyTaskOfRevolt, "农民起义军");

        // 启动线程,让军队开始作战
        armyOfSuiDynasty.start();
        armyOfRevolt.start();

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

        System.out.println("正当双方激战正酣,半路杀出个程咬金!");

        Thread mrCheng = new KeyPersonThread();
        mrCheng.setName("程咬金");
        System.out.println("程咬金的理想就是结束战争,使百姓安居乐业!");

        // 军队停止作战
        armyTaskOfSuiDynasty.keepRunning = false;
        armyTaskOfRevolt.keepRunning = false;

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

        mrCheng.start();
        // 万众瞩目,所有线程等待程线程完成历史使命
        try {
            mrCheng.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("战争结束,人民安居乐业!");
        System.out.println("谢谢观看!");
    }

    public static void main(String[] args) {
        new Stage().start();
    }
}

停止线程

正确的停止线程,使用退出标志(keepRunning)。

不正确的停止线程的方法

1.stop()方法

程序会戛然而止,不知道哪些工作还没有做,也没有完成清理工作,也不知道程序完成了什么。

/*armyTaskOfSuiDynasty.keepRunning = false;
armyTaskOfRevolt.keepRunning = false;*/
armyOfSuiDynasty.stop();
armyOfRevolt.stop();

不会输出正常的军队结束战斗的语句。

2.interrupt()方法

向线程发送中断请求。线程的中断状态将被设置为true。如果目前该线程被一个sleep调用阻塞(当前线程调用sleep或join方法时,interrupt方法会使中断状态将被清除),那么InterruptedException异常被抛出(这就是为什么在调用sleep和join时候,要catch InterruptedException)。

下面例子并没有使程序终止:

public class WrongWayStopThread extends Thread {
    public static void main(String[] args) {
        WrongWayStopThread thread = new WrongWayStopThread();
        System.out.println("Starting thread...");
        thread.start();
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("Interrupting thread...");
        thread.interrupt();
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("Stopping application...");
    }

    @Override
    public void run() {
        while (true) {
            System.out.println("Thread is running...");
            long time = System.currentTimeMillis();
            // 相当于sleep(1000)
            // 为什么不使用sleep,因为使用sleep之后, thread.interrupt()会抛出异常,且线程无法正确的结束
            while ((System.currentTimeMillis() - time < 1000)){
                // 减少屏幕输出的空循环
            }
        }
    }
}
 

while (true)替换成while (!this.isInterrupted()),可以使程序中断,但实质上还是使用退出标志的方法,只是标志被换成了中断状态。

另外,sleep方法的使用可以看出,同样一段代码只是内部某些程序的变动,导致退出行为的差异,显然这不是一种我们所期望的使线程结束的方式。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值