• 认识多线程


    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接口的实现作为操作的重点。

  • 相关阅读:
    Lync二次开发
    Socket 一对多通信
    DBImport V3.1 数据互导工具及文档生成器更新发布
    Sql学习第六天——SQL 巩固练习(用到了前几天几个知识点)
    文件监控
    Google Protocol Buffers 入门
    [原]常用Linux命令总结[Thx for commandlinefu]
    清理SQL Server日志释放文件空间
    ASP.NET MVC动态二级域名及ASP.NET管道机制
    搜索引擎——JobSearch简介
  • 原文地址:https://www.cnblogs.com/alsf/p/5638337.html
Copyright © 2020-2023  润新知