zoukankan      html  css  js  c++  java
  • Java 多线程(一)—— 概念的引入

    并发和并行

      并行:指两个或多个时间在同一时刻发生(同时发生);

      并发:指两个或多个事件在一个时间段内发生。

      在操作系统中,安装了多个程序,并发指的是在一段时间内宏观上有多个程序同时运行,这在单 CPU 系统中,每一时刻只能有一道程序执行,即微观上这些程序是分时的交替运行,只不过是给人的感觉是同时运行,那是因为分时交替运行的时间是非常短的。

      而在多个 CPU 系统中,则这些可以并发执行的程序便可以分配到多个处理器上(CPU),实现多任务并行执行,即利用每个处理器来处理一个可以并发执行的程序,这样多个程序便可以同时执行。

      目前电脑市场上说的多核 CPU,便是多核处理器,核 越多,并行处理的程序越多,能大大的提高电脑运行的效率。

    注意:单核处理器的计算机肯定不能并行的处理多个任务,只能是多个任务交替的在单个 CPU 上运行。

    进程和线程

      进程:是指一个内存中运行的应用程序,每个进程都有一个独立的内存空间,一个应用程序可以同时运行多个进程;进程也是程序的一次执行过程,是系统运行程序的基本单位;系统运行一个程序即是一个进程从创建、运行到消亡的过程。

      线程:进程内部的一个独立执行单元;一个进程可以同时并发的运行多个线程,可以理解为一个进程便相当于一个单 CPU 操作系统,而线程便是这个系统中运行的多个任务。

    注意:1、因为一个进程中的多个线程是并发运行的,那么从微观角度看也是有先后顺序的,哪个线程执行完全取决于 CPU 的调度,程序员是干涉不了的。而这也就造成的多线程的随机性。

       2、Java 程序的进程里面至少包含两个线程,主进程也就是 main()方法线程,另外一个是垃圾回收机制线程。每当使用 java 命令执行一个类时,实际上都会启动一个 JVM,每一个 JVM 实际上就是在操作系统中启动了一个线程,java 本身具备了垃圾的收集机制,所以在 Java 运行时至少会启动两个线程。

       3、由于创建一个线程的开销比创建一个进程的开销小的多,那么我们在开发多任务运行的时候,通常考虑创建多线程,而不是创建多进程。

         4、多线程是为了同步完成多个任务,不是为了提高程序运行效率,而是通过提高资源使用效率来提高系统的效率。比如做一个添加功能,需要向很多用户发送短信和邮件,如果需要一分钟,不能能让用户等一分钟才响应,可以使用多线程做后台发送,如果是单核,则多个任务交替的在单个 CPU 上运行,理论上比不使用多线程耗时多,因为CPU切换任务需要耗费时间,如果是多核,则可以提高资源的使用效率,多核可以充分利用上。

      

    如何创建多线程

    第一种方法:继承 Thread 类

    步骤:1、定义一个线程类 A 继承于 java.lang.Thread 类

       2、在 A 类中覆盖 Thread 类的 run() 方法

       3、在 run() 方法中编写需要执行的操作

       4、在 main 方法(线程)中,创建线程对象,并启动线程

          创建线程类:A类 a = new A()类;

          调用 start() 方法启动线程:a.start();

    /**
     * @author: ChenHao
     * 创建多线程的第一种方式,继承java.lang.Thread类
     * @Description:创建一个子线程,完成1-100之间自然数的输出。同样,主线程执行同样的操作
     * @Date: Created in 10:50 2018/10/29
     */
    public class TestThread {
        public static void main(String [] args){
            SubThread subThread1=new SubThread();
            SubThread subThread2=new SubThread();
            //调用线程的start(),启动此线程;调用相应的run()方法
            subThread1.start();
            subThread2.start();
            //一个线程只能够执行一次start(),start()中会判断threadStatus的状态是否为0,不为0则抛出异常
            //subThread1.start();
            //不能通过Thread实现类对象的run()去启动一个线程,此时只是主线程调用方法而已,并没有启动线程
            //subThread1.run();
            for (int i=0;i<=100;i++){
                System.out.println(Thread.currentThread().getName()+":"+i);
            }
        }
    }
    //1.创建一个继承Thread的子类
    class SubThread extends Thread{
        //2.重写run方法,方法内实现此子线程要完成的功能
        @Override
        public void run(){
            for (int i=0;i<=100;i++){
                System.out.println(Thread.currentThread().getName()+":"+i);
            }
        }
    }

    注意:1、不能通过Thread实现类对象的run()去启动一个线程,此时只是主线程调用方法而已,并没有启动线程,要启动线程,必须通过Start()方法

       2、一个线程只能够执行一次start(),start()中会判断threadStatus的状态是否为0,不为0则抛出异常,所以一个线程调用两次start()会报异常

    第二种方法:实现 Runnable 接口

    1、Runnable接口应由任何类实现,其实例将由线程执行。 该类必须定义一个无参数的方法,称为run 。 
    2、该接口旨在为希望在活动时执行代码的对象提供一个通用协议。此类整个只有一个 run() 抽象方法

    步骤:1、定义一个线程类 A 实现于 java.lang.Runnable 接口(注意:A类不是线程类,没有 start()方法,不能直接 new A 的实例启动线程)

       2、在 A 类中覆盖 Runnable 接口的 run() 方法

       3、在 run() 方法中编写需要执行的操作

       4、在 main 方法(线程)中,创建线程对象,并启动线程

          创建线程类:Thread t = new Thread( new A类() ) ;

          调用 start() 方法启动线程:t.start();

    package main.java.Thread;
    
    /**
     * @author: ChenHao
     * @Description:创建多线程的方式二:实现runnable
     * 对比一下继承的方式 VS 实现的方式
     * 哪个方式好?实现的方式优于继承的方式
     * why?   ①避免java单继承的局限性
     *        ②如果多个线程要操作同一份资源,更适合使用实现的方式
     * @Date: Created in 10:50 2018/10/29
     */
    public class TestThread2 {
        public static void main(String [] args){
            //此程序存在线程的安全问题,打印车票时,会出现重票、错票,后面线程同步会讲到
            Window window=new Window();
            Thread thread1=new Thread(window,"窗口一");
            Thread thread2=new Thread(window,"窗口二");
            Thread thread3=new Thread(window,"窗口三");
            thread1.start();
            thread2.start();
            thread3.start();
        }
    }
    
    class Window implements  Runnable{
        int ticket=100;
        @Override
        public void run(){
            while (true){
                if(ticket > 0){
                    try {
                        Thread.currentThread().sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()+"售票,票号为:"+ticket--);
                }else {
                    break;
                }
            }
        }
    }

    哪个方式好?实现的方式优于继承的方式

      why? ①避免java单继承的局限性
         ②如果多个线程要操作同一份资源,更适合使用实现的方式

    注意:此程序存在线程的安全问题,打印车票时,会出现重票、错票,下一篇线程同步会讲到

     第三种方法:使用匿名内部类创建线程

    public static void main(String[] args) {
        for(int i = 0 ; i < 10 ; i++){
            System.out.println("玩游戏"+i);
            if(i==5){
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        for(int i = 0 ; i < 10 ;i++){
                            System.out.println("播放音乐"+i);
                        }
                    }
                }).start();
            }
        }
    }
     
  • 相关阅读:
    CodeForces Gym 100935G Board Game DFS
    CodeForces 493D Vasya and Chess 简单博弈
    CodeForces Gym 100935D Enormous Carpet 快速幂取模
    CodeForces Gym 100935E Pairs
    CodeForces Gym 100935C OCR (水
    CodeForces Gym 100935B Weird Cryptography
    HDU-敌兵布阵
    HDU-Minimum Inversion Number(最小逆序数)
    七月馒头
    非常可乐
  • 原文地址:https://www.cnblogs.com/java-chen-hao/p/9895049.html
Copyright © 2011-2022 走看看