十 多线程

Posted ltfxy

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了十 多线程相关的知识,希望对你有一定的参考价值。

一、多线程的引入:

什么是线程?

  •                程序执行的一条路径,一个进程中可以包含多个线程
  •                多线程并发执行可以提高程序的效率,可以同时完成多项工作

多线程应用场景:

  •             qq同时和多人聊天
  •              服务器同时处理多个客户端请求

二、多线程并行和并发的区别:是否在某一时刻只有1可线程在运行

  •     并行:两个任务同时运行,甲任务运行的同时,乙任务也在运行,需要多核CPU
  •     并发:两个任务都请求运行,处理器只能处理一个请求,在短时间间隔内轮流进行,使人感觉两个任务同时在运行

 

三、关于JAVA的多线程

    1、Java程序运行原理
        Java命令会启动Java虚拟机,启动jvm,等于启动了一个应用程序(进程)。
        该进程会启动了一个主线程,然后主线程去调用某个类的main方法
    2、jvm的启动是多线程的吗?
        JVM开启至少启动了垃圾回收线程和主线程,所以是多线程的
        多次启动一个线程是非法的
   

四、多线程程序的两种实现方式

    方式一:继承Thread类
      1、继承Thread类,Override:run方法
      2、把新线程要做的事情写在run方法中
      3、创建线程对象
      4、执行start方法,启动新线程  
    方式二: 实现Runnable接口(建议使用,后期扩展性强)
       1、定义一个类实现Runnable接口
       2、重写run方法
       3、new一个Thread对象,利用Thread类的构造,将Runnable的子类对象当作参数传进去
       4、 执行Thread.start(),开启线程  
       技术分享图片

  

  原理(查看源码):
       1、Thread实现了Runnable接口,通过Thread的构造函数,传递了Runnable接口的引用target(目标,靶子)
       2、通过Thread类的init方法,将传入引用target赋给成员变量target
       3、在Thread类的run方法中,如果target不为空,则线程开启时(start())会调用Runnable接口子类对象的run方法
    
      两种方式的区别:
      a:继承Thread:由于子类重写了Thread类的run方法,当调用start()时,直接找到子类的run方法
      b : 实现Runnable:Thread类的构造函数中传入了Runnable的引用,通过init方法将引用赋给了Runnable类型的成员变量target
                      在启动线程时,start方法调用run方法,在run方法里,判断target不为空的话,则调用target的run方法,  执行子类重写的run方法
      
      两种方式的好处和弊端:
      继承Thread:  
               好处是:可以直接调用Thread类的方法,代码简单
               弊端是:如果已经有了父类,则不能用这种方法,java单继承,多实现
      实现Runnable
               好处: 由于java单继承多实现,即使线程类有了父类也可以实现接口。
               弊端:不能直接使用Thread中的方法,需要先获取线程对象,再通过Thread的构造传入,代码复杂  
      


      匿名内部类实现线程的两种的方式:
        1、new Thread(){ @override public void run();    }.start();
        2、new Thread(new Runnable(){ @override public void run();}){}.start();

   五、线程的命名 

     1、默认: Thread.getName(): 默认Thread-序号(从1开始)      
     2、构造:Thread(String name):构造命名 name
     3、设置:this.setName(String name):      
     

 

   六、获取当前正在执行的线程

     Thread.currentThread()
     
   

   七、线程睡眠    

    Thread.sleep(毫秒,微妙),控制当前线程休眠若干时间  1秒:1000毫秒:1000微妙:1000纳秒  1秒=10亿纳秒
        

     八、守护线程: 

      Thread.setDaemon(boolean on)将该线程标记为守护线程或用户线程。    
       该线程不会单独执行,当其他的非守护线程都执行结束后,自动退出
       比如QQ聊天,把QQ退出,聊天界面也会关闭,聊天界面可以理解为守护线程
  

     九、加入线程,插队

    join,当前线程暂停,等待指定的线程执行结束以后,当前线程再继续
    join(int),插队,等待指定的毫秒之后,两条线程交替执行         
   

  十、礼让线程

     yield让出cpu  
 

   十一、设置线程的优先级

     setProperties()设置线程优先级 ,最小是1,最大是10,默认是5
   

   十二、同步代码块


     1、什么情况下需要同步
          当多线程并发、有多段代码同时执行时,我们希望某一段代码执行的过程中不要切换到其他线程工作。 这时就需要同步。
          如果两段代码是同步的,那么同一时间只能执行一段,在一段代码没执行结束之前,不会执行另一段代码。
     2、同步代码块
          使用synchronized关键字加上一个锁对象来定义一段代码,这就叫同步代码块。
          多个同步代码块如果使用相同的锁对象,那么他就是同步的。
          锁对象是任意的,但不能是匿名对象,匿名对象不是同一个对象
          同步代码块中,用哪个对象锁,就用哪个对象调用wait方法
       

十三、同步方法及其锁对象   

    如何保证锁是一致的?

     对于继承Thread方式:
               在方法上加synchronized关键字
               对于静态方法,锁对象是this
               对于非静态方法,锁对象是当前类的字节码对象   
     对于实现Runnable接口方式:
                锁对象可以是this,因为只创建了一个Runnable引用对象传进Thread的构造
                    

     十四、死锁  

         多个线程相互等待
         避免同步代码块的嵌套


       技术分享图片

 

     

     十五、回顾以前的线程安全问题

         看源码:Vector、StringBuffer、HashTable、Collection.synchronized(xxx)
         Vector是线程安全的,ArrayList是线程不安全的
         StringBuffer是线程安全的,StringBuilder是线程不安全的
         HashTable是线程安全的,HashMap是线程不安全的
         Collection.synchronized(xxx):返回线程安全的集合
               
    

      十六、多线程之单例设计模式

       单例设计模式:保证类在内存中只有一个对象
       
      如何保证类在内存中只有一个对象?
     1、控制类的创建,不让其他类类创建本类的对象
     2、在本类中定义一个本类的对象
     3、提供公共的访问方式  
       
        单例设计模式的两种写法:
        饿汉式:      
        private Singleton(){  }
        //创建本类对象,私有化,相当于成员变量
        private static Singleton s = new Singleton();
        //对外提供一个公共的访问方式
        public static Singleton getInstance(){
        return s;
    }
        懒汉式:
    private Singleton(){  }
    private static Singleton s;
    public static Singleton getInstance(){
        if(s==null){
            //线程1等待,线程2等待,线程1执行,线程2执行,有可能造成2次对象的情况
            s = new Singleton();
        }
        return s;
    }
 

   其他写法:
    private Singleton(){  }
    //创建本类对象,私有化,相当于成员变量
    private static Singleton s = new Singleton();
    //对外提供一个公共的访问方式
    public static Singleton getInstance(){
    return s;
    }
    饿汉式和懒汉式的区别:
    1 饿汉式是空间换时间(一上来就创建对象),懒汉式是时间换空间(需要判断,浪费时间)
    2 在多线程访问时,饿汉式不会创建多个对象,而懒汉式有可能创建多个对象(如果判断的时候产生线程等待)
    
      

    十七、单例设计模式的应用场景:RunTime类

    Runtime类是一个单例类,查看源码发现构造方法私有,并且是饿汉式的单例形式
    r.exec("shutdown -s -t 300")://5分钟之后关机
     

   十八、Timer类:计时器,实现了Runnable接口


     Timer.schedule(task, delay):在指定的时间做指定的事情
     Timer.schedule(task, delay, period);period之后再做同样的事情,比如闹钟
    

    十九、两个线程间的通信

    1、什么时候需要通信
    多个线程并发执行时,在默认的情况下CPU是随机切换线程的
    如果我们希望他们有规律的执行,就可以使用通信,例如每个线程执行一次打印
    2、怎么通信
    线程等待 wait(),同步代码块中,用哪个对象锁,就用哪个对象调用wait方法
    线程唤醒,notify(),notifyAll();
    这两个方法必须在同步代码块中执行,并且使用同步锁对象来调用
        
  

    二十、wait()、Sleep()和notify()

    为什么wait和notify方法定义在Object类中?
               因为锁对象可以使任意对象,Object是所有类的基类,所以需要这么定义
    sleep和wait的区别?
       (a)sleep在Thread类中,需要传入参数,就是时间,时间到了就醒来
       wait在Object类中,方法可以传入参数,也可以不传入参数 ,传入参数则在参数时间后等待,不传则直接等待
           传参时。sleep睡一阵子就起来,wait一阵子之后就睡    
       (b)slepp在同步函数或者同步代码块中不释放锁(睡着了也抱着锁)    
        wait在同步函数或这同步代码块中释放锁      
        
    

     二十一、 jdk1.5新特性,互斥锁

     1.同步,使用ReenTrantLock类的lock()和unlock()方法进行同步
            lock()获取锁,unlock()释放锁
     2.通信,
                             使用ReenTrantLock类的newCondition()方法获取Condition对象
                             需要等待的时候使用Condition的await()方法,唤醒的时候用signal()方法
                             不同的线程使用不同的condition,这样就能区分唤醒的时候找哪个线程了
     3.常用方法:
         ReenTrantLock.lock():获得锁                      
         ReenTrantLock.unlock():释放锁                      
         ReenTrantLock.await():等待                                                
         ReenTrantLock.signal():唤醒                      
     
 

   二十二、线程组

       线程组概述:
       Java中使用ThreadGroup来表示线程组,对一批线程进行分类管理,java允许程序直接对线程组进行控制
           默认情况下,所有现场都属于主线程组
           我们也可以给线程设置分组                        
         

  二十三、线程的生命周期(此处应有图)


        新建:创建线程对象[thread、runnable]
        就绪:线程对象已经启动,但没有获得CPU执行权[start()]
        运行:获取到了CPU执行权[抢到了CPU执行权]
        阻塞:没有CPU的执行权,回到就绪[sleep()、wait()]
        死亡:代码运行完毕,线程消亡,变成垃圾  [run()结束、stop(方法过时)]
       
 

技术分享图片

 

     

 

     二十四、多线程实现的第三种方式,callable(了解)          

 

       二十五、设计模式之简单工厂模式的概述和使用       

       简单工厂模式:
           又叫静态工厂方法模式,它定义一个具体的工厂类负责创建一些类的实例
       优点:
        客户端不需要负责对象的创建,从而明确了每个类的职责
       缺点:
       这个静态工厂类负责所有对象的创建,如果有对象添加,或者某些对象的创建方式不同,就需要不断的修改工厂类,不利于后期维护
       
              

       二十六、工厂方法模式的概述、使用和了解   


       工厂方法模式概述:
         抽象工厂类定义创建对象的接口,具体对象的创建工作由抽象工厂类的各个实现类来完成
       优点:
         客户端不需要负责对象的创建,明确了个各类的职责,如果有新的对象增加,只需要增加一个具体的类和具体的工厂类即可
        不影响已有代码,便于后期维护,增强了系统的扩展性
       缺点:
        需要额外的编写代码,增加了工作量         
  













































































































































































以上是关于十 多线程的主要内容,如果未能解决你的问题,请参考以下文章

多线程十一 单例模式

想要金九银十面试通关,不懂 Java多线程肯定是不行的!

Python进阶:利用线程实现多任务

JAVA多线程提高十:同步工具CyclicBarrier与CountDownLatch

JAVA多线程提高十一:同步工具Exchanger

多线程面试题系列(16):多线程十大经典案例之一 双线程读写队列数据