博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
认识多线程
阅读量:4984 次
发布时间:2019-06-12

本文共 7137 字,大约阅读时间需要 23 分钟。

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 = 3Exception in thread "main" 线程A 运行,i = 4线程A 运行,i = 5线程A 运行,i = 6线程A 运行,i = 7线程A 运行,i = 8线程A 运行,i = 9java.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  ThreadExtends Objectimplements 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接口的实现作为操作的重点。

转载于:https://www.cnblogs.com/alsf/p/5638337.html

你可能感兴趣的文章
zynq -- cannot find -lxil
查看>>
hdu 4658 Integer Partition
查看>>
一个接口能否继承另一个接口?一个抽象类能否实现一个接口?
查看>>
C#基础知识系列八(const和readonly关键字)
查看>>
[转]Axis2创建WebService实例
查看>>
Linux的链接文件-ln命令
查看>>
百度地图的全景地图实现的过程
查看>>
非常可乐
查看>>
发布:.NET开发人员必备的可视化调试工具(你值的拥有)
查看>>
静态VLAN和动态VLAN
查看>>
C语言中如何对串口进行操作
查看>>
纯CSS制作各种图形(多图预警)
查看>>
BZOJ 4517: [Sdoi2016]排列计数(组合数学)
查看>>
使用Jquery,formData,Express,multer中间件实现文件上传
查看>>
学习总结:机器学习(二)
查看>>
HDU 1097[A hard puzzle]循环节
查看>>
百度seo
查看>>
【网络设备】某防火墙基于IP地址的目的地址转换
查看>>
算法: 最长回文子串 二层动态规划
查看>>
硬件综合实习——51单片机四则运算带括号计算器
查看>>