java基础学习_多线程01_多线程_day23总结

java基础学习_多线程01_多线程_day23总结

=============================================================================
=============================================================================
涉及到的知识点有1:多线程(理解)
    (1)多线程的概述
    (2)Java程序的运行原理及JVM的启动是多线程的吗?
    (3)多线程的实现方案(掌握)
    (4)线程的调度模型和如何获取和设置线程优先级
    (5)线程的控制(即线程常见的方法)
    (6)线程的生命周期(参照:03_线程的生命周期图解.png)
    (7)电影院卖票程序的实现
    (8)电影院卖票程序出现问题
    (9)多线程安全问题产生的原因(这些原因也是我们以后判断一个程序是否有线程安全问题的依据)
    (10)同步解决线程安全问题
    (11)回顾以前的线程安全的类
=============================================================================
=============================================================================
1:多线程(理解)
    (1)多线程的概述        
        进程:正在运行的应用程序。进程是系统进行资源分配和调用的独立单位。每一个进程都有它自己的内存空间和系统资源。
        线程:是进程(程序)的执行单元,执行路径。
        单线程:一个应用程序只有一条执行路径。
        多线程:一个应用程序有多条执行路径。
        
        一个进程 = 一个正在运行的程序 = 1个线程+1个线程+1个线程+... = 多个线程 = 多个任务
        
        多进程的意义?
            提高CPU的使用率。
        多线程的意义?
            提高应用程序的使用率。

        注意两个词汇的区别:并行和并发。
            并行:前者是逻辑上同时发生,指在某一个时间内同时运行多个程序。
            并发:后者是物理上同时发生,指在某一个时间点同时运行多个程序。
        
            在java就业班中会有如何解决高并发?
-------------------------------------- (2)Java程序的运行原理及JVM的启动是多线程的吗? A:Java程序的运行原理 Java通过java命令会启动java虚拟机。启动JVM,等于启动了一个应用程序,也就是启动了一个进程。 该进程会自动启动一个 “主线程” ,然后主线程去调用某个类的main方法。所以main方法运行在主线程中。在此之前的所有程序都是单线程的。 B:JVM的启动是多线程的吗? 垃圾回收线程也要先启动,否则很容易会出现内存溢出。 JVM的启动是多线程的,因为它最低有两个线程启动了,主线程和垃圾回收线程。 -------------------------------------- (3)多线程的实现方案(掌握) A:自定义类继承Thread类 1:自定义类MyThread继承Thread类 2:MyThread类里面重写run()方法 3:在测测试类MyThreadTest中创建MyThread类的对象 4:启动线程 B:自定义类实现Runnable接口 1:自定义类MyRunnable实现Runnable接口 2:MyRunnable类里面重写run()方法 3:在测测试类MyRunnableTest中创建MyRunnable类的对象 4;在测测试类MyRunnableTest中再创建Thread类的对象,并把3步骤的对象作为构造参数进行传递 5:启动线程 -------------------------------------- 注意事项: 1:Thread类的方法: public final String getName() 获取线程对象的名称(一般放在需要被线程执行的代run()方法里面) public final void setName(String name) 设置线程对象的名称 对象名.setName("林青霞"); 2:在不是Thread类的子类中,如何获取线程对象的名称呢? public static Thread currentThread() 返回当前正在执行的线程对象(静态方法) Thread.currentThread().getName() 3:该自定义的类为什么要重写run()方法? 自定义类中不是所有的代码都需要被线程执行。 而这个时候,为了区分哪些代码能够被线程执行,java提供了Thread类中的run()方法,用来包含那些需要被线程执行的代码。 注意:这里的 被线程执行 = 开一个新线程执行 4:由于自定义类实现了接口,所以就不能在自定义类中直接使用Thread类的getName()方法了,但是可以间接的使用。 Thread.currentThread().getName() -------------------------------------- 小问题: 1:为什么要重写run()方法? :run()方法里面封装的是被线程执行的代码。 2:启动线程对象用的是哪个方法? :start()方法 3:run()方法和start()方法的区别? :run()方法直接调用仅仅是普通方法。 start()方法是先启动线程,再由jvm去调用run()方法。 4:有了方式1,为什么还来一个方式2呢? :若自定义类MyThread类已经有一个父类了,那么它就不可以再去继承Thread类了。(java不支持多继承) 若自定义类MyRunnable类已经实现了一个接口了,那么它还可以再去实现Runnable接口。(java支持多实现) 即可以避免由于Java单继承带来的局限性。 在测试类MyThreadTest中,要想开多个线程,就要先new多个自定义类MyThread的对象,每一个自定义类MyThread的对象的成员变量都相同,这样需要在栈中开辟很多内存; 在测试类MyRunnableTest中,要想开多个线程,只需要new一个自定义类MyRunnable的对象,再new多个Thread类的对象即可,这样就大大节约了内存。 即适合多个相同程序的代码去处理同一个资源的情况,把线程同程序的代码和数据有效分离(即耦合性降低),较好的体现了Java面向对象的设计思想-------------------------------------- (4)线程的调度模型和如何获取和设置线程优先级 假如我们的计算机只有一个CPU,那么CPU在某一个时刻只能执行一条指令,线程只有得到CPU时间片,也就是使用权,才可以执行指令。 那么Java是如何对线程进行调用的呢?线程有两种调度模型。 A:线程的调度模型 a:分时调度模型 所有线程轮流使用CPU的使用权,平均分配每个线程占用CPU的时间片。 b:抢占式调度模型 (Java采用的是该调度方式) 优先让优先级高的线程使用CPU,如果线程的优先级相同,那么会随机选择一个,优先级高的线程获取的CPU时间片的概率相对高一些。 B:如何获取和设置线程优先级 线程默认的优先级是:5。 线程优先级的范围是:1-10如何获取线程对象的优先级? public final int getPriority() 返回线程对象的优先级 int i = 对象名.getPriority(); 如何设置线程对象的优先级? public final void setPriority(int newPriority) 更改线程的优先级 对象名.setPriority(10); IllegalArgumentException:非法参数异常 抛出的异常表明向方法传递了一个不合法或不正确的参数。 -------------------------------------- (5)线程的控制(即线程常见的方法) A:线程休眠 public static void sleep(long millis) 单位是毫秒(该方法会抛出异常) Thread.sleep(1000); B:线程加入 public final void join() 等待该线程终止(为了使某线程先执行完毕)(该方法会抛出异常) 对象名.join(); // 该方法必须在启动线程后调用 C:线程礼让 public static void yield() 暂停当前正在执行的线程对象,并执行其他线程。 能够在一定的程度上,让多个线程的执行更和谐,但是不能靠它保证一个线程一次。 Thread.yield(); D:后台线程(守护线程/用户线程) public final void setDaemon(boolean on) 将该线程标记为守护线程或用户线程 对象名.setDaemon(true); // 设置守护线程 当正在运行的线程都是守护线程时,Java虚拟机退出。该方法必须在启动线程前调用。 E:中断(终止)线程(掌握) public final void stop() 让线程停止,过时了,但是还可以使用。(为什么会过时呢?因为该方法太暴力了,具有固有的不安全性,直接把线程停止,该线程之后的代码都不能执行了) 对象名.stop(); public void interrupt() 中断线程。 把线程的状态终止,并抛出一个InterruptedException异常。 对象名.interrupt(); 注意事项: 如果被重写的方法没有异常抛出,那么子类的方法绝对不可以抛出异常,如果子类方法内有异常发生,那么子类只能try,不能throws。
-------------------------------------- (6)线程的生命周期(参照:03_线程的生命周期图解.png) A:线程的创建 创建线程对象,无资格无权。 B:线程的就绪 有资格无权 C:线程的运行 有资格有权 D:线程的阻塞 无资格无权 E:线程的死亡 无资格无权
-------------------------------------- (7)电影院卖票程序的实现 A:自定义类继承Thread类 B:自定义类实现Runnable接口 -------------------------------------- (8)电影院卖票程序出现问题 A:为了更符合真实的场景,加入了休眠100毫秒。程序就出现了问题。 B:但出现了卖票问题 a:相同的票出现多次 CPU的一次操作必须是原子性的(即这个操作不能再拆分了)。原子性 = 最简单基本的 b:出现了负数票 线程的随机性延迟导致的。 -------------------------------------- (9)多线程安全问题产生的原因(这些原因也是我们以后判断一个程序是否有线程安全问题的依据) A:是否是多线程环境 B:是否有共享数据 C:是否有多条语句操作共享数据 -------------------------------------- (10)同步解决线程安全问题 A:同步代码块 synchronized(对象) { 需要被同步的代码; } 这里的锁对象可以是任意对象B:同步方法 把同步加在方法上。 这里的锁对象是this。 C:静态同步方法 把同步加在方法上,再加上静态。 这里的锁对象是当前类的字节码文件对象(反射再讲字节码文件对象)。 -------------------------------------- 详解如下: * A:同步代码块的格式及其锁对象问题? * 格式: * synchronized (对象名称) { * 需要同步的代码; * } * * 同步代码块的锁对象是谁呢? * 任意对象。 * * B:同步方法的格式及其锁对象问题? * 如果一个方法一进去就看到了代码被同步了,那么我就在想能不能把这个同步加在方法上呢? 答:能。 * 把同步关键字加在方法上。 * 格式: * synchronized private void sellTicket() {...} * private synchronized void sellTicket() {...} // 习惯上这样写 * * 同步方法的锁对象是谁呢?(方法的内部有一个你看不到的对象是this啊,傻瓜哈) * this * * C:静态同步方法的格式及其锁对象问题? * 格式: * private static synchronized void sellTicket() {...} * * 静态同步方法的锁对象是谁呢? * 当前类的字节码文件对象。(反射会讲) * * 类的初始化过程:Person p = new Person(); // 第一步做的事情是:把Person.class文件加载进内存。在Person.class文件中找到main方法并放到栈。 * 因为静态是随着类的加载而加载。此时对象this根本就不存在。此时的对象是.class文件(字节码文件)。 * * 简言之:要想同步,需要先确定同步的对象。 * 要在静态同步方法加载之前就得先确定同步的对象,(否则你跟我咋同步) * 谁比静态先存在呢? 答:只有.class文件(字节码文件) * * 那么,我们到底使用谁? * 如果锁对象是this,就可以考虑使用同步方法。 * 否则能使用同步代码块的尽量使用同步代码块。 -------------------------------------- 同步的特点: 前提: 多个线程 多个线程使用的是同一个锁对象 同步的好处: 同步的出现解决了多线程的安全问题。 同步的弊端: 当线程相当多时,因为每个线程都会去判断同步上的锁,这是很耗费资源的,无形中会降低程序的运行效率。 -------------------------------------- (11)回顾以前的线程安全的类 A:StringBuffer B:Vector C:Hashtable D:如何把一个线程不安全的集合类变成一个线程安全的集合类 用Collections工具类的方法即可。 示例代码如下: // 线程安全的类 StringBuffer sb = new StringBuffer(); // 几乎所有的方法都加l了synchronized,所以线程安全,但效率低。 Vector<String> v = new Vector<String>(); // 几乎所有的方法都加了synchronized,所以线程安全,但效率低。 Hashtable<String, String> h = new Hashtable<String, String>(); // 几乎所有的方法都加了synchronized,所以线程安全,但效率低。 // Vector是线程安全的时候才会去考虑使用的,但是呢,即使要安全,也不用Vector。 // 为什么呢?那么到底用谁呢? // Collections工具类的让集合同步的方法,以List举例: // public static <T> List<T> synchronizedList(List<T> list) List<String> list1 = new ArrayList<String>(); // 线程不安全的List List<String> list2 = Collections.synchronizedList(new ArrayList<String>()); // 线程安全的List // 通过Collections类的让集合同步的方法,就把线程不安全的List变成线程安全的List了,所以我们不用Vector! =============================================================================

 

posted @ 2018-04-06 22:53  黑泽君  阅读(155)  评论(0编辑  收藏  举报