zoukankan      html  css  js  c++  java
  • 多线程

    多线程:
    1、进程和线程:
    进程:正在进行的程序。

    每个进程运行都有一个运行顺序,该顺序是一个运行路径。或者叫一个控制单元。
    线程:进程内部的一条运行路径或者一个控制单元。
    两者的差别:
    一个进程至少有一个线程
    进程在运行过程中拥有独立的内存单元,而多个线程共享内存。
    2、jvm多线程的启动是多线程吗?
    java的虚拟机jvm启动的是单线程,就有发生内存泄露的可能,而我们使用java程序没出现这种问题,
    也就是jvm启动至少有两个线程,一个运行java程序,一个运行垃圾回收。所以是多线程。
    2、多线程的优势:
    攻克了多部分同一时候执行的问题。提高效率
    3、线程的弊端:
    线程太多会导致效率的减少,由于线程的运行依靠的是CPU的来回切换。
    4、什么叫多线程:
    一个进程中有多个线程,称为多线程。
    5、实现多线程的方法:
    实现多线程能够通过继承Thread类和实现Runnable接口。
    (1)继承Thread
       定义一个类继承Thread类
       复写Thread类中的public void run()方法,将线程的任务代码封装到run方法中
       直接创建Thread的子类对象,创建线程
       调用start()方法,开启线程(调用线程的任务run方法)
       //另外能够通过Thread的getName()获取线程的名称。


    (2)实现Runnable接口。
    定义一个类。实现Runnable接口;
    覆盖接口的public void run()的方法。将线程的任务代码封装到run方法中。
    创建Runnable接口的子类对象
    将Runnabl接口的子类对象作为參数传递给Thread类的构造函数,创建Thread类对象
                           (原因:线程的任务都封装在Runnable接口子类对象的run方法中。


            所以要在线程对象创建时就必须明白要执行的任务)。
    调用start()方法,启动线程。

    两种方法差别:
    (1)实现Runnable接口避免了单继承的局限性
    (2)继承Thread类线程代码存放在Thread子类的run方法中
      实现Runnable接口线程代码存放在接口的子类的run方法中;
      在定义线程时,建议使用实现Runnable接口,由于差点儿全部多线程都能够使用这样的方式实现
    6、创建线程是为什么要复写run方法?
    Thread类用于描写叙述线程。Thread类定义了一个功能,用于存储线程要执行的代码,该存储功能就是run方法。
    7、start()和run方法有什么差别?
    调用start方法方可启动线程。而run方法仅仅是thread的一个普通方法,调用run方法不能实现多线程。
    Start()方法:
    start方法用来启动线程,实现了多线程运行,这时无需等待run方法体代码运行完成而直接继续运行以下的
    代码。通过调用Thread类的start()方法来启动一个线程,这时此线程处于就绪(可执行)状态,并没有执行,
    一旦得到cpu时间片(运行权),就開始运行run()方法,这里方法run()称为线程体,
    它包括了要执行的这个线程的内容,Run方法执行结束,此线程随即终止。


    Run()方法:
    run()方法仅仅是Thread类的一个普通方法,假设直接调用Run方法,程序中依旧仅仅有主线程这一个线程,
    其程序运行路径还是仅仅有一条,还是要等待run方法体运行完成后才可继续运行以下的代码,
    这样就没有达到多线程的目的。


    8、线程的几种状态:
    新建:new一个Thread对象或者其子类对象就是创建一个线程。当一个线程对象被创建,可是没有开启,这个时候,
         仅仅是对象线程对象开辟了内存空间和初始化数据。        
    就绪:新建的对象调用start方法。就开启了线程,线程就到了就绪状态。
         在这个状态的线程对象。具有运行资格。没有运行权。
    执行:当线程对象获取到了CPU的资源。
         在这个状态的线程对象,既有运行资格。也有运行权。
    冻结:运行过程中的线程因为某些原因(比方wait,sleep),释放了运行资格和运行权。
                  当然,他们能够回到执行状态。仅仅只是,不是直接回到。
         而是先回到就绪状态。
    死亡:当线程对象调用的run方法结束,或者直接调用stop方法,就让线程对象死亡,在内存中变成了垃圾。      
    9、sleep()和wait()的差别:
    (1)这两个方法来自不同的类,sleep()来自Thread类,和wait()来自Object类。
    (2)sleep是Thread的静态类方法,谁调用的谁去睡觉,即使在a线程里调用了b的sleep方法。实际上还是a去睡觉,
       要让b线程睡觉要在b的代码中调用sleep。而wait()是Object类的非静态方法
    (3)sleep()释放资源不释放锁。而wait()释放资源释放锁;
    (4)使用范围:wait,notify和notifyAll仅仅能在同步控制方法或者同步控制块里面使用,而sleep能够在不论什么地方使用
    10、多线程安全问题:
    (1)原因:当程序的多条语句在操作线程共享数据时(如买票样例中的票就是共享资源),因为线程的随机性导致
           一个线程对多条语句,运行了一部分还没运行完,还有一个线程抢夺到cpu运行权參与进来运行,
           此时就导致共享数据错误发生。比方买票样例中打印重票和错票的情况。


    (2)解决方法:对多条操作共享数据的语句进行同步,一个线程在运行过程中其它线程不能够參与进来
    11、Java中多线程同步是什么?
    同步是用来解决多线程的安全问题的,在多线程中,同步能控制对共享数据的訪问。

    假设没有同步。当一个线程在
    改动一个共享数据时,而另外一个线程正在使用或者更新同一个共享数据,这样easy导致程序出现错误的结果。 
    12、什么是锁?锁的作用是什么?
    锁就是对象
    锁的作用是保证线程同步,解决线程安全问题。
    持有锁的线程能够在同步中运行,没有锁的线程即使获得cpu运行权。也进不去。


    13、同步的前提:
    (1)必须保证有两个以上线程
    (2)必须是多个线程使用同一个锁,即多条语句在操作线程共享数据
    (3)必须保证同步中仅仅有一个线程在执行
    14、同步的优点和弊端
    优点:同步攻克了多线程的安全问题
    弊端:多线程都须要推断锁。比較消耗资源
    15、同步的两种表现形式:
    (1)同步代码块:
    能够指定须要获取哪个对象的同步锁,使用synchronized的代码块相同须要锁,但他的锁能够是随意对象
    考虑到安全问题。一般还是使用同一个对象,相对来说效率较高。




    注意:
    **尽管同步代码快的锁能够使不论什么对象。可是在进行多线程通信使用同步代码快时,
     必须保证同步代码快的锁的对象和。否则会报错。
    **同步函数的锁是this,也要保证同步函数的锁的对象和调用wait、notify和notifyAll的对象是
     同一个对象。也就是都是this锁代表的对象。
    格式:
    synchronized(对象)
    {
    需同步的代码;
    }
    (2)同步函数
    同步方法是指进入该方法时须要获取this对象的同步锁,在方法上使用synchronizedkeyword,
    使用this对象作为锁。也就是使用了当前对象。由于锁住了方法。所以相对于代码块来说效率相对较低。
    注:静态同步函数的锁是该方法所在的类的字节码文件对象。即类名.class文件
    格式:
    修饰词 synchronized 返回值类型 函数名(參数列表)
    {
    需同步的代码;
    }


    在jdk1.5后。用lock锁代替了synchronized。个人理解也就是对同步代码块做了改动,

    并没有提供对同步方法的改动,主要还是效率问题吧。

    多线程的单例设计模式:保证某个类中内存中仅仅有一个对象
    (1)饿汉式:
    class Single
    {
    private Single(){}//将构造函数私有化。不让别的类建立该类对象
    private static final Single s=new Single();//自己建立一个对象
    public static Single getInstance()//提供一个公共訪问方式
    {
    return s;
    }
    }
    (2)懒汉式:
    class Single
    {
    private Single(){} 
    private static Single s;
    public static Single getInstance()
    {
    if(s==null)
    s=new Single();
    return s;
    }
    }
    饿汉式和懒汉式的差别:
    **
    饿汉式是类一载入进内存就创建好了对象;
    懒汉式则是类载入进内存的时候,对象还没有存在。仅仅有调用了getInstance()方法时,对象才開始创建。


    **
    懒汉式是延迟载入,假设多个线程同一时候操作懒汉式时就有可能出现线程安全问题,解决线程安全问题
    能够加同步来解决。可是加了同步之后,每一次都要比較锁。效率就变慢了,
    所以能够加双重推断来提高程序效率。
    如将上述懒汉式的Instance函数改成同步:
    public static Single getInstance()
    {
    if(s==null)
    {
    synchronized(Single.class)
    {
    if(s==null) 
    s=new Single();
    }
    }
    return s;
    }
    17、死锁
    两个线程对两个同步对象具有循环依赖时,就会发生死锁。即同步嵌套同步,而锁却不同。
    18、wait()、sleep()、notify()、notifyAll()
    wait():使一个线程处于等待状态。而且释放所持有的对象的lock。 
    sleep():使一个正在执行的线程处于睡眠状态,是一个静态方法,调用此方法要捕捉InterruptedException异常。 
    notify():唤醒一个处于等待状态的线程,注意的是在调用此方法的时候。并不能确切的唤醒某一个等待状态的线程,
    而是由JVM确定唤醒哪个线程(通常是最先開始等待的线程),并且不是按优先级。 
    Allnotity():唤醒全部处入等待状态的线程,注意并非给全部唤醒线程一个对象的锁,而是让它们竞争。


    18、为什么wait()、notify()、notifyAll()这些用来操作线程的方法定义在Object类中?
    (1)这些方法仅仅存在于同步中。
    (2)使用这些方法时必需要指定所属的锁,即被哪个锁调用这些方法。
    (3)而锁能够是随意对象。所以随意对象调用的方法就定义在Object中。
    19、多线程间通讯:
    多线程间通讯就是多个线程在操作同一资源,可是操作的动作不同.
            (1)为什么要通信
    多线程并发运行的时候, 假设须要指定线程等待或者唤醒指定线程, 那么就须要通信.比方生产者消费者的问题,
    生产一个消费一个,生产的时候须要负责消费的进程等待,生产一个后完毕后须要唤醒负责消费的线程,
    同一时候让自己处于等待。消费的时候负责消费的线程被唤醒,消费完生产的产品后又将等待的生产线程唤醒,
    然后使自己线程处于等待。这样来回通信,以达到生产一个消费一个的目的。
            (2)怎么通信
    在同步代码块中, 使用锁对象的wait()方法能够让当前线程等待, 直到有其它线程唤醒为止.
    使用锁对象的notify()方法能够唤醒一个等待的线程。或者notifyAll唤醒全部等待的线程.
    多线程间通信用sleep非常难实现。睡眠时间非常难把握。


    20、Lock和Condition
    实现提供比synchronized方法和语句可获得的更广泛的锁的操作。可支持多个相关的Condition对象
    Lock是个接口
    锁是控制多个线程对共享数据进行訪问的工具。


    JDK1.5中提供了多线程升级的解决方式:
    将同步synchonized替换成了显示的Lock操作,将Object中的wait、notify、notifyAll替换成了Condition对象。
    该对象能够Lock锁进行获取


    Lock的方法摘要:
    void lock()  获取锁。

     
    Condition newCondition() 返回绑定到此 Lock 实例的新 Condition 实例。 
    void unlock() 释放锁。


    Condition方法摘要:
    void await() 造成当前线程在接到信号或被中断之前一直处于等待状态。
    void signal() 唤醒一个等待线程。

             
    void signalAll() 唤醒全部等待线程。
               
    21、停止线程:
    stop方法已经过时,怎样停止线程?
    停止线程的方法仅仅有一种,就是run方法结束。

    怎样让run方法结束呢?
    开启多线程执行,执行代码一般是循环体,仅仅要控制住循环。就能够让run方法结束。也就是结束线程。


    特殊情况:当线程属于冻结状态,就不会读取循环控制标记,则线程就不会结束。


    为解决该特殊情况,可引入Thread类中的Interrupt方法结束线程的冻结状态。
    当没有指定的方式让冻结线程恢复到执行状态时,须要对冻结进行清除,强制让线程恢复到执行状态
    22、interrupt:
    void interrupt() 中断线程: 
    中断状态将被清除,它还将收到一个 InterruptedException
    22、守护线程(后台线程)
    setDaemon(boolean on):将该线程标记为守护线程或者用户线程。
    当主线程结束。守护线程自己主动结束,比方圣斗士星矢里面的守护雅典娜,
    在多线程里面主线程就是雅典娜,守护线程就是圣斗士,主线程结束了,
    守护线程则自己主动结束。
    当正在执行的线程都是守护线程时。java虚拟机jvm退出;所以该方法必须在启动线程前调用;


    守护线程的特点:
    守护线程开启后和前台线程共同抢夺cpu的执行权,开启、执行两者都没差别,
    但结束时有差别,当全部前台线程都结束后,守护线程会自己主动结束。
    23、多线程join方法:
    void join() 等待该线程终止。


    void join(long millis)  等待该线程终止的时间最长为 millis 毫秒。
    throws InterruptedException         
    特点:当A线程运行到B线程的join方法时,A就会等待B线程都运行完,A才会运行
    作用: join能够用来暂时增加线程运行;
    24、多线程优先级:yield()方法
    yield():暂停当前正在运行的线程对象。并运行其它线程
    setPriority(int newPriority):更改线程优先级
    int getPriority() 返回线程的优先级。
    String toString() 返回该线程的字符串表示形式,包含线程名称、优先级和线程组
               
    (1)MAX_PRIORITY:最高优先级(10级)
    (1)Min_PRIORITY:最低优先级(1级)
    (1)Morm_PRIORITY:默认优先级(5级)


    25、什么是ThreadLocal类,怎么使用它?
    ThreadLocal类提供了线程局部 (thread-local) 变量。是一个线程级别的局部变量。并不是“本地线程”。
    ThreadLocal 为每一个使用该变量的线程,提供了一个独立的变量副本。每一个线程改动副本时不影响其他线程对象的副本


    以下是线程局部变量(ThreadLocal variables)的关键点:
    一个线程局部变量(ThreadLocal variables)为每一个线程方便地提供了一个单独的变量。
    ThreadLocal 实例通常作为静态的私有的(private static)字段出如今一个类中。这个类用来关联一个线程。

     
    当多个线程訪问 ThreadLocal 实例时。每一个线程维护 ThreadLocal 提供的独立的变量副本。


    经常使用的使用可在 DAO 模式中见到。当 DAO 类作为一个单例类时。
    数据库链接(connection)被每个线程独立的维护。互不影响。

    (基于线程的单例)
    26、什么时候抛出InvalidMonitorStateException异常?为什么?
    调用 wait ()/notify ()/notifyAll ()中的不论什么一个方法时,假设当前线程没有获得该对象的锁,
    那么就会抛出 IllegalMonitorStateException 的异常
    也就是说程序在没有运行对象的不论什么同步块或者同步方法时,
    仍然尝试调用 wait ()/notify ()/notifyAll ()时。因为该异常是 RuntimeExcpetion 的子类,
    所以该异常不一定要捕获(虽然你能够捕获仅仅要你愿意
    作为 RuntimeException,此类异常不会在 wait (),notify (),notifyAll ()的方法签名提及。 
    27、在静态方法上使用同步时会发生什么事?
    同步静态方法时会获取该类的“Class”对象,所以当一个线程进入同步的静态方法中时。
    线程监视器获取类本身的对象锁。其他线程不能进入这个类的不论什么静态同步方法。


    它不像实例方法,由于多个线程能够同一时候訪问不同实例同步实例方法。


    28、当一个同步方法已经运行。线程可以调用对象上的非同步实例方法吗?
    能够,一个非同步方法总是能够被调用而不会有不论什么问题。
    实际上。Java 没有为非同步方法做不论什么检查,锁对象只在同步方法或者同步代码块中检查。
    假设一个方法没有声明为同步,即使你在使用共享数据Java照样会调用,而不会做检查是否安全,
    所以在这样的情况下要特别小心。

    一个方法是否声明为同步取决于临界区訪问(critial section access),
    假设方法不訪问临界区(共享资源或者数据结构)就不是必需声明为同步的。


    29、在一个对象上两个线程能够调用两个不同的同步实例方法吗?
    不能,由于一个对象已经同步了实例方法。线程获取了对象的对象锁。
    所以仅仅有运行完该方法释放对象锁后才干运行其他同步方法。


    30、什么是线程饿死。什么是活锁?
    线程饿死和活锁尽管不像死锁一样是常见的问题。可是对于并发编程的设计者来说就像一次邂逅一样。


    当全部线程堵塞。或者因为须要的资源无效而不能处理。不存在非堵塞线程使资源可用。
    JavaAPI 中线程活锁可能发生在下面情形:
    当全部线程在程序中运行 Object.wait (0),參数为 0 的 wait 方法。


    程序将发生活锁直到在对应的对象上有线程调用 Object.notify ()或者 Object.notifyAll ()。
    当全部线程卡在无限循环中。




  • 相关阅读:
    Java第十次作业-房地产;学生成绩计算
    找回感觉之模拟房地产首付计算
    软件工程第四次作业—结对项目
    【作业】需要改进的用户体验二三则
    项目互测测试方式及报告
    [ASE]项目介绍及项目跟进——TANK BATTLE·INFINITE
    Hello World!
    [ASE]sprint3 总结 & sprint4计划
    [ASE][Daily Scrum]12.15
    [ASE][Daily Scrum]12.12
  • 原文地址:https://www.cnblogs.com/jzssuanfa/p/7400078.html
Copyright © 2011-2022 走看看