认识多线程

美好的明天 美好的明天     2022-07-31     717

关键词:

1,进程与线程

  对于WORD来讲,每次启动一个WORD相当于操作系统上分配了一个进程。

  线程实际上是进程的进一步划分,从WORD来看,可以把拼写检查当作一个线程处理,当然,会同时存在多个线程。

  如果进程没有了,线程肯定消失了;但是线程消失,进程未必消失。所有线程都是在进程的基础之上并发(同时运行)。

  现在如果同时运行多个任务,则所有的线程资源是共享的,被所有线程所公用。但是程序处理需要CPU,在同一个时间段会有多个程序执行,但是同一个时间点只能存在

一个程序运行,也就是说,所有程序都要抢占CPU资源。

  

2,JAVA多线程实现

  在Java中实现多线程可以采用以下两种方式:

  1)继承Thread类。

  2)实现Runnable接口。

2.1 Thread类

  Thread类是在java.lang包中定义的,java.lang包会在程序运行时候自动导入,无需手动import导入。

  一个类继承了Thread类之后,那么此类具有了多线程的操作功能。

  在Thread类的子类中,必须明确覆写run()方法,此方法为线程的主体

  一个线程子类的实现如下:

class MyThread extends Thread{    // 继承Thread类,作为线程的实现类
    private String name ;        // 表示线程的名称
    public MyThread(String name){
        this.name = name ;        // 通过构造方法配置name属性
    }
    public void run(){    // 覆写run()方法,作为线程 的操作主体
        for(int i=0;i<10;i++){
            System.out.println(name + "运行,i = " + i) ;
        }
    }
};
public class ThreadDemo01{
    public static void main(String args[]){
        MyThread mt1 = new MyThread("线程1 ") ;     // 实例化对象
        MyThread mt2 = new MyThread("线程2 ") ;     // 实例化对象
        mt1.run() ;    // 调用线程主体
        mt2.run() ;    // 调用线程主体
    }
};

运行结果:

线程1运行,i=0
线程1运行,i=1
线程1运行,i=2
线程1运行,i=3
线程1运行,i=4
线程1运行,i=5
线程1运行,i=6
线程1运行,i=7
线程1运行,i=8
线程1运行,i=9
线程2运行,i=0
线程2运行,i=1
线程2运行,i=2
线程2运行,i=3
线程2运行,i=4
线程2运行,i=5
线程2运行,i=6
线程2运行,i=7
线程2运行,i=8
线程2运行,i=9

  以上程序是先执行完A,后执行B,并没有达到所谓的并发执行效果。

  因为以上程序还是按照古老的形式调用的,通过:对象.方法。但是如果要想启动一个线程,必须使用Thead类中定义的start()方法

  一旦调用start()方法,实际上最终调用的是run()方法。修改如下:

class MyThread extends Thread{    // 继承Thread类,作为线程的实现类
    private String name ;        // 表示线程的名称
    public MyThread(String name){
        this.name = name ;        // 通过构造方法配置name属性
    }
    public void run(){    // 覆写run()方法,作为线程 的操作主体
        for(int i=0;i<10;i++){
            System.out.println(name + "运行,i = " + i) ;
        }
    }
};
public class ThreadDemo02{
    public static void main(String args[]){
        MyThread mt1 = new MyThread("线程A ") ;     // 实例化对象
        MyThread mt2 = new MyThread("线程B ") ;     // 实例化对象
        mt1.start() ;    // 调用线程主体
        mt2.start() ;    // 调用线程主体
    }
};

运行结果:

线程A 运行,i = 0
线程B 运行,i = 0
线程A 运行,i = 1
线程B 运行,i = 1
线程A 运行,i = 2
线程B 运行,i = 2
线程A 运行,i = 3
线程A 运行,i = 4
线程A 运行,i = 5
线程A 运行,i = 6
线程A 运行,i = 7
线程A 运行,i = 8
线程A 运行,i = 9
线程B 运行,i = 3
线程B 运行,i = 4
线程B 运行,i = 5
线程B 运行,i = 6
线程B 运行,i = 7
线程B 运行,i = 8
线程B 运行,i = 9

从以上效果来看,确实是并发执行的,哪个线程先抢占CPU资源,那个线程就执行

注意:

  一个线程只能启动一次,启动多次就会出错。如下:

package Thread1;
class MyThread extends Thread{    // 继承Thread类,作为线程的实现类
    private String name ;        // 表示线程的名称
    public MyThread(String name){
        this.name = name ;        // 通过构造方法配置name属性
    }
    public void run(){    // 覆写run()方法,作为线程 的操作主体
        for(int i=0;i<10;i++){
            System.out.println(name + "运行,i = " + i) ;
        }
    }
};
public class demo1{
    public static void main(String args[]){
        MyThread mt1 = new MyThread("线程A ") ;     // 实例化对象
        MyThread mt2 = new MyThread("线程B ") ;     // 实例化对象
        mt1.start() ;    // 调用线程主体
        mt1.start() ;    // 错误
    }
};

运行结果:

线程A 运行,i = 0
线程A 运行,i = 1
线程A 运行,i = 2
线程A 运行,i = 3
Exception in thread "main" 线程A 运行,i = 4
线程A 运行,i = 5
线程A 运行,i = 6
线程A 运行,i = 7
线程A 运行,i = 8
线程A 运行,i = 9
java.lang.IllegalThreadStateException
    at java.lang.Thread.start(Unknown Source)
    at Thread1.demo1.main(demo1.java:18)

3. Runnable接口实现多线程

  通过Runnable接口的方式实现多线程,Runnable接口只定义了一个抽象方法。

  private void run();

  通过Runnable接口实现多线程:

class 类名称 implements Runnable{
        
           属性...;
           方法....;
           public void run(){
        }
  }   

  如果要想启动线程,则肯定依靠Thread类,但是如果之前直接继承了Thread类,则可以将start()方法直接继承下来使用,但是在Runnable接口中,

并没有start()方法,启动多线程一定要使用start()方法

  Thread类的构造:

public Thread ( Runnable  target)

  就利用以上构造方法,启动多线程。

   RunnableThread mt = new RunnableThread("线程B ") ;     // 实例化对象
     Thread t1 = new Thread(mt) ;        // 实例化Thread类对象
     t1.start() ;    // 启动多线程

  例子如下:

class MyThread implements Runnable{    // 实现Runnable接口,作为线程的实现类
    private String name ;        // 表示线程的名称
    public MyThread(String name){
        this.name = name ;        // 通过构造方法配置name属性
    }
    public void run(){    // 覆写run()方法,作为线程 的操作主体
        for(int i=0;i<10;i++){
            System.out.println(name + "运行,i = " + i) ;
        }
    }
};
public class RunnableDemo01{
    public static void main(String args[]){
        MyThread mt1 = new MyThread("线程A ") ;     // 实例化对象
        MyThread mt2 = new MyThread("线程B ") ;     // 实例化对象
        Thread t1 = new Thread(mt1) ;        // 实例化Thread类对象
        Thread t2 = new Thread(mt2) ;        // 实例化Thread类对象
        t1.start() ;    // 启动多线程
        t2.start() ;    // 启动多线程
    }
};

运行结果:

线程A 运行,i = 0
线程B 运行,i = 0
线程A 运行,i = 1
线程B 运行,i = 1
线程A 运行,i = 2
线程B 运行,i = 2
线程A 运行,i = 3
线程A 运行,i = 4
线程B 运行,i = 3
线程B 运行,i = 4
线程A 运行,i = 5
线程B 运行,i = 5
线程A 运行,i = 6
线程B 运行,i = 6
线程A 运行,i = 7
线程B 运行,i = 7
线程A 运行,i = 8
线程B 运行,i = 8
线程A 运行,i = 9
线程B 运行,i = 9

  从运行结果可以看出,已经完成多线程功能。

4.Thread类与Runnable接口

4.1Thread类与Runnable接口的联系

  Thread类定义:

public class  Thread
Extends Object
implements Runnable

  从定义格式可以发现,Thread类也是Runnable接口子类

  从类的关系上看,之前的做法非常类似代理设计模式!Thread类完成比主体线程更多的操作,例如:分配CPU资源,判断是否已经启动等

4.2 Thread类与Runnable接口的区别

  使用Thread类,在操作多线程的时候无法达到资源共享的目的,而使用Runnable接口实现的多线程操作可以实现资源共享

  使用Thread类的操作:

package Thread1;
class MyThread extends Thread{    // 继承Thread类,作为线程的实现类
    private int ticket = 5 ;        // 表示一共有5张票
    public void run(){    // 覆写run()方法,作为线程 的操作主体
        for(int i=0;i<100;i++){
            if(this.ticket>0){
                System.out.println("卖票:ticket = " + ticket--) ;
            }
        }
    }
};
public class demo1{
    public static void main(String args[]){
        MyThread mt1 = new MyThread() ;     // 实例化对象
        MyThread mt2 = new MyThread() ;     // 实例化对象
        MyThread mt3 = new MyThread() ;     // 实例化对象
        mt1.run() ;    // 调用线程主体
        mt2.run() ;    // 调用线程主体
        mt3.run() ;    // 调用线程主体
    }
};

运行结果:

卖票:ticket = 5
卖票:ticket = 4
卖票:ticket = 3
卖票:ticket = 2
卖票:ticket = 1
卖票:ticket = 5
卖票:ticket = 4
卖票:ticket = 3
卖票:ticket = 2
卖票:ticket = 1
卖票:ticket = 5
卖票:ticket = 4
卖票:ticket = 3
卖票:ticket = 2
卖票:ticket = 1

  发现一个卖出了15张票,也就是三个线程各自卖各自的5张票,也就是说现在没有达到资源共享的目的。

  因为在每一个MyThread对象中都包含各自的ticket属性。

  如果现在使用Runnable接口呢?同样启动多个线程,那么,所有的线程将卖出共同的五张票。

package Thread1;
class MyThread implements Runnable{    // 继承Thread类,作为线程的实现类
    private int ticket = 5 ;        // 表示一共有5张票
    public void run(){    // 覆写run()方法,作为线程 的操作主体
        for(int i=0;i<100;i++){
            if(this.ticket>0){
                System.out.println("卖票:ticket = " + ticket--) ;
            }
        }
    }
};
public class demo1{
    public static void main(String args[]){
        MyThread mt = new MyThread() ;     // 实例化对象
        Thread t1=new Thread(mt) ;    // 调用线程主体
        Thread t2=new Thread(mt) ;    // 调用线程主体
        Thread t3=new Thread(mt) ;    // 调用线程主体
        t1.run();
        t2.run();
        t3.run();
    }
};

  结果:

卖票:ticket = 5
卖票:ticket = 4
卖票:ticket = 3
卖票:ticket = 2
卖票:ticket = 1

  可见,虽然现在启动了三个线程,但是三个线程一共才卖出了五张票,所以达到了资源共享的目的

  这是因为三个Thread都是调用了同一个Runnable子类的对象MyThread 。

4.3 Thread类与Runnable接口比较的结论。

  实现Runnable接口比继承Thread类有如下的优点:

  1)适合多个相同程序代码的线程去处理同一个资源(资源共享)

  2)可以避免由于单继承局限所带来的影响。

  3)增强程序的健壮性,代码能够被多个线程共享,代码与数据是独立的(共享)。

  综合来看,开发中Runnable接口最合适。

在以后的章节中,使用多线程时候,都将以Runnable接口的实现作为操作的重点。

认识多线程

1,进程与线程  对于WORD来讲,每次启动一个WORD相当于操作系统上分配了一个进程。  线程实际上是进程的进一步划分,从WORD来看,可以把拼写检查当作一个线程处理,当然,会同时存在多个线程。  如果进程没有了,... 查看详情

认识cpu核与线程

...知识。如果本文有不严谨或者疏忽的地方,请指正。目录认识cpu、核心与线程java多线程系列(一)之java多线程技能java多线程系列(二)之对象变量的并发访问java多线程系列(三)之等待通知机制java多线程系列(四)之ReentrantL... 查看详情

详细讲解——多线程初阶认识线程(javaee初阶)(代码片段)

认识线程一、概念什么是线程为什么要有线程二、创建线程方法一继承Thread类方法二实现Runnable接口方法三匿名内部类创建Thread子类对象方法四匿名内部类创建Runnable子类对象方法五使用lambda表达式三、线程之间并发执行四、多... 查看详情

多线程的基本认识之三

一、简单介绍1.什么是GCD?全称是GrandCentralDispatch,可译为“牛逼的中枢调度器”纯C语言,提供了非常多强大的函数 2.GCD的优势GCD是苹果公司为多核的并行运算提出的解决方案GCD会自动利用更多的CPU内核(比如双核、四核)GC... 查看详情

认识多线程

一、进程要想了解线程,首先得提到“进程”这个概念,进程:进程是操作系统结构的基础;是一次程序的执行;是一个程序及其数据在处理机上顺序执行时所发生的行动;是程序在一个数据集合上运行的过程,它是系统进行资... 查看详情

java并发编程揭开篇章,并发编程基本认识,了解多线程意义和使用

多线程(multithreading),是指从软件或者硬件上实现多个线程并发执行的技术。具有多线程能力的计算机因有硬件支持而能够在同一时间执行多于一个线程,进而提升整体处理性能。具有这种能力的系统包括对称多处理机、多核... 查看详情

从头认识多线程-1.9迫使线程停止的方法-return法

这一章节我们来讨论一下还有一种停止线程的方法-return1.在主线程上面return,是把全部在执行的线程都停掉packagecom.ray.deepintothread.ch01.topic_9;publicclassStopByReturn{ publicstaticvoidmain(String[]args)throwsInterruptedException{ ThreadFiveth 查看详情

从头认识多线程-1.8迫使线程停止的方法-暴力stop方法

这一章节我们来讨论一下暴力Stop方法。1.使用样例packagecom.ray.deepintothread.ch01.topic_8;publicclassStopByStopMethod{ @SuppressWarnings("deprecation") publicstaticvoidmain(String[]args)throwsInterruptedExceptio 查看详情

多线程---再次认识volatile,synchronize,lock

   在多线程中我们常用的保证共享变量的方法有很多,现在我们介绍其中的一种,volatile,也是效率最高的一种。  一、volatile的意义:           为了确保共享变量能被正确和一致的更新,字段被声... 查看详情

从头认识多线程-1.16对照不同的优先级

这一章节我们来做一个測试,对照一下不同的优先级会形成如何的结果?1.代码清单packagecom.ray.deepintothread.ch01.topic_16;importjava.util.Random;publicclassPrioritySample2{ publicstaticvoidmain(String[]args)throwsInterruptedException{ for( 查看详情

《java多线程编程核心技术一》---快速认识线程(代码片段)

前言最近读完了《深入理解Java虚拟机》大部分理论章节,感觉对JVM内部执行豁然开朗,并且发现并发编程和虚拟机工作也密不可分,强推先读一读JVM,或者读我归纳的几篇JVM文章,现在再系统读一读多线程、... 查看详情

一分钟认识synchronized(代码片段)

synchronized1.多线程的创建方式之一2.synchronized实现原理3.使用synchronized1.多线程的创建方式之一通过实现Runnable接口,并重写run方法实现多线程,下面调用10个线程,每次运行的结果都不一样。publicclasssynchronized_testimplemen... 查看详情

多线程多线程基础知识(代码片段)

文章目录1.认识线程(Thread)2.Thread类及常见方法2.1Thread常见构造方法2.2start和run方法的区别2.3Thread常见属性2.4中断线程2.5等待线程2.6休眠线程3.线程的状态3.1线程的所有状态3.2线程状态转移图1.认识线程(Thread)进... 查看详情

多线程多线程基础知识(代码片段)

文章目录1.认识线程(Thread)2.Thread类及常见方法2.1Thread常见构造方法2.2start和run方法的区别2.3Thread常见属性2.4中断线程2.5等待线程2.6休眠线程3.线程的状态3.1线程的所有状态3.2线程状态转移图1.认识线程(Thread)进... 查看详情

趣谈并发2:认识并发编程的利与弊

...临界区内存可见性总结Thanks从上篇文章趣谈并发(1):全面认识Thread我们了解了Java中线程的基本概念和关键方法。在开始使用线程之前,我觉得我们有必要 查看详情

python的线程06认识线程安全文末送书(代码片段)

正式的Python专栏第44篇,同学站住,别错过这个从0开始的文章!前面学委分享了5篇多线程的文章了,一开始写多线程程序好像非常简单。可是实际应用跟第4篇,第5篇的场景比较像,而且还更复杂。有没有... 查看详情

从gil开始重新认识python多线程编程

我们想要了解Python得多线程,就必须要了解GIL,GIL即全局解释锁。举个栗子计算机执行程序时,是需要把代码编译成机器指令再去执行的,我们现在用的编辑器,其实就是一种解释器,在我们右键运行程序时,它能够将整个文件编译成字... 查看详情

java----线程初级

一、认识多任务、多进程、单线程、多线程要认识多线程就要从操作系统的原理说起。 以前古老的DOS操作系统(V6.22)是单任务的,还没有线程的概念,系统在每次只能做一件事情。比如你在copy东西的时候不能rename文件名。... 查看详情