多线程复习

内存可见性问题

//同步锁每次都能刷新缓存

 

 
volatile关键字,多线程访问主存(共享数据) 是可见的(保证多线程访问的数据可见性)
相较于线程锁synchronized 是一种较为轻量级的同步策略。
//注意:1.volatile 不具备互斥性
        2.volatile 不能保证 变量的“原子性”

 

 

 

 
原子变量
原子变变量:jdk1.5之后java.util.concurrent.atomic 包下提供了常用的原子变量
                    1.volatile 保存内存可见性
                    2.CAS() 算法保证数据的原子性   (硬件对于并发操作共享数据的支持)

 

JUC包下提供,多线程代替方案

ConcurrentHashMap 优于同步的hashmap,
ConcurrentSkipListMap优于TreeMap
CopyOnWriteArrayList 优于 ArrayList

 

闭锁       final CountDownLatch latch=new CountDownLatch(5);

    public static void main(String[] args) {
        // 闭锁  一个线程执行完,执行下一个
        final CountDownLatch latch=new CountDownLatch(5);
        LatchDemo latchDemo = new LatchDemo(latch);
        long start = System.currentTimeMillis();
        for (int i = 0; i < 5; i++) {
            new Thread(latchDemo).start();
        }


        try {
            latch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }


        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 void run() {
        synchronized (this){
            try {
                for (int i = 0; i <50000 ; i++) {
                    if(i % 2 == 0){
                        System.out.println(i);
                    }
                }
            }finally {
                latch.countDown();
            }
        }
    }

 

 
创建执行线程的方式三:时间Callable接口,相较于实现Runnable接口,可有返回值,并进行 异常抛出
    public static void main(String[] args) {

        ThreadDemo threadDemo = new ThreadDemo();
        //执行Callable 方式, 需要 FutureTask 实现类的支持,运用于接受运算结果。
        //FutureTask  ————> Future的实现类
        FutureTask<Integer> result= new FutureTask<>(threadDemo);
        new Thread(result).start();
        //接受的对象
        Integer integer = null;
        try {
            integer = result.get();
            System.out.println(integer);
        } catch (InterruptedException | ExecutionException e){
            e.printStackTrace();
        }
        
    }
}

class ThreadDemo implements Callable<Integer>{


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

 

Lock (ReentrantLock 同步锁)

    /**
     *
     * @author Qianxy
     * @date
     * @param  synchronized : 隐式锁、
     *         同步锁Lock
     *         注意:是一个显示锁,需要通过lock()方法上锁,必须通过unlock()方法进行释放锁
     *
     * @return
     */
    public static void main(String[] args) {
        Ticket ticket = new Ticket();
        new Thread(ticket,"one:").start();
        new Thread(ticket,"two").start();
        new Thread(ticket,"three").start();
    }
}

class Ticket implements Runnable{


    private  int tick = 100;
    private Lock lock = new ReentrantLock();
    @Override
    public void run() {


        while (tick != 0){
            lock.lock();
            try{
                if(tick > 0 ){
                    System.out.println(Thread.currentThread().getName() + "完成售票, 余票为:" + --tick);
                }
            }finally {
                //释放锁
                lock.unlock();
            }
        }
    }
//优势
ReentrantLock提供了多样化的同步,比如有时间限制的同步,可以被Interrupt的同步(synchronized的同步是不能Interrupt的)等。在资源竞争不激烈的情形下,性能稍微比synchronized差点点。但是当同步非常激烈的时候,synchronized的性能一下子能下降好几十倍。而ReentrantLock确还能维持常态。

//Synchronized 同步
资源竞争不是很激烈的情况下,偶尔会有同步的情形下,synchronized是很合适的。原因在于,编译程序通常会尽可能的进行优化synchronize,另外可读性非常好

ReentrantLock实现原理:
CAS(Compare and Swap) + CLH
CAS:内存值V、预期值A、要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做。改操作是一个原子操作。
CLH:带头结点的双向非循环链表

 

Condition 线程通讯

Condition提供三种 await、signal、signalAll方法 对应wait、notify、notifyAll

 

生产者、消费者和店员案例, 
//主函数
public static void main(String[] args) {

        Clerk clerk = new Clerk();
        Productor productor = new Productor(clerk);
        Consumer consumer = new Consumer(clerk);
        new Thread(productor,"生产者").start();
        new Thread(consumer,";消费者:").start();

}


//店员
class Clerk{
    private int product = 0;

    private Lock lock=new ReentrantLock();

    //获得 condtion 对象
    private Condition condition = lock.newCondition();


    //进货
    public  void get(){//循环次数:0
        lock.lock();

        try{
            while(product >= 1){//为了避免虚假唤醒问题,应该总是使用在循环中
                System.out.println("产品已满!");


                try {
                    condition.await();
                } catch (InterruptedException e) {
                }
            }
            System.out.println(Thread.currentThread().getName() + " : " + ++product);
            condition.signalAll();
        }finally {
            lock.unlock();
        }
    }


    //卖货
    public  void sale(){//product = 0; 循环次数:0

        lock.lock();

        try {
            while(product <= 0){
                System.out.println("缺货!");


                try {
                    condition.await();
                } catch (InterruptedException e) {
                }
            }
            System.out.println(Thread.currentThread().getName() + " : " + --product);
            condition.signalAll();
        }finally {
            lock.unlock();
        }


    }
}
//生产者
class Productor implements Runnable{
    private Clerk clerk;
    public Productor(Clerk clerk) {
        this.clerk = clerk;
    }
    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
            }
            clerk.get();
        }
    }
}
//消费者
class Consumer implements Runnable{
    private Clerk clerk;
    public Consumer(Clerk clerk) {
        this.clerk = clerk;
    }
    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            clerk.sale();
        }
    }
}

 

 
posted @ 2020-09-21 09:19  Money131  阅读(106)  评论(0编辑  收藏  举报