美文网首页
多线程之 volatile与JUC【简】

多线程之 volatile与JUC【简】

作者: 测试员 | 来源:发表于2019-10-05 19:27 被阅读0次

volatile关键字

1.不具备“互斥性”。
2.不保证“原子性”。
3.保证共享数据在缓存中的可见性,相较于synchronized是一种较为轻量级的同步策略。

说明:线程运行时,其实是在运行当先线程的副本,运行结束后,将结果覆盖,所以出现两个线程操作(改变)共享数据的情况下,会出现A副本修改了之后没有覆盖,B线程已经开始了对共享数据的操作,这就是”不可见“,就出现了问题。而volatile关键字修饰变量之后就会使得内存中的数据保持可见性,线程副本修改之后立即更新。

原子性

java.util.concurrent.atomic包下提供了很多CAS算法保证原子性的类并且具有内存可见性

CAS【当内存值等于预估值时(预估值就是再次读取当前变量)就将变量修改

同步容器类【简介】

ConcurrentHashMap【锁分段机制,默认16个分段,每个分段里有一个HashMap(1.8之后升级成CAS算法,有锁变无锁)】

ConcurrentSkipListSet
ConcurrentSkipListMap【ConcurrentSkipListMap通常优于同步的TreeMap】
CopyOnWriteArrayList 【期望的读和遍历远远大于更新时CopyOnWriteArrayList优于同步的ArrayList】
CopyOnWriteArraySet

闭锁

CountDownLatch,一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待。【大致意思是,其他线程完成之前,改线程一直等待

代码样例

package com.yuan.learn;

import java.util.concurrent.CountDownLatch;

public class 闭锁 {
    public static void main(String[] args) {
        final long start = System.currentTimeMillis();

        final CountDownLatch countDownLatch = new CountDownLatch(5);
        final LatchDemo latchDemo = new LatchDemo(countDownLatch);

        for (int i = 0; i < 5; i++) {
            new Thread(latchDemo).start();
        }

        try {
            countDownLatch.await();
        } catch (InterruptedException e) {
        }
        final long end = System.currentTimeMillis();

        System.out.println("耗时" + (end - start) + "毫秒");
    }
}

class LatchDemo implements Runnable {
    /**
     * 倒计时锁
     */
    private CountDownLatch latch;

    public LatchDemo(CountDownLatch latch) {
        this.latch = latch;
    }

    @Override
    public synchronized void run() {
        try {
            for (int i = 0; i < 50000; i++) {
                if (i % 2 == 0) {
                    System.out.println(i);
                }
            }
        } finally {
            /**
             * 减少闩锁的计数,如果计数达到零,则释放所有等待的线程。如果当前计数大于零,则将其递减。
             * 如果新计数为零,则所有等待的线程都将重新启用以进行线程调度。
             * 如果当前计数等于零,则不会发生任何事情。
             */
            latch.countDown();
        }
    }
}

Callable<T>

相较于实现Runnable接口的方式,多出了返回值,以及可以抛出异常。

代码样例

package com.yuan.learn;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

public class CallableLearn {
    public static void main(String[] args) {
        final CallableDemo callableDemo = new CallableDemo();

        //1.需要FutureTask的支持,用于接收结果
        final FutureTask<Integer> futureTask = new FutureTask<Integer>(callableDemo);
        //2.执行
        new Thread(futureTask).start();
        try {
            //在2执行完之后才会执行 可以用于闭锁
            final Integer integer = futureTask.get();
            System.out.println("integer = " + integer);
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }
    }
}

class CallableDemo implements Callable<Integer> {

    private int a = 0;

    @Override
    public Integer call() throws Exception {
        for (int i = 0; i < 100; i++) {
            a += i;
        }
        return a;
    }
}

同步锁案例:

ABCABCABCABCABC......交替打印十次

package com.yuan.learn;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class 线程按序交替打印 {
    public static void main(String[] args) {
        final AlternateDamo ad = new AlternateDamo();
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 10; i++) {
                    ad.loopA(i);
                }
            }
        }, "A").start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 10; i++) {
                    ad.loopB(i);
                }
            }
        }, "B").start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 10; i++) {
                    ad.loopC(i);
                }
            }
        }, "C").start();
    }
}

class AlternateDamo {
    /**
     * 当前线程的标记
     */
    private String sign = "A";
    /**
     * 同步锁
     */
    private Lock lock = new ReentrantLock();
    Condition conditionA = lock.newCondition();
    Condition conditionB = lock.newCondition();
    Condition conditionC = lock.newCondition();

    /**
     * 打印A线程
     */
    public void loopA(int totalloop) {
        lock.lock();
        try {
            //1.判断
            if (!"A".equals(sign)) {
                conditionA.await();
            }
            //2.打印
//            for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName() + "\t" + "" + "\t" + totalloop);
//            }
            //3.唤醒
            sign = "B";
            conditionB.signal();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    /**
     * 打印B线程
     */
    public void loopB(int totalloop) {
        lock.lock();
        try {
            //1.判断
            if (!"B".equals(sign)) {
                conditionB.await();
            }
            //2.打印
//            for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName() + "\t" + "" + "\t" + totalloop);
//            }
            //3.唤醒
            sign = "C";
            conditionC.signal();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }

    }

    /**
     * 打印C线程
     */
    public void loopC(int totalloop) {
        lock.lock();
        try {
            //1.判断
            if (!"C".equals(sign)) {
                conditionC.await();
            }
            //2.打印
//            for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName() + "\t" + "" + "\t" + totalloop);
//            }
            //3.唤醒
            sign = "A";
            conditionA.signal();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

}

读写锁案例

package com.yuan.learn;

import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class 读写锁 {
    
    /**
     * 读读 不互斥
     * 读写 互斥
     * 写写 互斥
     */
    public static void main(String[] args) {
        final RWDemo rwDemo = new RWDemo();
        new Thread(new Runnable() {
            @Override
            public void run() {
                rwDemo.setNum((int) (Math.random() * 101));
            }
        }, "Write").start();

        for (int i = 0; i < 100; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    rwDemo.getNum();
                }
            }, "Read").start();

        }

    }
}

class RWDemo {
    int num = 0;
    ReadWriteLock lock = new ReentrantReadWriteLock();

    /**
     * 读方法用读锁
     * @return
     */
    public int getNum() {
        lock.readLock().lock();
        try {
            System.out.println(Thread.currentThread().getName() +" " + num);
        } finally {
            lock.readLock().unlock();
        }
        return num;
    }

    /**
     * 写方法用写锁
     *
     * @param num
     */
    public void setNum(int num) {
        lock.writeLock().lock();
        try {
            this.num = num;
            System.out.println(Thread.currentThread().getName() +" " + num);
        } finally {
            lock.writeLock().unlock();
        }
    }
}

线程八锁

1.两个普通同步方法,两个线程,标准打印,结果是【one two】
2.getOne()方法新增sleep(3000),结果是【one two】
3.新增普通方法getThree() 不加 synchronize 并且调用 结果是【Three one two】
4.去掉getThree()方法,两个同步方法,两个线程对象,同时调用,【two one】
5.修改getOne()方法为静态方法 ,一个线程对象调用【two one】
6.getTwo()方法也改为静态同步方法,一个线程对象调用【one two】
7.将getTwo()方法 去掉static ,两个线程对象调用【two one】
8.两个静态方法,俩个独享调用【one two】

结论:非静态方法的锁默认是this,静态方法的默认锁是class实例,无论是几个方法,只要是一个线程对象调用,只有一个方法会拿到所

相关文章

网友评论

      本文标题:多线程之 volatile与JUC【简】

      本文链接:https://www.haomeiwen.com/subject/zhrypctx.html