Java基础进阶_day15_(多线程)

来源:互联网 发布:静态编译 e源码 编辑:程序博客网 时间:2024/05/18 03:59

Java基础进阶_day15_(多线程)

  • Java基础进阶_day15_多线程
      • 多线程
        • 1进程
        • 2线程
        • 3并行和并发
        • 4 jvm运行的线程
        • 5 线程调度模型
        • 6线程生命周期
        • 7 Java中创建线程的方式
          • 71继承Thread类
          • 72 实现Runnable接口
        • 73 匿名内部类的方式实现线程
      • 多线程同步
        • 1 多线程安全问题
        • 2 多线程安全问题判断依据
        • 3 多线程安全解决方法
        • 4 线程同步的特点
        • 5 线程同步方法选用
        • 6 案例代码

1.多线程

1.1进程

正在运行的程序,是系统进行资源分配和调用的独立单位;
每一个进程都有它自己的内存空间和系统资源.

# 多进程的作用:    多进程的作用不是提高执行速度,而是提高CPU的使用率.

1.2线程

是进程中的单个顺序控制流,是一条执行路径;
一个进程如果只有一条执行路径,则称为单线程程序;
一个进程如果有多条执行路径,则称为多线程程序.

# 多线程的作用:    多线程的作用不是提高执行速度,而是为了提高应用程序的使用率;    线程运行具有随机性.

1.3并行和并发

# 并行是逻辑上同时发生,指在某一个时间内同时运行多个程序。# 并发是物理上同时发生,指在某一个时间点同时运行多个程序。

1.4 jvm运行的线程

JVM虚拟机进程启动时至少启动了运行程序的主线程和垃圾回收的线程,属于多线程.

1.5 线程调度模型

# 线程调度模型:两种线程调度模型    分时调度模型:所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间片;    抢占式调度模型:优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一个,优先级高的线程获取的CPU时间片相对多一些.# Java使用的是抢占式调度模型.

1.6线程生命周期

# 线程的生命周期:1.新建:创建线程对象2.就绪:线程具有执行资格,没有执行权3.运行:线程抢占到CPU资源,具有执行资格,有执行权    阻塞:执行sleep()或wait()或其他原因使得线程处于该状态,没有执行资格,没有执行权其他的有些操作可以将该线程激活,使该线程回转到就绪状态4.死亡:线程对象变为垃圾,等待垃圾回收器的回收

线程的生命周期

1.7 Java中创建线程的方式

1.7.1继承Thread类

继承Thread类,重写run()方法,使用start()方法启动线程.

run()方法和start()方法的区别

# run()方法只是封装需要开启线程进行执行的代码,本身不能开启线程,单独调用时和普通方法调用效果相同;# start()方法是开启线程后,由jvm调用run()方法,并且一个线程只能启动一次,不能启动多次(否则会报异常).

获取和设置线程名称

* 可以通过有参构造方法设置线程的名称:    public Thread(String name):创建线程名称为name的线程对象;    public final String getName():返回该线程的名称;    public final void setName(String name):将线程名称改为name.* 获取调用当前程序的线程的名称:    public static Thread currentThread():返回对当前正在执行的线程对象的引用. 

案例代码1

public class My_Thread_Demo01 {    public static void main(String[] args) {        // 创建对象,启动线程1        MyThread mt = new MyThread();        mt.start();        // 创建对象,启动线程2        MyThread mt2 = new MyThread();        mt2.start();        // 设置线程的名称        mt.setName("线程1");        mt2.setName("线程2");        // 获取调用当前程序的线程        // 获取main方法执行的线程        System.out.println(Thread.currentThread().getName());    }}// 创建线程方式1class MyThread extends Thread {    // 重写run方法,定义需要在线程中执行的代码    @Override    public void run() {        for (int i = 0; i < 10; i++) {            System.out.println(this.getName()+":"+i);        }    }}

线程优先级

# 线程优先级:默认的线程优先级是5,范围是[1,10]之间的整数,数值越大优先级越高.        public final void setPriority(int newPriority):更改线程的优先级为newPriority;    public final int getPriority():返回线程的优先级.# 注意事项:线程的优先级只是代表线程被执行的几率高,一般在多次的调用中效果较为明显.

案例代码2

public class My_Thread_Demo02 {    public static void main(String[] args) {        MyThread2 mt1 = new MyThread2();        MyThread2 mt2 = new MyThread2();        MyThread2 mt3 = new MyThread2();        mt1.setName("线程1");        mt2.setName("线程2");        mt3.setName("线程3");        // 设置线程优先级        mt1.setPriority(1);        mt1.setPriority(5);        mt1.setPriority(9);        // 启动线程        mt1.start();        mt2.start();        mt3.start();        // 获取线程优先级        System.out.println(mt1.getPriority());        System.out.println(mt2.getPriority());        System.out.println(mt3.getPriority());    }}

线程控制

# 线程控制: * 线程休眠:在线程的run()方法中通过Thread直接调用        public static void sleep(long millis):在指定的毫秒数内让当前正在执行的线程休眠. * 线程加入:该线程执行完毕后其他的线程才能执行        public final void join():等待该线程执行完毕后,其他的线程才能执行. * 线程礼让:在线程的run()方法中调用,是让同一个线程类不同对象间线程的执行尽可能的均衡化.        public static void yield():暂停当前正在执行的线程对象,并执行其他线程.  * 后台线程:就是主线程执行完后,后天线程(守护线程)就会终止(可能会守护线程会再执行一段时间,但不会执行完);        public final void setDaemon(boolean on):将该线程标记为守护线程或用户线程,当正在运行的线程都是守护线程时,Java虚拟机退出,该方法必须在启动线程前调用.  * 中断线程:        public final void stop():中断线程,是直接清除线程的状态,被终止的线程中未执行的代码不能再执行;        public void interrupt():中断线程,是清除线程的状态,并抛出异常,线程中未被执行的代码还可以继续执行.

案例代码3

public class My_Thread_Demo03 {    public static void main(String[] args) {        // 测试线程休眠,线程加入,线程礼让,后台线程        // test01();        // 测试中断线程        MyThread3 mt1 = new MyThread3();        mt1.start();        // 当mt1休眠时间操作5秒就中断        try {            Thread.sleep(5000);            // mt1.stop();            mt1.interrupt();        } catch (InterruptedException e) {            e.printStackTrace();        }    }    public static void test01() {        MyThread3 mt1 = new MyThread3();        MyThread3 mt2 = new MyThread3();        MyThread3 mt3 = new MyThread3();        mt1.setName("线程1");        mt2.setName("线程2");        mt3.setName("线程3");        // 设置mt2和mt3为守护线程,当mt1线程执行完后,mt2和mt3会在终止执行(在继续执行一段时间后)        mt2.setDaemon(true);        mt3.setDaemon(true);        mt1.start();        // 将线程1加入        /*try {            mt1.join();        } catch (InterruptedException e) {            e.printStackTrace();        }*/        mt2.start();        mt3.start();    }}class MyThread3 extends Thread {    @Override    public void run() {        for (int i = 0; i < 10; i++) {            System.out.println("线程开始执行");            System.out.println(this.getName() +":" + i);            // 使线程休眠1秒            try {                Thread.sleep(10000);            } catch (InterruptedException e) {                System.out.println("线程被中断了");            }            // 线程礼让            // Thread.yield();            System.out.println("线程继续执行");        }    }}
1.7.2 实现Runnable接口
# 创建线程:类实现Runnable接口,并实现run方法. * 创建线程的另一种方法是声明实现Runnable接口的类,该类然后实现run方法, * 然后可以分配该类的实例,在创建Thread时作为一个参数来传递并调用start()方法启动线程.# 接口方式的线程的作用: * A:可以避免由于Java单继承带来的局限性(继承其他类的同时可以实现线程接口); * B:适合多个相同程序的代码去处理同一个资源的情况,把线程同程序的代码,数据有效分离,较好的体现了面向对象的设计思想.实现线程接口的类只创建一个对象,但是可以将这个对象作为多个线程实例的参数,则多个线程操作一个对象中的数据.

案例代码

public class My_Runnable_Demo01 {    public static void main(String[] args) {        MyThread4 mt = new MyThread4();        Thread t1 = new Thread(mt, "线程1");        t1.start();        Thread t2 = new Thread(mt, "线程2");        t2.start();    }}class MyThread4 implements Runnable {    // 重写run方法    @Override    public void run() {        for (int i = 0; i < 10; i++) {            // 这种方式创建的线程不能直接获取线程的名字,可以通过Thread.currentThread()获取当前执行的线程            System.out.println(Thread.currentThread().getName()+":"+i);        }    }}

1.7.3 匿名内部类的方式实现线程

public class My_Thread_Demo05 {    public static void main(String[] args) {        // 方式1        // 匿名子类和匿名接口方式        new Thread(new Runnable() {            @Override            public void run() {                for (int i = 0; i < 100; i++) {                    System.out.println(Thread.currentThread().getName()+":"+i);                }            }        }).start();        // 方式2        // 匿名子类实现        new Thread() {            @Override            public void run() {                for (int i = 0; i < 100; i++) {                    System.out.println(Thread.currentThread().getName()+":"+i);                }            }        }.start();        // 方式3        // 输出的结果是python的,匿名子类对象的方法将接口中的run方法覆盖掉        new Thread(new Runnable() {            @Override            public void run() {                for (int i = 0; i < 100; i++) {                    System.out.println("java"+":"+i);                }            }        }) {            @Override            public void run() {                for (int i = 0; i < 100; i++) {                    System.out.println("python"+":"+i);                }            }        }.start();    }}

2.多线程同步

2.1 多线程安全问题

#CPU的一次操作必须是原子性的:当操作数据不是原子性(多个语句操作同一个数据),可能出现一个线程在完成数据操作的所有语句前,其他的线程就来操作数据; * 如:线程A和线程B,操作共享数据的有a和b两条语句, * 当A操作完数据的a语句还没操作语句b时,线程B进来操作数据, * 由于A的b语句操作没有完成,共享数据没有发生变化,当B线程执行完b语句时,A线程再执行使用的还是之前的数据,则线程A和线程B的操作的数据的结果相同.# 线程的执行具有随机性和一定的延迟.

2.2 多线程安全问题判断依据

# A:是否是多线程;# B:是否存在共享数据;# C:是否多条语句操作共享数据.

2.3 多线程安全解决方法

# 同步代码块:锁对象是任意对象,线程间共享的对象  * 格式:        synchronized(锁对象){            需要被同步的代码块;        }# 同步方法:锁对象是this    在方法上添加synchronized关键字修饰# 静态同步方法:锁对象是该类的.class对象    在方法上添加synchronized关键字修饰

2.4 线程同步的特点

# 前提:    多线程环境;    多个线程使用同一个锁对象(不同的执行路径要使用相同的锁对象)# 好处:    解决多线程安全问题# 弊端:    当线程相当多时,因为每个线程都会去判断同步上的锁,这是很耗费资源的,无形中会降低程序的运行效率.

2.5 线程同步方法选用

# 如果锁对象是this,就可以考虑使用同步方法;# 否则能使用同步代码块的尽量使用同步代码块.

2.6 案例代码

/* * 模拟买电影票 */public class My_Thread_Test03 {    public static void main(String[] args) {        SellTickets2 s1 = new SellTickets2();        Thread t1 = new Thread(s1, "窗口1");        Thread t2 = new Thread(s1, "窗口2");        Thread t3 = new Thread(s1, "窗口3");        t1.start();        t2.start();        t3.start();    }}class SellTickets2 implements Runnable {    // 定义票数    private static int tickets = 100;    private Object obj = new Object();    // 使用同步代码块    /*@Override    public void run() {        synchronized (obj) {            while( tickets > 0) {                System.out.println(Thread.currentThread().getName()+"窗口正在出售第"+(tickets--)+"张票");            }        }    }*/    // 使用同方法    @Override    public void run() {        sellTicket();    }    public synchronized void sellTicket() {        while( tickets > 0) {            try {                Thread.sleep(100);            } catch (InterruptedException e) {                e.printStackTrace();            }            System.out.println(Thread.currentThread().getName()+"窗口正在出售第"+(tickets--)+"张票");        }    }}
0 0
原创粉丝点击