Java 并发实战

synchronized与volatile的区别:
1. volatile本质是在告诉jvm当前变量在寄存器(工作内存)中的值是不确定的,需要从主存中读取;synchronized则是锁定当前变量,只有当前线程可以访问该变量,其他线程被阻塞住。
2. volatile仅能使用在变量级别;synchronized则可以使用在变量、方法、和类级别的
3. volatile仅能实现变量的修改可见性,不能保证原子性;而synchronized则可以保证变量的修改可见性和原子性
4. volatile不会造成线程的阻塞;synchronized可能会造成线程的阻塞。
5. volatile标记的变量不会被编译器优化;synchronized标记的变量可以被编译器优化

线程不安全的原因:竞态条件
解决线程不安全的问题

  1. 程序封闭(线程封闭、栈封闭、Threadlocal封闭)
  2. 只读共享(不可变对象和事实不可变对象,在没有额外的同步的情况下,都是线程安全的)
  3. 线程安全共享(线程安全的对象内部实现同步,多线程可以通过对象的公有接口进行访问)
  4. 保护对象(将其封装在其他线程安全的容器中:vector,hashtable,concurrentSet,concurrentMap,concurrentLinkedList,copyOnWriteArrayList,copyOnWriteArraySet,blockingQueue,synchronizedList,synchronizedSet,synchronizedMap等等)
  5. static初始化由于JVM内部同步机制,也是线程安全的private static final Test test = new Test();

封闭模式:保证任意时刻只有一个线程能够访问对象,常用方法是加锁机制

    // 线程安全的对象,外部不能直接访问
    private static class Test {
        // 非线程安全的对象
        private HashSet<Integer> set = new HashSet<>();

        // 封闭的方法
        public synchronized boolean contains(int x) {
            return set.contains(x);
        }

        // 封闭的方法
        public synchronized void add(int x) {
            set.add(x);
        }
    }

监视器模式

    // 线程安全
    private static class Test {
        // 非线程安全
        private HashSet<Integer> set = new HashSet<>();

        private final Object lock = new Object();

        // 封闭的方法
        public boolean contains(int x) {
            synchronized (lock) {
                return set.contains(x);
            }
        }

        // 封闭的方法
        public void add(int x) {
            synchronized (lock) {
                set.add(x);
            }
        }
    }

委托给线程安全的容器进行管理(多个状态变量时)

    // 线程安全
    private static class Test {
        // 委托给线程安全的容器进行管理
        private Set<Integer> set = Collections.synchronizedSet(new HashSet<>());

        // 线程安全的方法
        public boolean contains(int x) {
            return set.contains(x);
        }

        // 线程安全的方法
        public void add(int x) {
            set.add(x);
        }
    }

必须保证逻辑上的操作是线程安全的

    // 非线程安全
    private static class Test {
        // 线程安全的对象
        private Set<Integer> set = Collections.synchronizedSet(new HashSet<>());
        // 线程安全的对象
        private AtomicInteger currInteger = new AtomicInteger();

        // 逻辑上非线程安全的方法
        public void set(int x) {
            synchronized (this) {
                set.add(x);
                currInteger.set(x);
            }
        }

    }

SynchronizedCollection源码

static class SynchronizedCollection<E> implements Collection<E>, Serializable {
        private static final long serialVersionUID = 3053995032091335093L;

        final Collection<E> c;  // Backing Collection
        final Object mutex;     // Object on which to synchronize

        SynchronizedCollection(Collection<E> c) {
            this.c = Objects.requireNonNull(c);
            mutex = this;
        }

        SynchronizedCollection(Collection<E> c, Object mutex) {
            this.c = Objects.requireNonNull(c);
            this.mutex = Objects.requireNonNull(mutex);
        }

        public int size() {
            synchronized (mutex) {return c.size();}
        }
        public boolean isEmpty() {
            synchronized (mutex) {return c.isEmpty();}
        }
        public boolean contains(Object o) {
            synchronized (mutex) {return c.contains(o);}
        }
        public Object[] toArray() {
            synchronized (mutex) {return c.toArray();}
        }
        public <T> T[] toArray(T[] a) {
            synchronized (mutex) {return c.toArray(a);}
        }

        public Iterator<E> iterator() {
            return c.iterator(); // Must be manually synched by user!
        }

        public boolean add(E e) {
            synchronized (mutex) {return c.add(e);}
        }
        public boolean remove(Object o) {
            synchronized (mutex) {return c.remove(o);}
        }

        public boolean containsAll(Collection<?> coll) {
            synchronized (mutex) {return c.containsAll(coll);}
        }
        public boolean addAll(Collection<? extends E> coll) {
            synchronized (mutex) {return c.addAll(coll);}
        }
        public boolean removeAll(Collection<?> coll) {
            synchronized (mutex) {return c.removeAll(coll);}
        }
        public boolean retainAll(Collection<?> coll) {
            synchronized (mutex) {return c.retainAll(coll);}
        }
        public void clear() {
            synchronized (mutex) {c.clear();}
        }
        public String toString() {
            synchronized (mutex) {return c.toString();}
        }
        // Override default methods in Collection
        @Override
        public void forEach(Consumer<? super E> consumer) {
            synchronized (mutex) {c.forEach(consumer);}
        }
        @Override
        public boolean removeIf(Predicate<? super E> filter) {
            synchronized (mutex) {return c.removeIf(filter);}
        }
        @Override
        public Spliterator<E> spliterator() {
            return c.spliterator(); // Must be manually synched by user!
        }
        @Override
        public Stream<E> stream() {
            return c.stream(); // Must be manually synched by user!
        }
        @Override
        public Stream<E> parallelStream() {
            return c.parallelStream(); // Must be manually synched by user!
        }
        private void writeObject(ObjectOutputStream s) throws IOException {
            synchronized (mutex) {s.defaultWriteObject();}
        }
    }
}

synchronized的嵌套可能造成死锁
synchronizedCollection错误使用

    // 非线程安全
    private static class Test {
        // 线程安全的对象
        private Set<Integer> set = Collections.synchronizedSet(new HashSet<>());

        // 逻辑上非线程安全的方法
        public synchronized void set(int x) {
            // set的加锁对象为自身的mutex,所以无法保证在其他的方法调用里出现线程不安全情况
            if(!set.contains(x)) {
                set.add(x);
            }
        }

        // 逻辑上线程安全的方法
        public void set2(int x) {
            synchronized (set) {
                if (!set.contains(x)) {
                    set.add(x);
                }
            }
        }

    }

同步容器并非绝对不存在线程不安全的操作:例如同步容器的复合操作:

        // 逻辑上非线程安全的方法
        public void set(int x) {
            if (!set.contains(x)) {
                set.add(x);
            }
        }

ArrayIndexOutOfBoundsException :数组越界异常

        // 复合操作多线程出现问题,应该加上同步
        for (int i = 0; i < vector.size(); i++) {
            vector.get(i);
        }

        // 防止别的线程进行操作修改,例如remove
        // 缺点是开销大
        // 还可以使用克隆再遍历,但是效率也不高
        synchronized (vector) {
            for (int i = 0; i < vector.size(); i++) {
                vector.get(i);
            }
        }

同步容器(将所有访问操作都串行化):

vector,hashtable,synchronizedSet等等

ConcurrentModificationException:快速失败异常,迭代的过程中发现元素被其它线程修改了就会马上抛出异常
想要避免快速失败:
1. 持有容器的锁,不允许其他线程修改。缺点:如果容器数据过多,造成长时间持有锁,容易导致饥饿和死锁。
2. 克隆容器,在副本上进行遍历操作。缺点:额外开销
注意:有些函数操作隐藏着迭代器遍历的操作,例如removeAll(),containsAll()

并发容器(代替同步容器,提高伸缩性并降低风险):

ConcurrentHashMap,CopyOnWriteArrayList等等
1. BlockingQueue:为空时阻塞掉获取的线程,等待元素进入队列
2. ConcurrentHashMap:分段锁,弱一致性非快速失败(迭代的过程可以进行修改,并且反应到其他的线程中),实现了复合操作接口
3. CopyOnWriteArrayList:写入时复制,迭代远大于修改操作使用,写入时会进行加锁,读取时不加锁,所以不能保证读取的数据一定最新,非快速失败
4. BlockingQueue:并发安全,重入锁

中断机制:中断机制是一种协作机制,一个线程不能强制停止正在执行的操作而去执行其他操作。
不能捕获不做处理,否则上层的代码无法处理中断
1. 传递中断:直接抛出(明智)
2. 恢复中断:为了更高层次的代码能够看见次异常

private class MyThread implements Runnable {

        @Override
        public void run() {
            try{
                //
            }catch (InterruptedException e){
                Thread.currentThread().interrupt();
            }
        }
    }

同步工具类:

  1. 信号量(Semaphore,控制资源访问)
  2. 栅栏(Barrier,相对于闭锁,等待最后结束并且决定要做什么)
  3. 闭锁(Latch,控制线程同时开始和等待最终的结束)
    这里写图片描述
1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 、4下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合;、下载 4使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合;、 4下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值