zoukankan      html  css  js  c++  java
  • 第21章 java线程(1)-线程初步

    java线程(1)-线程初步

    1.并行和并发

    并行和并发是即相似又有区别:
    并行:指两个或者多个事件在同一时刻点发生。
    并发:指两个或多个事件在同一时间段内发生
    在操作系统中,并发性是指在一段事件内宏观上有多个程序在同时运行,但是单CPU系统中,每一时刻仅能有一道程序执行。故微观上这些程序只能是分时交替执行
    单核处理器的计算机肯定是不能并行的处理多个任务的,只能是多个任务在单个CPU上并发运行,同理,线程也是一样的,从宏观角度上理解是并行运行的,但是从微观角度上分析却是串行运行的,即一个线程一个线程的去运行,当系统只有一个CPU时,线程会以某种顺序执行多个线程,我们把这种情况称之为线程调度

    2.理解进程和线程

    进程和线程:
    进程是指一个内存中运行中的应用程序,每一个进程都有自己独立的一块内存空间,一个应用程序可以同启动多个进程。
    大多数的时候,操作系统都不需要一个进程方法其他进程的空间,也就是说进程之间的通信是很不方便的。此时我们引入“线程”这门技术,来解决这个问题
    线程是指进程中一个执行任务,一个进程可以同时并发运行多个线程,如:多线程下载软件。
    多任务系统:该系统可以运行多个进程,一个进程可以执行多个任务,一个进程可以包含多个线程

    一个进程至少有一个线程,为了提高效率,可以在一个进程中开启多个执行任务,即多线程

    多进程:操作系统中同时运行的多个程序
    多线程:在同一个进程中同时运行的多个任务

    在操作系统中允许多个任务,每一个任务就是一个进程,每一个进程也可以同时执行多个任务,每一个任务就是线程

    进程和线程的区别:
    进程:有独立的内存空间,进程中的数据存放空间(堆空间和栈空间)是独立的,至少有一个线程
    线程:堆空间是共享的,栈空间是独立的,线程消耗的资源也比进程小,相互之间可以影响的,又称为轻型进程或进程元
    因为一个进程中的多个线程是并发的,那么从微观角度来考虑也是有先后顺序的,那么哪一个线程执行完全取决于CPU调度器(JVM来调度),程序员是控制不了的
    我们可以把线程并发性看做是多个线程在瞬间抢CPU资源,谁抢到了资源谁就运行,这也就造成了多线程的随机性
    java程序的进程里至少包含主线程和垃圾回收线程(后台线程)

    线程调度:
    计算机通常只有一个CPU时,在任意时刻只能执行一条计算机指令,每一个进程只有获取CPU的使用权才能执行指令。所谓多进程并发性,从宏观上来看,其实就是各个进程轮流获得CPU的使用权,分别执行各自的任务
    那么,在可运行池中,会有多个线程处理就绪状态等到CPU,JVM就负责了线程的调度
    JVM采用的是抢占式调度,没有采用分时调度,因此可能造成多线程执行结果的随机性。

    3.多线程的优势

    多线程作为一种多任务,并发的工作方法,当然有其存在的优势:
    1.进程之前不能共享内存,而线程之间共享内存(堆内存)则很简单
    2.系统创建进程时需要为该进程重新分配系统资源,创建线程则代价小的多,因此实现多任务并发时,多线程效率更高
    3.java语言本身内置多线程功能的支持,而不是单纯作为底层系统的调度方式,从而简化了多线程编程

    4.创建一个简单的多线程

    在java代码中如何去运行一个进程:
    方式1:Runtime类的exec方法
    方式2:ProcessBuilder的start方法

    //在java中如何开启一个进程:运行记事本程序
    public class ProcessDemo{
    	public static voild main(String[] args) throws Exception{
    	//方式1:使用Runtime类的exec方法
    	Runtime runtime = Runtime.getRuntime();
    	runtime.exce("notepad");
    	//方式2:使用ProcessBuilder的start方法
    	ProcessBuilder pb = new ProcessBuilder("notepad");
    	pb.start();
    }
    }
    
    

    5.使用继承方式和实现方式创建并启动线程

    创建和启动线程,传统有两种方法:
    方式1:继承Thread类
    方式2:实现Runnable接口
    线程类:Thread类(java.lang.Thread)和Thread的子类才能称之为线程类(API有详细例子)
    方式1:继承Thread类
    步骤:
    1.定义一个类A继承于java.lang.Thread类
    2.在A类中覆盖Thread类中的run方法
    3.我们在run方法中编写需要执行的操作->run方法里的线程执行体
    4.在main方法(线程)中,创建线程对象,并启动线程

    创建线程类: A类 a = new A类();
    调用线程对象的start方法,a.start();//启动一个线程

    注意:千万不要调用run方法,如果调用run方法,好比是对象调用方法,依然还是只有一个线程,并没有开启新的线程.

    方式1:继承Thread类

    //计算大于某一规定值的质数的线程可以写成:
    //1.定义Thread的集成类
    class PrimeThread extends Thread{
    	long mainPrime;
    	public PrimeThread(long mainPrime){
    		this.mainPrime = mainPrime;
    	}
    
    	public void run(){
    		//TODO
    	}
    }
    
    //2.调用线程类.创建并启动一个线程
    PrimeThread p = new PrimeThread(143);
    p.start();//注意是调用start方法
    
    

    方式2:实现Runnable接口
    步骤:
    1.定义一个类A继承于java.lang.Thread类
    2.在A类中覆盖Thread类中的run方法
    3.我们在run方法中编写需要执行的操作->run方法里的线程执行体
    4.在main方法(线程)中,创建线程对象,并启动线程

    创建线程类: Thread(Runnable target);//分配新的Thread对象
    调用线程对象的start方法,a.start();//启动一个线程

    //方式2,是声明实现 Runnable 接口的类
    class PrimeRun implements Runable{
    	long mainPrime;
    	public PrimeRun(long mainPrime){
    		this.mainPrime = mainPrime;
    	}
    
    	public void run(){
    		//TODO
    	}
    }
    
    
    //创建并启动一个线程:
    PrimeRun p = new PrimeRun(143);
    Thread t = new Thread(p);
    t.start();
    //每个线程都有一个标识名,多个线程可以同名。如果线程创建时没有指定标识名,就会为其生成一个新名称。
    

    6.使用匿名内部类来创建线程

    只适用于某一个类只适用一次的情况。
    匿名内部类其实就是把5中步骤1,2,3放在4中写,例如创建一个Runnable接口的匿名内部类,上面的改成:

    new Thread(new Runnable(){
    	public void run(){
    		//TODO
    	}
    }).start();
    

    通过继承方式的内部类也是可以这样写的,写的时候就不用声明继承了,直接在{}里面写run()方法就可以了。

    new Thread(){
    	public void run(){
    		//TODO
    	}
    }.start();
    

    这种方式用的比较少

    7.案例

    我们写一个案例来具体说明:
    需求:有50个苹果,请三个小朋友来吃,A,B,C三个人可以同时吃苹果,用多线程来实现
    分析:
    可以先定义线程对象,然后启动线程
    方法:
    可以用我们上面的两种方法来实现
    方式1:使用继承Thread方法
    但是下面类型创造出来多线程会每个线程都吃50个苹果

    /**
     * 用继承方法来完成吃苹果的多线程需求
     */
    //创建一个线程类
    class Person extends Thread{
        private int num = 50;
    
        //构造器,调用Thread的方法赋值
        public Person(String name){
            super(name);
        }
    
        public void run(){
            for (int i = 0; i < 50; i++) {
                if (num > 0){
                    System.out.println(super.getName()+"吃了编号为"+ num-- +"的苹果");
                }
            }
        }
    }
    
    
    public class AppleExtends {
        public static void main(String[] args) {
            //创建桑个线程
            new Person("小A").start();
            new Person("小B").start();
            new Person("小C").start();
        }
    }
    
    
    

    方式2:使用实现Runnable方式来实现

    class Apple implements Runnable{
        private int num = 50;
    
        public void run(){
            for (int i = 0; i < 50; i++) {
                if (num > 0){
                    System.out.println(Thread.currentThread().getName()+"吃了编号为"+ num-- +"的苹果");
                }
            }
        }
    }
    
    
    public class appleImplements {
        public static void main(String[] args) {
            //创建桑个线程
            Apple a = new Apple();
            new Thread(a,"小A").start();
            new Thread(a,"小B").start();
            new Thread(a,"小C").start();
        }
    }
    

    两种方式的区别分析:
    继承方式:
    1.java中类是单继承的,如果继承了Thread了,该类就不能再有其他的直接父类了
    2.从操作上分析,继承方式更简单,获取线程名字也简单(操作上,更简单)
    3.从多线程共享一个资源上分析,继承方式不能做到共享

    实现接口方式:
    1.java中类可以多实现接口,此时该类还可以继承其他类,并且还可以实现其他接口(设计上更优雅)
    2.从操作上分析,实现方式稍微复杂点,获取线程名字也稍微复杂,得使用Thread.currentThread()来获取当前线程的引用,使用Thread.currentThread().getname()来获取当前线程的名字
    3.从多线程共享一个资源分析,实现方式可以做到共享

  • 相关阅读:
    linux脚本启动停止一个jar
    如何突破各种防火墙的防护 [转]
    linux查看端口占用
    网页标题图标添加
    Go语言的一些使用心得
    kubernetes系列之ConfigMap使用方式
    Kubernetes因限制内存配置引发的错误
    Kubernetes系列之理解K8s Service的几种模式
    kubernetes中的Pause容器如何理解?
    Kubernetes系列之Helm介绍篇
  • 原文地址:https://www.cnblogs.com/cenyu/p/6149838.html
Copyright © 2011-2022 走看看