• 2.1多线程(java学习笔记) java中多线程的实现(附静态代理模式)


    一、多线程

    首先我们要清楚程序、进程、线程的关系。

    首先进程从属于程序,线程从属于进程。                                                                                                                           

    程序指计算机执行操作或任务的指令集合,是一个静态的概念。

    但我们实际运行程序时,并发程序因为相互制约,具有“执行——暂停——执行”的状态,

    显然这时程序这个静态的概念无法描述这种状态,从而人们引入了进程这个动态的概念分析研究程序的活动。

    目前国内对进程的定义:进程是指一个具有一定独立功能的程序关于某个数据集合的一次运行活动

    而线程从属于进程,线程是比进程更小的活动单位,它是进程中的一个执行路径。一个进程可以有多个执行路径,即多线程。

    二、java中多线程的实现

    1. 继承Thread实现多线程

    public class TestThread {
        public static void main(String args[]){
            Thre t1 = new Thre("线程1");
            Thre t2 = new Thre("线程2");
            t1.start();                   //多线程的调用并不是直接调用重写之后run方法
            t2.start();                   //而是通过start调用,start方法通过调用操作系统中的函数来实现资源的分配 
        }                                 //但实质是调用的还是run方法,只是中间通过了start方法。
    
    }
    class Thre extends Thread{ //要实现多线程需继承Thread类,并重写Thread中的run方法。
        private int i;         //run方法称为线程主体,是多线程运行时执行的部分。
        private String name;   
        
        public Thre(String name){
            this.name = name;
            
        }
        
        public void run(){
            for(i = 0; i < 10; i++){
                System.out.println(name +": "+ i);
            }
        }    
    }
    运行结果:
    线程2: 0
    线程1: 0
    线程2: 1
    线程1: 1
    线程2: 2
    线程2: 3
    线程2: 4
    线程2: 5
    线程1: 2
    线程1: 3
    线程1: 4
    线程1: 5
    线程1: 6
    线程1: 7
    线程1: 8
    线程1: 9
    线程2: 6
    线程2: 7
    线程2: 8
    线程2: 9

    可以看到最后的运行结果,是两个线程“同时”运行的,这里的同时是一种宏观上同时处理两个线程,但它们实质是是交替运行的的。

    这个举个例子,比如通信运营商要同时提供20个人通话,可以把一秒划分成1000ms,把第1、21、41.....(以此类推)ms给第一个用户使用,第2、22、42...ms给第二个用户使用

    由于间隔时间很短,用户察觉不到其中的间隔看起来像是连续的。从微观上看每一个ms只能服务一个用户,从宏观上来看是同时服务了20个用户。

    因为分时系统采用时间片轮转的方法来处理多线程,由于一个时间片很短,所以看起来像是同时运行。

    2.通过实现Runable接口来实现多线程

    在用接口实现多线程之前,需要了解一种模式:

    静态代理模式:

    静态代理模式实现的条件:

      2.1.真实角色

      2.2代理角色:(代理角色中还需要引用真实角色)

      2.3两者实现相同的接口

    下面我们举个例子

     1 public class Test{
     2     public static void main(String[] args) {
     3         My my = new My();
     4         MarryCompany user_my = new MarryCompany(my);//代理角色引用真实角色。
     5         user_my.marry();                            //最后结婚的是自己,但可以使用代理角色提供的服务。
     6     }
     7 }
     8 
     9 interface Marry{           //结婚功能的接口
    10     public void marry(); 
    11 }
    12 
    13 class My implements Marry{ //my是真实角色,要实现结婚接口
    14 
    15     @Override
    16     public void marry() {
    17         System.out.println("my 结婚");
    18     }    
    19 }
    20 
    21 class MarryCompany implements Marry{  //婚庆公司是代理角色也要实现结婚接口
    22     private Marry user;               //代理角色对真实角色的引用,首先在代理角色中创建真实角色。
    23     public MarryCompany(Marry user){
    24         this.user = user;
    25     }
    26     
    27     private void deadWork_1(){       //代理角色提供的一些服务(方法)。
    28         System.out.println("准备工作1");
    29     }
    30     
    31     private void deadWork_2(){
    32         System.out.println("准备工作2");
    33     }
    34         
    35     @Override
    36     public void marry() {            //代理角色中实现接口的方法。
    37             deadWork_1();            //代理角色提供的方法。
    38             deadWork_2();
    39             user.marry();            //真实角色结婚
    40     }    
    41 }
    运行结果:
    准备工作1
    准备工作2
    my 结婚

    就相当于与你要结婚,可以委托婚庆公司,这就是把代理角色引用真实角色(My my = new My();
    MarryCompany user_my = new MarryCompany(my);)最后是让婚庆公司代理一些事务,但最后结婚的还是自己。

    真实对象可以使用代理对象的一些方法,也可以说代理对象帮我做一些事情。

    和房产中介也是一样的,我们可以让代理角色(中介)帮我们提供一些它拥有的(方法)资源。

    但买房的还是我们自己。

    接着我们来看接口实现多线程,接口实现多线程也是使用的代理模式。

    首先回一下代理模式的三个条件

    1.真实对象

    2.代理对象(代理对象引用真实对象)

    3.实现相同的接口

    我们来看下Thread这个类的源码

    会发现Thread这个类实现了Runable接口。

    而Runable接口中有run方法。

    同时Thread提供了各种构造方法传递真实对象。(Runable接口作为类型)

    所以我们用接口实现多线程还需要如下步骤:

    1.真实角色(实现Runable接口)

    2.代理角色(引用真实角色,代理角色是Thread类)

     1 public class TestThread {
     2     public static void main(String args[]){
     3         Thre t1 = new Thre("线程1");  //创建真实角色,并分配一个名称便于查看
     4         Thre t2 = new Thre("线程2");
     5         Thread p_t1 = new Thread(t1); //代理角色对真实角色的引用
     6         Thread p_t2 = new Thread(t2);
     7         p_t1.start();                 //通过代理启动多线程,但实质上运行的还是真实角色。
     8         p_t2.start();
     9     }
    10 
    11 }
    12 
    13 
    14 class Thre implements Runnable{
    15     private int i;
    16     private String name;
    17     
    18     public Thre(String name){
    19         this.name = name;
    20         
    21     }
    22     
    23     public void run(){
    24         for(i = 0; i < 10; i++){
    25             System.out.println(name +": "+ i);
    26         }
    27     }    
    28 }
    运行结果:
    线程1: 0
    线程2: 0
    线程1: 1
    线程2: 1
    线程1: 2
    线程1: 3
    线程1: 4
    线程2: 2
    线程2: 3
    线程2: 4
    线程2: 5
    线程2: 6
    线程2: 7
    线程2: 8
    线程2: 9
    线程1: 5
    线程1: 6
    线程1: 7
    线程1: 8
    线程1: 9

    一般建议使用接口实现多线程:

    1.用接口实现多线程可以避免单继承的局限性。

    比如我有一个类要一定要继承别的类,那么就无法通过继承Thread来实现多线程了。

    2.可以资源共享

    这里举个例子,比如抢票。票的总数应该是被所以线程(用户)共享的。

    我们用传统的继承实现多线程来模拟简易抢票:

     1 public class TestThread {
     2     public static void main(String[] args) {
     3         My_12306 user1 = new My_12306("用户");
     4         My_12306 user2 = new My_12306("黄牛");
     5         user1.start();
     6         user2.start();
     7     }
     8 }
     9 
    10 
    11 class My_12306 extends Thread {
    12     private int ticket = 5;
    13     private String name;
    14     
    15     public My_12306(String name){
    16         this.name = name;
    17     }
    18     
    19     
    20     public void run(){
    21         for(int i = 0; i < 100; i++){
    22             if(ticket > 0)
    23                 System.out.println(name +"抢到了"+ ticket-- +"号票!");
    24         }
    25     }
    26 }
    运行结果:
    黄牛抢到了5号票!
    用户抢到了5号票!
    黄牛抢到了4号票!
    用户抢到了4号票!
    用户抢到了3号票!
    用户抢到了2号票!
    用户抢到了1号票!
    黄牛抢到了3号票!
    黄牛抢到了2号票!
    黄牛抢到了1号票!

    可以发现两个线程间的资源是独立的,每个用户都有5张票。

    结合下图理解

    如果我们用接口实现多继承来模拟抢票

     1 public class TestThread {
     2     public static void main(String[] args) {
     3         My_12306 m12306 = new My_12306();
     4         
     5         Thread user_1 = new Thread(m12306,"用户");
     6         Thread user_2 = new Thread(m12306,"黄牛");
     7         user_1.start();
     8         user_2.start();
     9     }
    10 }
    11 
    12 
    13 class My_12306 implements Runnable {
    14     private int ticket = 10;
    15     private String name;
    16     
    17     public My_12306(){}
    18     
    19     public My_12306(String name){
    20         this.name = name;
    21     }
    22     
    23     
    24      public void run(){
    25         for(int i = 0; i < 100; i++){
    26                 if(ticket > 0){
    27                     System.out.println(Thread.currentThread().getName() +"抢到了"+ ticket-- +"号票!");    
    28             }
    29         }
    30     }
    31 }
    运行结果:
    用户抢到了10号票!
    黄牛抢到了10号票!
    黄牛抢到了9号票!
    用户抢到了8号票!
    用户抢到了6号票!
    用户抢到了5号票!
    用户抢到了4号票!
    用户抢到了3号票!
    用户抢到了2号票!
    用户抢到了1号票!
    黄牛抢到了7号票!

    可以看到用接口实现多线程可以实现资源共享,第10号票同时被用户和黄牛抢走,

    这是因为线程执行速度太快,资源又没有同步导致的,这就涉及到后面的同步问题。

    结合下图理解

  • 相关阅读:
    Postfix常用命令和邮件队列管理(queue)
    window7下面rabbitMQ安装配置过程详解
    RabbitMQ系列之消息确认机制
    全文检索:sphinx elasticsearch xunsearch 比较
    用SQL命令查看Mysql数据库大小
    部署Percona监控和管理--- PMM Server
    什么是MTU?为什么MTU值普遍都是1500?
    Mysql删除数据后,磁盘空间未释放的解决办法
    数据库索引
    visual studio 容器工具首次加载太慢 vsdbgvs2017u5 exists, deleting 的解决方案
  • 原文地址:https://www.cnblogs.com/huang-changfan/p/9433959.html
Copyright © 2020-2023  润新知