javaSE_base03_多线程

三、多线程:
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中多线程同步是什么?
     同步是用来解决多线程的安全问题的,在多线程中,同步能控制对共享数据的访问。如果没有同步,当一个线程在
     修改一个共享数据时,而另外一个线程正在使用或者更新同一个共享数据,这样容易导致程序出现错误的结果。
12、什么是锁?锁的作用是什么?
     锁就是对象
     锁的作用是保证线程同步,解决线程安全问题。
     持有锁的线程可以在同步中执行,没有锁的线程即使获得cpu执行权,也进不去。
13、同步的前提:
     (1)必须保证有两个以上线程
     (2)必须是多个线程使用同一个锁,即多条语句在操作线程共享数据
     (3)必须保证同步中只有一个线程在运行
14、同步的好处和弊端
     好处:同步解决了多线程的安全问题
     弊端:多线程都需要判断锁,比较消耗资源
15、同步的两种表现形式:
     (1)同步代码块:
          可以指定需要获取哪个对象的同步锁,使用synchronized的代码块同样需要锁,但他的锁可以是任意对象
          考虑到安全问题,一般还是使用同一个对象,相对来说效率较高。

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

在jdk1.5后,用lock锁取代了synchronized,个人理解也就是对同步代码块做了修改,
     并没有提供对同步方法的修改,主要还是效率问题吧。
16、多线程的单例设计模式:保证某个类中内存中只有一个对象
     (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 ()。
     当所有线程卡在无限循环中。

javaSE_base03_多线程

时间: 2024-10-14 00:38:03

javaSE_base03_多线程的相关文章

Java多线程学习(吐血超详细总结)

林炳文Evankaka原创作品.转载请注明出处http://blog.csdn.net/evankaka 目录(?)[-] 一扩展javalangThread类 二实现javalangRunnable接口 三Thread和Runnable的区别 四线程状态转换 五线程调度 六常用函数说明 使用方式 为什么要用join方法 七常见线程名词解释 八线程同步 九线程数据传递 本文主要讲了java中多线程的使用方法.线程同步.线程数据传递.线程状态及相应的一些线程函数用法.概述等. 首先讲一下进程和线程

Spring多线程

Spring是通过TaskExecutor任务执行器来实现多线程和并发编程的.使用ThreadPoolTaskExecutor可实现一个基于线程池的TaskExecutor.而实际开发中任务一般是非阻碍的,即异步的,所以我们要在配置类中通过@EnableAsync开启对异步的支持,并通过在实际执行的Bean的方法中使用@Async注解来声明其是一个异步任务. 实例代码: (1)配置类 package com.lwh.highlight_spring4.ch3.taskexecutor; /**

python进阶学习(一)--多线程编程

1. 多线程 概念:简单地说操作系统可以同时执行多个不用程序.例如:一边用浏览器上网,一边在听音乐,一边在用笔记软件记笔记. 并发:指的是任务数多余cpu核数,通过操作系统的各种任务调度算法,实现用多个任务"一起"执行(实际上总有一些任务不在执行,因为切换任务的熟度相当快,看上去一起执行而已) 并行:指的是任务数小于等于CPU核数,即任务真的是一起执行的. 2. 线程 概念:线程是进程的一个实体,是CPU调度和分派的基本单位. threading--单线程执行: 1 import ti

多线程的实现及其安全问题

一.进程和线程概述 1.进程:进程是一个具有独立功能的程序关于某个数据集合的一次运行活动,简单来说开启一个程序就开启了一个进程: 如果开启多个进程,它们之间是由于CPU的时间片在相互的切换: 2.线程:开启一个进程的一个任务,对于多线程:每一个线程都在争夺CPU的执行权(CPU的执行权具有随机性): 如果一个程序的执行路径有多条,那么该线程是多线程;反之,就单线程线程:线程是依赖于进程存在的! 3.Jvm是多线程 -- 至少开启了两条线程 main方法 主线程 gc() 垃圾回收线程 二.多线程

多线程和多进程的区别与联系

1.单进程单线程:一个人在一个桌子上吃菜.2.单进程多线程:多个人在同一个桌子上一起吃菜.3.多进程单线程:多个人每个人在自己的桌子上吃菜. 多线程的问题是多个人同时吃一道菜的时候容易发生争抢,例如两个人同时夹一个菜,一个人刚伸出筷子,结果伸到的时候已经被夹走菜了...此时就必须等一个人夹一口之后,在还给另外一个人夹菜,也就是说资源共享就会发生冲突争抢. 1.对于 Windows 系统来说,[开桌子]的开销很大,因此 Windows 鼓励大家在一个桌子上吃菜.因此 Windows 多线程学习重点

Python有了asyncio和aiohttp在爬虫这类型IO任务中多线程/多进程还有存在的必要吗?

最近正在学习Python中的异步编程,看了一些博客后做了一些小测验:对比asyncio+aiohttp的爬虫和asyncio+aiohttp+concurrent.futures(线程池/进程池)在效率中的差异,注释:在爬虫中我几乎没有使用任何计算性任务,为了探测异步的性能,全部都只是做了网络IO请求,就是说aiohttp把网页get完就程序就done了. 结果发现前者的效率比后者还要高.我询问了另外一位博主,(提供代码的博主没回我信息),他说使用concurrent.futures的话因为我全

多线程(一)

这边来谈谈java中,我对对多线程的理解 在了解多线程前,先说说进程. 进程就是正在运行的应用程序.  当你打开任务管理器的时候,你就会发现很多的进程. 而我们要说的线程,就是依赖于进程而存在的,一个进程可以开启多个线程. Thread类 说到线程,就必须来说说Thread类. Thread类是说有线程的父类.具体请参见api 线程的创建以及执行(图解如下) 继承Thread类,或者实现rennable接口. 当继承了父类后,需要重写父类的run方法,这个run方法里面就写你要执行的代码,当这个

多线程下的单例-double check

话不多说直接上代码: public sealed class Singleton { private static Singleton _instance = null; // Creates an syn object. private static readonly object SynObject = new object(); Singleton() { } public static Singleton Instance { get { // Double-Checked Lockin

笔记:多线程

多线程程序在较低的层次上扩展了多任务的概念:一个程序同时执行多个任务,通常每个任务称为一个线程(thread),他是线程控制的简称,可以同时运行一个以上线程的程序称为多线程程序(multithreaded):多线程和多进程有哪些区别呢,本质的区别在于每个进程拥有自己的一整套变量,而线程则是共享数据,Java中启动一个线程的代码如下: // 线程任务的具体实现接口 ????public interface Runnable { public abstract void run(); ????} /