Java高并发

来源:互联网 发布:淘宝扣12分以后怎么办 编辑:程序博客网 时间:2024/05/21 22:25

线程的基础知识
概念:线程是进程内的执行单元
Java中线程的基本操作

  1. 新建线程 :继承Thread类和实现Runnable接口
  2. 终止线程:Thread.stop()不推荐使用,因为会释放所有的monitor
  3. 中断线程:public void Thred.interrupt()public boolean Thread.isInterrupted()public stataic boolean Thread.interrupted()
  4. 挂起(suspend)和继续执行(resume)线程:suspend()不会释放锁,如果加锁发生在resume之前,则会发生死锁,不推荐使用
  5. 等待线程结束(join)和谦让(yeild)
  6. 守护线程:在后台默默完成一些系统性的服务,比如垃圾回收线程等;当一个Java应用内,只有守护线程时,Java虚拟机就会退出
  7. 线程优先级 Thread.setPriority()
  8. synchronized 加锁的三种方式:指定加锁对象、直接作用于实例方法、直接作用于静态方法
  9. Object.wait() 和Object.notify(): T1取得object监视器,wait释放object监视器;T2取得object监视器,object.notify()释放object监视器;T1等待object监视器,重获object监视器,继续执行
  10. 10.

为什么要并发?

业务模型的需要
- 业务上需要多个执行单元
- HTTP服务器,为每个Socket链接新建一个处理线程
- 让不同的线程处理不同的业务
- 简化任务调度

同步和异步

同步,就是在发出一个调用时,在没有得到结果之前,该 调用 就不返回。但是一旦调用返回,就得到返回值了
异步则是相反,调用在发出之后,这个调用就直接返回了,所以没有返回结果。也就是说,当一个异步过程调用发出后,调用者不会立刻得到结果。而是在调用发出后,被调用者通知调用者,或通过回调函数处理这个调用。

并发和并行

并发的实质是一个物理CPU(也可以多个物理CPU) 在若干道程序之间多路复用,并发性是对有限物理资源强制行使多用户共享以提高效率。
并行性指两个或两个以上事件或活动在同一时刻发生。在多道程序环境下,并行性使多个程序同一时刻可在不同CPU上同时执行。

临界区

临界区是所有所有线程都能访问的公共资源(没一次只有一个线程使用,一旦临界区资源被占用,其他资源要使用必须等待)

Created with Raphaël 2.1.0进程进入区(申请资源) 资源被占用时,暂时不能访问,进入等待队列临界区退出区(释放资源)

阻塞和非阻塞

阻塞:若一个线程占用了临界区资源,则其他所有需要这个资源的线程就必须在这个临界区中进行等待,等待会导致线程挂起
非阻塞:允许多个线程同时进入临界区

死锁、活锁、饥饿

死锁:静态的,抢占资源不释放导致
活锁:线程申请资源失败时就会释放已经获得的资源,一直重复 尝试—失败—尝试—失败的过程
饥饿:线程一直获取不到资源,一直无法运行

并发级别

  • 阻塞:当一个线程进入临界区,其他线程就必须等待
  • 无障碍:
    最弱的非阻塞调度
    线程可以自由出去临界区
    无竞争时,有限布内完成操作;
    有竞争时,回滚数据
  • 无锁:无障碍的,保证有一个线程胜出
  • 无等待:无锁的,要求所有线程必须在有限步内完成;无饥饿的

无锁(Lock-Free)

原理:
CAS: 包含三个参数CAS(V,E,N) V:要更新的值;E:预期值;N:新值。当且仅当V和E相等时,才会将V值改为N,否则说明其他线程做了更新,当前线程什么也不做;CAS返回当前V的真实值。失败的线程不会被挂起,允许再次尝试(线程被挂起时大约需要8万个时钟周期,而一次重试只需要2-3个时钟周期)
无锁类
1.AtomicInteger
源码

private volatile int value;/**     * Atomically sets to the given value and returns the old value.     *     * @param newValue the new value     * @return the previous value     */    public final int getAndSet(int newValue) {        return unsafe.getAndSetInt(this, valueOffset, newValue);    }    /**     * Atomically sets the value to the given updated value     * if the current value {@code ==} the expected value.     *     * @param expect the expected value     * @param update the new value     * @return {@code true} if successful. False return indicates that     * the actual value was not equal to the expected value.     */    public final boolean compareAndSet(int expect, int update) {        return unsafe.compareAndSwapInt(this, valueOffset, expect, update);    }
  • AtomicInteger中真正存储数据的是value变量,而改变量是被volatile修饰的,保证了线程直接的可见性。
  • getAndSet方法通过一个死循环不断尝试赋值操作。而真正的赋值操作交给了unsafe类来实现。
    Unsafe
    非安全的操作:
  • 根据偏移量设置值
  • park()
  • 底层的CAS操作
    Unsafe类是CAS实现的核心
    compareAndSwapInt
public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);

第一个参数是给定的对象,offset是对象内的偏移量(其实就是一个字段到对象头部的偏移量,通过这个偏移量可以快速定位字段),第三个参数是期望值,最后一个是要设置的值。
2.AtomicReference
对引用进行修改
例子

public static final AtomicReference<String> REFERENCE = new AtomicReference<String>("abc");    public static void main(String[] args) {        for (int i = 0; i < 10; i++) {            new Thread() {                public void run() {                    try {                        Thread.sleep(1000);                    } catch (InterruptedException e) {                        // TODO Auto-generated catch block                        e.printStackTrace();                    }                    if (REFERENCE.compareAndSet("abc", "cdf")) {                        System.out.println(Thread.currentThread().getName() + "修改成功");                    } else {                        System.out.println(Thread.currentThread().getName() + "修改失败");                    }                };            }.start();        }    }

是一个模板类,抽象化了数据类型
3.AtomicStampedInteger
为了解决ABA问题
例子

public class AtomicStampedReferenceDemo {    static AtomicStampedReference<Integer> money = new AtomicStampedReference<Integer>(19, 0);// 余额    public static void main(String[] args) {        // 模拟多个线程为用户充值        for (int i = 0; i < 3; i++) {            final int timestamp = money.getStamp();            new Thread() {                public void run() {                    while (true) {                        while (true) {                            Integer m = money.getReference();                            if (m < 20) {                                if (money.compareAndSet(m, m + 20, timestamp, timestamp + 1)) {                                    System.out.println("成功充值!" + "余额是:" + money.getReference());                                    break;                                }                            } else {                                // System.out.println("无需充值");                                break;                            }                        }                    }                };            }.start();            // 用户消费            new Thread() {                public void run() {                    for (int i = 0; i < 4; i++) {                        while (true) {                            int timestamp = money.getStamp();                            Integer m = money.getReference();                            if (m > 10) {                                if (money.compareAndSet(m, m - 10, timestamp, timestamp + 1)) {                                    System.out.println("成功消费10元!" + "余额是:" + money.getReference());                                    break;                                }                            } else {                                System.out.println("没有足够金额");                                break;                            }                        }                        try {                            Thread.sleep(100);                        } catch (InterruptedException e) {                            // TODO Auto-generated catch block                            e.printStackTrace();                        }                    }                };            }.start();        }    }}

4.AtomicIntegerArray
支持无锁的数组
5.AtomicIntegerFieldUpdater
让普通变量也享受原子操作

1.Updater只能修改它可见范围内的变量。因为Updater使用反射得到这个变量,如果变量不可见,就会出错。比如private的变量,就不可行
2.为了确保变量被正确读取,必须是volatile
3.由于CAS操作会通过对象实例中的偏移量直接进行复制,因此不支持static字段,因为Unsafe.objectFieldOffset()不支持静态变量
例子

public class AtomicIntegerFieldUpdaterDemo {    public static class Candidate {        int id;        volatile int score;    }    public final static AtomicIntegerFieldUpdater<Candidate> scoreUpdater = AtomicIntegerFieldUpdater            .newUpdater(Candidate.class, "score");    // 检查Updater是否工作正确    public static AtomicInteger allScore = new AtomicInteger(0);    public static void main(String[] args) throws InterruptedException {        final Candidate stu = new Candidate();        Thread[] t = new Thread[10000];        for (int i = 0; i < 10000; i++) {            t[i] = new Thread() {                public void run() {                    if (Math.random() > 0.4) {                        scoreUpdater.incrementAndGet(stu);                        allScore.incrementAndGet();                    }                }            };            t[i].start();        }        for (int i = 0; i < 10000; i++) {            t[i].join();        }        System.out.println("score=" + stu.score);        System.out.println("allScore=" + allScore);    }}
原创粉丝点击