Java多线程

来源:互联网 发布:python解析字符串re 编辑:程序博客网 时间:2024/05/21 02:51
  • 1多线程的优势:进程间不能共享内存,而线程间很容易,创建线程的代价比较小,java语言内置了多线程功能(一个浏览器同时下载多张图片)
    1) 简而言之,一个程序至少有一个进程,一个进程至少有一个线程.
    2) 线程的划分尺度小于进程,使得多线程程序的并发性高。
    3) 另外,进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序的运行效率。
    4) 线程在执行过程中与进程还是有区别的。每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口。但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。
    5) 从逻辑角度来看,多线程的意义在于一个应用程序中,有多个执行部分可以同时执行。但操作系统并没有将多个线程看做多个独立的应用,来实现进程的调度和管理以及资源分配。
  • 2线程的生命周期:
    这里写图片描述
  • 3.创建线程:
    继承Thread:定义threadf的子类,重写该类的run方法,创建子类的实例,调用start()方法,new FirstThread().start();
    实现runnable接口:定义runnable的实现类,重写run方法,创建实现类的实例,并用该实例作为thread的target创建并启动thread对象,new Thread(runnable).start();
    实现callable接口:创建callable的实现类,重写call方法()有返回值,创建实现类的实例,用futuretask(实现了future接口和runnable接口)包装了callable对象和他的返回值,使用futuretask对象作为thread的target来创建并启动thread对象,调用futuretask对象的get方法来获得返回值
    比较:
    runnable和callable实现了接口,还可以继承其他类,多个线程可以共享一个target对象,但如果需要访问当前线程需要使用Thread.currentThread()方法,编程稍复杂(call比run更强大:有返回值并能抛出异常)
    继承thread编写简单,如果需要访问当前线程可直接使用this,多个线程之间不能共享线程类的实例变量,不能再继承其他父类
    判断线程是否死亡:isAlive()方法,就绪、运行、阻塞——True,新建,死亡——false,只能对新建状态的线程调用start()方法,而且不能调用两次
  • 4.控制线程:
    join线程:调用线程将被阻塞,直到join()方法加入的线程执行完为止
    后台线程:为其他线程提供服务,如果所有前台线程死亡,后台线程会自动死亡(jvm的垃圾回收线程),在start方法之前调用setDaemon方法可设置后台线程,isDaemon判断指定线程是否为后台线程
    线程睡眠:sleep——阻塞
    线程让步:yield使线程运行状态转为就绪,给优先级相同或更高的线程执行机会
  • 5.线程同步:
    同步代码块(synchronized):
    java允许多线程并发控制,当多个线程同时操作一个可共享的资源变量时(如数据的增删改查), 将会导致数据不准确,相互之间产生冲突,用被并发访问的共享资源充当同步监视器,对同步监视器加锁——修改——释放锁,任何时刻只有一个线程可以获得对同步监视器的锁定
    同步方法(synchronized):类似于同步代码块,同步监视器是类的默认实例
    同步代码块与同步方法的区别:
    同步代码块的同步监视器是一个共享资源变量,它是细粒度的并发控制,只会将代码快同步,之外的仍然可以被其他线程访问;同步方法是粗粒度的并发控制,监视对象是类的实例或者类本身,某一时刻只有一个线程能执行该方法
    释放同步监视器的锁定:同步方法,同步代码块执行结束;break,return终止了方法或代码的运行;出现了error和exception;当前线程执行了同步监视器的wait方法,当前线程暂停
    线程通信:
    同步监视器来调用方法
    wait——导致当前线程等待,只到其他线程调用notify,notifyall方法来唤醒,调用wait方法的线程会释放对同步监视器的锁定;
    notify:任意唤醒一个在当前同步监视器上等待的一个线程,只有当前线程放弃对同步监视器锁定后才能执行被唤醒的线程
    notifyall:唤醒此同步监视器上的所有线程,只有当前线程当前线程放弃对同步监视器锁定后才能执行被唤醒的线程
    注意:一个线程被唤醒不代表立即获取了对象的锁,只有等调用完notify()或者notifyAll()并退出synchronized块,释放对象锁后,其余线程才可获得锁执行。
    lock:比较常用的有ReentrantLock(可重入锁),一般用法是将需要在lock()和unlock()方法之间执行的代码放在try{}块中,并且在finally{}块中调用unlock()方法,lock比synchronized功能更强大
    lock对象生成condition实例,调用condition实例的方法。
    await:类似wait,导致当前线程等待,只到其他线程调用condition的signal,signalAll方法来唤醒,调用await方法的线程会释放对lock对象的锁定;
    signal:类似notify,任意唤醒一个在lock对象上等待的一个线程,只有当前线程当前线程放弃对lock对象锁定后才能执行被唤醒的线程
    signalall:类似notifyAll,唤醒lock对象上的所有线程,只有当前线程当前线程放弃对lock对象锁定后才能执行被唤醒的线程
    阻塞队列通信:生产者线程放入元素,如果队列已满则该线程被阻塞;消费者取出元素,如果队列已空则该线程被阻塞
  • 6.线程组:对一批线程进行管理,如果没有显式指定,属于默认线程组,默认情况下子线程和创建它额线程处于同一个线程组
  • 7.线程池:程序把runnable或callable对象传给线程池,线程池启动一个线程执行他们的方法,方法执行完后线程并不会死亡,再次返回线程池成为空闲状态,等待执行下一个runnable或callable对象的方法;
    线程池的优点:
    -重用线程池的线程,避免创建和销毁带来的开销
    -系统中有大量并发线程会导致系统性能下降,甚至导致jvm崩溃,线程池的最大线程数可以控制这一点
    -能够对线程进行简单的管理,并提供定时执行和制定间隔循环执行等功能
    Android线程池:
    FixedThreadPool:有固定数量的不会被回收的核心线程,,任务队列大小没有限制,可以快速地响应外界请求
    CachedThreadPool:只有非核心线程,最大线程数为Integer.MAX_VALUE,超时时间为60s,,任何任务会被立即执行,适合大量耗时较少的任务
    ScheduledThreadPool:核心线程数固定,非核心线程数没有限制,非核心线程闲置会立即回收,主要用于执行定时任务和固定周期的重复任务
    SingleThreadExecutor:只有一个核心线程,所有任务都在同一个线程中按顺序执行
  • 8.ThreadLocal:线程局部变量,为每一个使用该变量的线程提供一个变量值的副本,就像完全拥有该变量一样
    在ThreadLocal类中定义了一个ThreadLocalMap,每一个Thread中都有一个该类型的变量——threadLocals——用于存储每一个线程的变量副本,Map中元素的键为线程对象,而值对应线程的变量副本。
    同步机制是为了同步多个线程对相同资源的并发访问,threadlocal是为了隔离多个线程的数据共享,避免了对共享资源的竞争
  • 9.Synchronized代码块和方法:
    代码块:对共享资源变量加锁,细粒度,对代码块加锁,代码块以外的地方其他线程能够访问
    静态方法:对类本身加锁,粗粒度,
    某个类Object中的某个静态方法staticMethod加上同步锁之后,当某个线程Thread调用staticMethod时该线程会获取Object类的锁,此时其他任何线程在Thread释放锁前都无法调用Object类中任何的同步静态方法。
    非静态方法:对类对象加锁,粗粒度,某个类Object中的某个非静态方法Method加上同步锁之后,当某个线程Thread调用Method时会获取所对应的Object对象的锁,此时其他线程在Thread释放锁前都无法调用改对象中的任何同步非静态方法
  • 10.wait和sleep
    wait是object的方法,会导致线程释放同步锁,用在同步方法或代码块,不需要捕获异常
    sleep是Thread的方法,不会导致线程释放同步锁,用在任何地方,必须捕获异常
  • 11.synchronized和lock,Atomic
    • synchronized是在JVM层面实现的,若代码执行时出现异常,JVM 会自动释放锁定,lock是通过代码实现的,将unlock()方法放在finally()快中保证锁一定会被释放
    • lock可以有多个condition(通过lock.newCondition()方法),这样就有多路等待和通知,而synchronized的notify会随机唤醒等待队列的线程
    • 线程之间竞争不激烈的时候,synchronized的性能稍好;线程之间竞争激烈的时候,lock的性能更好。JVM可以花更少的时间来调度线程,把更多时间用在执行线程上。
    • synchronized最终由native的物理锁实现的。synchronized是一种悲观锁,lock是乐观锁
    • reentrant有一个锁的获取计数器,拥有锁的线程再次得到锁,获取计数器加1,锁需要被释放两次才能得到真正释放。类似于synchronized的语义,如果线程进入该线程已经拥有的同步监视器保护的同步代码块,那么线程必须退出这两个代码块才会释放锁
    • reentrantLock提供了可轮询的锁请求,它会尝试获得锁,成功继续处理,不成功则等下次运行的时候处理,而synchronized一旦进入锁要么请求成功,要么一直阻塞,所以更容易产生死锁
    • 当 JVM 用 synchronized 管理锁定请求和释放时,JVM 在生成线程转储时能够包括锁定信息。这些对调试非常有价值,因为它们能标识死锁或者其他异常行为的来源。 Lock 类只是普通的类,JVM 不知道具体哪个线程拥有 Lock 对象(java的线程转储可以被定义为JVM中在某一个给定的时刻运行的所有线程的快照)
1 0
原创粉丝点击