并发编程学习笔记(三)——锁的优化

一:编程优化

  • 减少锁的持有时间:只在必要时进行synchronized同步部分方法,避免同步整个方法
  • 减少锁的粒度:通过对数据结构分块处理,如果多个线程处理的数据在多个块,则可以并发执行(当系统需要取得全局锁时开销大)
  • 读写分离锁:通过对系统功能点的分割,读写分离
  • 锁分离:对于不存在竞争关系的方法,可以分离为多个锁
  • 锁粗化:对于一连串请求释放锁的操作,整合成对锁的一次请求释放操作

二:虚拟机的优化

  • 锁偏向:如果一个线程获得了锁,则当其再次申请锁时无需同步操作(适合没有锁竞争的场合)
  • 轻量级锁:将对象头部作为指针,指向持有锁的线程堆栈内部,以此判断线程是否持有对象锁。如果线程获得轻量级锁,则进入临界区,如果没有获得锁,则请求转化为重量级锁
  • 自旋锁:避免重量级锁太快挂起,让其做几个空循环,之后如果还无法获得锁,再挂起
  • 锁消除:通过对运行上下文的扫描,进行逃逸分析(私有变量无需加锁),去除不可能存在共享资源的锁

三:ThreadLocal

1. 基本概念

线程独自拥有一个局部变量,需要在应用层上保证每一个线程有不同的对象,ThreadLocal起到了容器的作用

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

class MyObject {
    String name;

    MyObject(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "MyObject{" + "name='" + name + '\'' + '}';
    }
}

public class ThreadLocalTest {

    static ThreadLocal object = new ThreadLocal<>();

    static class MyThread extends Thread {
        @Override
        public void run() {
            if (object.get() == null) {
                object.set(new MyObject("test"));
            }
            System.out.println(object.get());
        }
    }

    public static void main(String[] args) {
        ExecutorService service = Executors.newFixedThreadPool(10);
        for (int i = 0; i < 1000; i++) {
            service.submit(new MyThread());
        }
    }
}

2. 原理

(1)设置

将数据写入线程持有的ThreadLocalMap ,键-值=对象-当前值

	/**
     * Sets the current thread's copy of this thread-local variable
     * to the specified value.  Most subclasses will have no need to
     * override this method, relying solely on the {@link #initialValue}
     * method to set the values of thread-locals.
     *
     * @param value the value to be stored in the current thread's copy of
     *        this thread-local.
     */
    public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }

(2)获取值

获取线程的ThreadLocalMap,再将自己作为键,获取对应的值

	/**
     * Returns the value in the current thread's copy of this
     * thread-local variable.  If the variable has no value for the
     * current thread, it is first initialized to the value returned
     * by an invocation of the {@link #initialValue} method.
     *
     * @return the current thread's value of this thread-local
     */
    public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }

(3)移除

将对象设置在ThreadLocal中,其实是设置在线程的ThreadLocalMap 中,如果对象没有及时得到清理,那么他将无法回收,造成内存泄露

/**
     * Removes the current thread's value for this thread-local
     * variable.  If this thread-local variable is subsequently
     * {@linkplain #get read} by the current thread, its value will be
     * reinitialized by invoking its {@link #initialValue} method,
     * unless its value is {@linkplain #set set} by the current thread
     * in the interim.  This may result in multiple invocations of the
     * {@code initialValue} method in the current thread.
     *
     * @since 1.5
     */
     public void remove() {
         ThreadLocalMap m = getMap(Thread.currentThread());
         if (m != null)
             m.remove(this);
     }

(4)移除全部

手动设置ThreadLocal=null,回收所有变量

static class Entry extends WeakReference> {
            /** The value associated with this ThreadLocal. */
            Object value;

            Entry(ThreadLocal k, Object v) {
                super(k);
                value = v;
            }
        }

当ThreadLocal设为null时,键为空,则当系统对ThreadLocalMap 进行清理时,就会回收所有失效的(弱)键值对

四:无锁

是一种乐观策略,假设对资源的访问时没有冲突的,所有线程都不会被阻塞

1. 并发策略——CAS

(1)定义

CAS(V,E,N)

  • V是更新的变量
  • E是预期值
  • N是新值

仅当V=E时,V的值才会设为N。如果V≠E,说明已经有其他线程修改了,可以再尝试修改。最后CAS返回V的真实值

(2)特点

  • 程序复杂
  • 避免了死锁,锁之间的影响小,也不会有线程频繁调度的开销

2. 安全包atomic

(1)组成

执行类中的指令时,不会被其他线程打断,直接使用CAS操作

  • 基本类型的原子类:AtomicBoolean,AtomicInteger,AtomicLong
  • 引用类型的原子类:AtomicReference
  • 基本类型数组的原子类:AtomicIntegerArray,AtomicLongArray
  • 引用类型数组的原子类:AtomicReferenceArray
  • 各种变量的原子类:AtomicLongFieldUpdater,AtomicIntegerFieldUpdate,AtomicReferenceFieldUpdater
  • 时间戳的原子类:AtomicStampedReference
  • AtomicMarkableReference

(2)AtomicInteger

保证安全的整数操作

使用
import java.util.concurrent.atomic.AtomicInteger;

public class AtomicTest {
    static AtomicInteger integer = new AtomicInteger();

    static class MyThread extends Thread{
        @Override
        public void run() {
            for (int i = 0; i < 100; i++) {
                integer.incrementAndGet();
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        for (int i = 0; i < 10; i++) {
            MyThread thread = new MyThread();
            thread.start();
            thread.join();
        }
        System.out.println(integer);
    }
}

原理
//当前实际值
private volatile int value;
//实际值的偏移量
private static final long valueOffset;
//委托给非安全的指针操作
public final int incrementAndGet() {
        return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
    }
//CAS操作+本地方法
//不断重试,直到对象的当前值var5和偏移量var2相同,CAS操作成功,将结果赋值给var2
public final int getAndAddInt(Object var1, long var2, int var4) {
        int var5;
        do {
            var5 = this.getIntVolatile(var1, var2);
        } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));

        return var5;
    }

(3)AtomicReference

保证修改对象引用的安全性,但是不能确保对象引用不被修改

import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;

public class AtomicTest {
    static AtomicInteger integer = new AtomicInteger();
    static AtomicReference reference = new AtomicReference<>();
    static class MyThread extends Thread{
        @Override
        public void run() {
            for (int i = 0; i < 100; i++) {
//                integer.incrementAndGet();
                Integer m = reference.get();
                reference.compareAndSet(m,m+1);
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        reference.set(0);
        for (int i = 0; i < 10; i++) {
            MyThread thread = new MyThread();
            thread.start();
            thread.join();
        }
//        System.out.println(integer);
        System.out.println(reference.get());
    }
}

(4)AtomicStampedReference

带有时间戳的对象引用,在内部不仅维护了对象值,也维护了对象的状态值。当对象被修改时,需要同时修改数据和时间戳。当要修改对象时,需要数据和时间戳都满足期望值

import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.atomic.AtomicStampedReference;

public class AtomicTest {
//    static AtomicInteger integer = new AtomicInteger();
//    static AtomicReference reference = new AtomicReference<>();
    static AtomicStampedReference reference = new AtomicStampedReference<>(0,0);
    static class MyThread extends Thread{
        @Override
        public void run() {
            for (int i = 0; i < 100; i++) {
//                integer.incrementAndGet();
                Integer m = reference.getReference();
                final int stamp = reference.getStamp();
                reference.compareAndSet(m,m+1,stamp,stamp+1);
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
//        reference.set(0);
        reference.set(0,0);
        for (int i = 0; i < 10; i++) {
            MyThread thread = new MyThread();
            thread.start();
            thread.join();
        }
//        System.out.println(integer);
        System.out.println(reference.getReference()+" "+reference.getStamp());
    }
}

(5)AtomicIntegerArray

通过CAS方式,控制数组的安全性

import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicIntegerArray;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.atomic.AtomicStampedReference;

public class AtomicTest {
//    static AtomicInteger integer = new AtomicInteger();
//    static AtomicReference reference = new AtomicReference<>();
    static AtomicIntegerArray array = new AtomicIntegerArray(10);
//    static AtomicStampedReference reference = new AtomicStampedReference<>(0,0);
    static class MyThread extends Thread{
        @Override
        public void run() {
            for (int i = 0; i < 10; i++) {
//                integer.incrementAndGet();
//                Integer m = reference.getReference();
//                final int stamp = reference.getStamp();
//                reference.compareAndSet(m,m+1,stamp,stamp+1);
                array.getAndIncrement(i);
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
//        reference.set(0);
//        reference.set(0,0);
        for (int i = 0; i < 10; i++) {
            MyThread thread = new MyThread();
            thread.start();
            thread.join();
        }
//        System.out.println(integer);
//        System.out.println(reference.getReference()+" "+reference.getStamp());
        System.out.println(array);
    }
}

(6)AtomicIntegerFieldUpdater

普通变量也可以进行CAS操作,只能修改可见的、volatile类型的变量,不支持static类型

import java.util.concurrent.atomic.*;

public class AtomicTest {
//    static AtomicInteger integer = new AtomicInteger();
//    static AtomicReference reference = new AtomicReference<>();
//    static AtomicIntegerArray array = new AtomicIntegerArray(10);
//    static AtomicStampedReference reference = new AtomicStampedReference<>(0,0);
    static class MyField {
        volatile int data;
    }
    static AtomicIntegerFieldUpdater updater = AtomicIntegerFieldUpdater.newUpdater(MyField.class,"data");
    static class MyThread extends Thread{
        private MyField field;

        public MyThread(MyField field) {
            this.field = field;
        }

        @Override
        public void run() {
            for (int i = 0; i < 10; i++) {
//                integer.incrementAndGet();
//                Integer m = reference.getReference();
//                final int stamp = reference.getStamp();
//                reference.compareAndSet(m,m+1,stamp,stamp+1);
//                array.getAndIncrement(i);
                updater.incrementAndGet(field);
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
//        reference.set(0);
//        reference.set(0,0);
        MyField field = new MyField();
        for (int i = 0; i < 10; i++) {
//            MyThread thread = new MyThread();
            MyThread thread = new MyThread(field);
            thread.start();
            thread.join();
        }
//        System.out.println(integer);
//        System.out.println(reference.getReference()+" "+reference.getStamp());
//        System.out.println(array);
        System.out.println(field.data);
    }
}

你可能感兴趣的:(JAVA)