Java多线程编程

来源:互联网 发布:软件设计方案评审 编辑:程序博客网 时间:2024/05/18 00:36
Java多线程编程

java.util.concurrent结构图


1.线程的创建和启动


(1)继承Thread类创建线程类
(2)实现Runnable接口创建线程类,将其对象作为Thread对象的target
(3)实现Callable接口,用FutureTask类包装Callable对象并将其作为Thread对象的target

2.线程的生命周期


(1)使用new关键字新建了线程--新建状态
(2)调用start()方法--就绪状态
(3)线程获得了CPU--运行状态
(4) > 线程调用sleep或join或suspend方法
      > 线程调用阻塞式IO
      > 线程在等待通知(notify)或同步监视器 --阻塞状态
    阻塞状态会返回到就绪状态
(5) > run()方法执行完成或发生错误
      > 调用stop()方法 --死亡状态
     死亡状态不能用start()使它重新启动

3.线程锁


锁存在Java对象头Mark Word中
(1)同步监视器 synchronized(obj) {}
(2)同步方法 synchronized 修饰方法,监视器为this

线程会在以下情况释放同步监视器:

    > 线程结束
    > 线程使用了wait()方法

以下情况不会释放同步监视器:

    > 调用Thread.sleep() Thread.yield() suspend方法

(3)同步锁
Lock类用Java写成,增强了Synchronized的功能
lock()方法只能被一个线程执行,因此lock()后的代码只能由该线程执行

ReentrantLock 可重入锁,表示同一个线程可以多次获得一个锁。

Example:

class Parent {          protected Lock lock = new ReentrantLock();                public void test() {              lock.lock();              try {                  System.out.println("Parent");              } finally {                  lock.unlock();              }                }      }            class Sub extends Parent {                @Override          public void test() {              lock.lock();              try {                  super.test();                  System.out.println("Sub");              } finally {                  lock.unlock();              }          }      }            public class AppTest {          public static void main(String[] args) {              Sub s = new Sub();              s.test();          }      }

Synchronized 也是可重入的
偏向锁-->同一个线程多次获得锁不需要CAS操作,
轻量锁(自旋锁)-->通过执行无用操作占用CPU时间片,短时间内获取锁,
重量锁
属于JVM层面的优化,是互斥锁的具体实现方式

(4)死锁

两个线程互相等待对方释放同步监视器时就会发生死锁

4.控制线程

(1)join线程 join() 阻塞调用线程,直到被join方法加入的join线程完成
(2)后台线程 setDaemon(true) 前台线程死亡,后台线程随之死亡

(3)static sleep() 当前正在执行的线程暂停一段时间
(4)static yield() 当前正在执行的线程转入就绪状态

(5)改变线程优先级 setPriority() getPriority()

(6)线程的协调运行
使用syncoronized关键字同步:
    > wait() 导致当前线程等待
    > notify() 唤醒此同步监视器上等待的任意线程
    > notifyAll() 唤醒此同步监视器上等待的所有线程
    以上三个方法属于Object类
使用Lock对象同步:
使用lock.newCondition()新建condition对象
    > await() 方法
    > signal() 唤醒在此Lock对象上等待的单个线程
    > signalAll() 唤醒在此Lock对象上等待的所有线程

5.线程通信

Java采用了共享内存模型,其中所有实例域,静态域和数组元素存储在堆内存中,可以在线程之间共享
局部变量,方法参数,异常处理器参数在栈内存中,不会在线程之间共享

每个线程都有一个私有的本地内存,保存了共享变量的副本。由JMM(Java内存模型)自动完成与主内存的同步

注意本地内存是一个抽象概念,它涵盖了缓存,写缓冲区,寄存器以及其他的硬件和编译器优化。



6.非锁实现

(1)volatile关键字
使用该关键字会添加lock指令:
    将当前处理器缓存行的数据会写回到系统内存
    这个写回内存的操作会引起在其他CPU里缓存了该内存地址的数据无效
保证了变量的可见性,但不保证操作的原子性。
比如i++ 由 1)从主内存读取保存到本地内存 2)修改本地内存的值 3)写回主内存 组成
如果在第一个步骤后中断,则修改的是旧的值,写回主内存的也是旧的值
(2)原子类型
CAS操作:
有三个操作数--内存位置V,旧的预期值A和新值B。比较V与A,若相同,则使用B更新
原子操作即反复进行CAS操作,直至成功为止
Java中提供了原子类
AtomicInteger,AtomicLong,AtomicBoolean,AtomicReference
AtomicIntegerArray,AtomicLongArray,AtomicReferenceArray
提供了以下方法:
get() 获取值
set() 设置值
getAndSet() 设定新值并返回旧值
lazySet() 启动后台线程进行set()操作,不会发生阻塞
addAndGet() 相当于 i = i + delta 并返回i
getAndAdd() 相当于 t = i; i = newValue; return t;
incrementAndGet() 相当于 ++i
getAndIncrement() 相当于 i++
decrementAndGet() 相当于 --i
getAndDecrement() 相当于 i--
compareAndSet(int expect, int update) CAS操作
weakCompareAndSet(int expect, int update) 类似于上一个方法,但可能存在指令重排序导致操作失败
对于数组类型,第一个参数为int i 表示索引位置

7.线程组
默认情况下子线程与父线程属于同一线程组
可以用ThreadGroup类的构造方法新建线程组
利用Thread(ThreadGroup group, ...)构造方法新建加入到某一线程组的线程。
但不可以中途改变某线程所属的组

8.线程池



线程池预先创建一定数量休眠状态的线程。可以提高响应速度,有效控制并发线程的数量。
以下为Executor框架的类图:

Executors工厂类包含了以下方法:

newCachedThreadPool() 创建可缓存的线程池,由JVM动态调整大小
newFixedThreadPool(int nThreads) 创建具有固定线程数的线程池
newSingleThreadExecutor() 创建单线程的线程池,保证按照提交顺序执行任务
newScheduledThreadPool(int corePoolSize) 创建具有固定线程数的线程池,可以定时以及周期性执行任务
newSingleThreadScheduledExecutor() 创建单线程线程池,可以定时以及周期性执行任务

ExecutorService:

    > submit(task) 向线程池提交任务

ScheduledExecutorService:

    > schedule 指定在delay延迟后执行
    > scheduleAtFixedRate (command, initialDelay, period, unit) 、
    initialDelay后开始执行,每period为一个周期。当执行任务的时间大于指定的间隔时间时,等待该线程执行完毕。
    > scheduleWithFixedDelay (command, initialDelay, delay, unit)
    initialDelay后开始执行,一次任务结束后间隔delay再执行一次

使用shutdown()关闭线程池


9.多线程相关容器

1)同步容器
所有涉及到更新其中内容的方法都是同步的,将所有对容器的访问都串行化
主要包括两类:
    > Vector、Stack、HashTable
    > Collections类中提供的静态工厂方法创建的类
     比如 Collections.synchronized(hashmap)
2)并发容器

    > ConcurrentHashMap--HashMap
    > ConcurrentLinkedQueue--Queue

    > CopyOnWriteArrayList
    > CopyOnWriteArraySet--基于CopyOnWriteArrayList,不允许有重复数据

ConcurrentHashMap将HashMap分为16个桶(默认值),每次只锁一个桶。实现了高并发
迭代时,ConcurrentHashMap没有使用快速失败迭代器,而是采用了弱一致迭代器。
弱一致迭代器可能会也可能不会反映迭代器迭代过程中的插入操作,但是一定会反映迭代器还没有到达的键的更新或删除操作,并且对任何值最多返回一次。

CopyOnWrite即写时复制的容器,当向一个容器添加元素的时候,先复制当前容器,向新容器添加。添加完后再将原容器引用指向新的容器。因此读的时候可以实现并发。


3)可阻塞队列

若BlockingQueue为空,从BlockingQueue取操作将会被阻断进入等待状态,直到BlockingQueue不为空才会被唤醒,若BlockingQueue已满,任何存操作也会被阻断,直到BlockingQueue不为满


BlockingQueue有以下方法:

前两列继承自Queue接口,后两列为可阻塞的方法


 BlockingQueue有四个具体的实现类,根据不同需求,选择不同的实现类:

       > ArrayBlockingQueue:规定大小的BlockingQueue,其构造函数必须带一个int参数来指明其大小。其所含的对象是以FIFO(先入先出)顺序排序的。
       > LinkedBlockingQueue:大小不定的BlockingQueue,若其构造函数带一个规定大小的参数,生成的BlockingQueue有大小限制,若不带大小参数,所生成的BlockingQueue的大小由Integer.MAX_VALUE来决定。其所含的对象是以FIFO顺序排序的。

       > PriorityBlockingQueue:类似于LinkedBlockingQueue,但其所含对象的排序不是FIFO,而是依据对象的自然排序顺序或者是构造函数所带的Comparator决定的顺序。
       > SynchronousQueue:特殊的BlockingQueue,对其的操作必须是放和取交替完成的。


0 0
原创粉丝点击