[JAVA] 多线程

来源:互联网 发布:android 美团外卖源码 编辑:程序博客网 时间:2024/05/22 15:06

参考视频: 深入浅出JAVA多线程

一. 线程实现

  1. 类: Thread
  2. 接口:Runnable
  3. 重写方法
public void run() {    // 具体动作代码。。。。}

二. 常用Thread方法

1. static void sleep(), 睡眠

// 给予足够时间让军队停下来, 睡眠5s        try {            Thread.sleep(2000);        } catch (InterruptedException e) {            e.printStackTrace();        }

2. void join(), 其他线程等待这个线程运行结束,才能继续运行

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

3. static void yield(), 让出处理器时间

  for (int i = 0; i < 5; i++) {                 System.out.println(Thread.currentThread().getName() + "进攻对方[" + i + "]");    // 让出处理器时间, 下次该谁进攻改不一定呢    Thread.yield();  }

三. 名词解释

1. 原子性(Atomicity)

原子是世界上的最小单位,具有不可分割性。比如 a=0;(a非long和double类型) 这个操作是不可分割的,那么我们说这个操作时原子操作。再比如:a++; 这个操作实际是a = a + 1;是可分割的,所以他不是一个原子操作。非原子操作都会存在线程安全问题,需要我们使用同步技术(sychronized)来让它变成一个原子操作。一个操作是原子操作,那么我们称它具有原子性。Java的concurrent包下提供了一些原子类,我们可以通过阅读API来了解这些原子类的用法。比如:AtomicInteger、AtomicLong、AtomicReference等

2. 可见性(Visibility, 保证线程可以正确读取其他线程写入的值)

可见性,是指线程之间的可见性,一个线程修改的状态对另一个线程是可见的。也就是一个线程修改的结果。另一个线程马上就能看到。比如:用volatile修饰的变量,就会具有可见性。volatile修饰的变量不允许线程内部缓存和重排序,即直接修改内存。所以对其他线程是可见的。但是这里需要注意一个问题,volatile只能让被他修饰内容具有可见性,但不能保证它具有原子性。比如 volatile int a = 0;之后有一个操作 a++;这个变量a具有可见性,但是a++ 依然是一个非原子操作,也就这这个操作同样存在线程安全问题。

3.有序性(Ordering)

Java内存模型中的程序天然有序性可以总结为一句话:如果在本线程内观察,所有操作都是有序的;如果在一个线程中观察另一个线程,所有操作都是无序的。前半句是指“线程内表现为串行语义”,后半句是指“指令重排序”现象和“工作内存主主内存同步延迟”现象。

Java语言提供了volatile和synchronized两个关键字来保证线程之间操作的有序性,volatile关键字本身就包含了禁止指令重排序的语义,而synchronized则是由“一个变量在同一时刻只允许一条线程对其进行lock操作”这条规则来获得的,这个规则决定了持有同一个锁的两个同步块只能串行地进入

四. 停止线程方法

正确停止方法, 利用可见性

1. 线程内增加volatile字段,run()方法里判断此字段
volatile boolean keepRunning = true; @Override    public void run() {        while(keepRunning) {            ......        }    }

说明: 效果是会等一个完整循环跑完后停止

错误停止方法
1. public final void stop()(final修饰,不能被重写)
Thread.stop();

说明:效果是程序没有跑完就戛然而止

2. public void interrupt()
mCheng.interrupt();

说明:
- 效果是中断一下程序,程序继续运行。
- 调用interrput()方法,线程的状态变成被打断, 可调用下面两个方法判断程序是否被中断。
public static boolean interrupted()
public boolean isInterrupted()
- 调用完interrupt(), 在调用join(), join(long), join(long, int), sleep(long), or sleep(long, int)等方法后, 中断状态会被置位,调用interrupted()、isInterrupted()方法返回false.

五. 完整demo程序, 参考文首参考视频:隋唐演义

/** * 军队线程 * 模拟作战双方的行为 * Created by Danny on 2017/2/16. */public class ArmyRunnable implements Runnable {    // 保证线程可以正确读取其他线程写入的值    // 可见性   volatile boolean keepRunning = true;    @Override    public void run() {        while(keepRunning) {            // 发动5连击            for (int i = 0; i < 5; i++) {                System.out.println(Thread.currentThread().getName() + "进攻对方[" + i + "]");                // 让出处理器时间, 下次该谁进攻改不一定呢                Thread.yield();            }        }        System.out.println(Thread.currentThread().getName() + "结束了战斗!");    }}
/** * 关键人物线程 * 模拟关键人物作战行为 * Created by Danny on 2017/2/16. */public class KeyPersonThread extends Thread {    @Override    public void run() {        super.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() + "结束了战斗!");    }}
/** * 隋唐演义大舞台 * Created by Danny on 2017/2/16. */public class Stage extends Thread {    @Override    public void run() {        super.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();        // 创建线程        Thread armyOfSuiDynasty = new Thread(armyTaskOfSuiDynasty, "隋军");        Thread armyOfRevolt = new Thread(armyTaskOfSuiDynasty, "农民起义军");        // 启动线程让军队开始作战        armyOfSuiDynasty.start();        armyOfRevolt.start();        // 舞台休眠,大家专心观看军队厮杀        try {            Thread.sleep(5000);        } catch (InterruptedException e) {            e.printStackTrace();        }        // 停止作战//        armyTaskOfSuiDynasty.keepRunning = false;//        armyTaskOfRevolt.keepRunning = false;//////        try {//            armyOfRevolt.join();//        } 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();}
0 0