JUC实战之利用JUC解决问题

题目1 使用3个线程循环打印10遍ABC

方案一

    public static void main(String[] args) {
    
    

        Maj maj = new Maj();

        Thread a = new Thread(new ThreadABC(maj,"A"));
        Thread b = new Thread(new ThreadABC(maj,"B"));
        Thread c = new Thread(new ThreadABC(maj,"C"));

        a.start();
        b.start();
        c.start();

    }

    static class  ThreadABC implements Runnable{
    
    

        private String content;

        private Maj maj;

        public ThreadABC(Maj maj, String content) {
    
    
            this.content = content;
            this.maj = maj;
        }

        @Override
        public void run(){
    
    
            try{
    
    
                while (true){
    
    
                    if(maj.cnt_a == 10 && maj.cnt_b == 10 && maj.cnt_c == 10){
    
    
                        break;
                    }
                    synchronized (maj){
    
    

                        switch (content){
    
    
                            case "A":
                                if(maj.cnt_a == maj.cnt_b && maj.cnt_a == maj.cnt_c){
    
    
                                    System.out.println(content);
                                    maj.cnt_a++;
                                    maj.notifyAll();
                                }else {
    
    
                                    maj.wait();
                                }
                            break;

                            case "B":
                                if(maj.cnt_a > maj.cnt_b && maj.cnt_b == maj.cnt_c){
    
    
                                    System.out.println(content);
                                    maj.cnt_b++;
                                    maj.notifyAll();
                                }else {
    
    
                                    maj.wait();
                                }
                                break;
                            case "C":
                                if(maj.cnt_a > maj.cnt_c && maj.cnt_a > maj.cnt_c){
    
    
                                    System.out.println(content);
                                    maj.cnt_c++;
                                    maj.notifyAll();
                                }else {
    
    
                                    maj.wait();
                                }
                                break;
                        }
                    }

                }
            }catch (Exception ex){
    
    

            }
        }
    }

    static class Maj {
    
    
        public volatile int cnt_a = 0;
        public volatile int cnt_b = 0;
        public volatile int cnt_c = 0;
    }

但是注意这个方案其实是错误的。会导致程序卡死。但这个仍然是唯一可行的方案。其实严格按照题目要求是不可实现的。

如果我们允许修改原题目给的模版,那么这个就变成了很轻松实现实现的东西。方案二

package org.luzhen.test;
 
import java.awt.image.VolatileImage;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
 
/**
 * @Author: luzhen
 * @Date: 2019-02-12 13:09
 * @Version 1.0
 */
public class ConditionTest {
    
    
 
    static Lock lock = new ReentrantLock();
    final static Condition condition1 = lock.newCondition();
    final static Condition condition2 = lock.newCondition();
    final static Condition condition3 = lock.newCondition();
    private volatile int state = 0;
 
 
    public static void main(String[] args) {
    
    
        ConditionTest conditionTest = new ConditionTest();
        
        new Thread(() -> {
    
    
            conditionTest.printA("A", 0,1, condition1, condition2);
        }).start();
 
        new Thread(() -> {
    
    
            conditionTest.printA("B", 1,2, condition2, condition3);
        }).start();
 
        new Thread(() -> {
    
    
            conditionTest.printA("C", 2,0, condition3, condition1);
        }).start();
    }
 
    /**
     * 
     * @param s 打印的字符串内容
     * @param inParameter 初始化的state入参值
     * @param expectNextState 控制打印顺序的开关 让对应的哪个线程执行打印
     * @param currentCondition 由于3个condition 会创建3个阻塞队列 代表当前condition所对应的那个阻塞队列
     * @param nextCondition 通知我们规定的那个线程 从对应的等待队列里进入同步队列中 获取锁 执行打印
     */
    public void printA(String s, int inParameter,int expectNextState, Condition currentCondition, Condition nextCondition) {
    
    
        for (int i = 0; i < 10; i++) {
    
    
            try {
    
    
                lock.lock();
                while (state != inParameter) {
    
    
                    currentCondition.await();
                }
                System.out.println(s);
                state = expectNextState;
                nextCondition.signal();
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            } finally {
    
    
                lock.unlock();
            }
        }
    }
}

题目二

写一个简单的callable和future的使用

public class Test {
    
    
    public static void main(String[] args) {
    
    
        ExecutorService executor = Executors.newCachedThreadPool();
        Task task = new Task();
        Future<Integer> result = executor.submit(task);
        executor.shutdown();

        try {
    
    
            Thread.sleep(1000);
        } catch (InterruptedException e1) {
    
    
            e1.printStackTrace();
        }

        System.out.println("主线程在执行任务");

        try {
    
    
            System.out.println("task运行结果"+result.get());
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        } catch (ExecutionException e) {
    
    
            e.printStackTrace();
        }

        System.out.println("所有任务执行完毕");
    }
}
class Task implements Callable<Integer>{
    
    
    @Override
    public Integer call() throws Exception {
    
    
        System.out.println("子线程在进行计算");
        Thread.sleep(3000);
        int sum = 0;
        for(int i=0;i<100;i++)
            sum += i;
        return sum;
    }
}

题目三 描述下concurrentHashMap中的ReentrantLock的使用

ConcurrentHashMap中的segment作为ReentrantLock的包装。

    static class Segment<K,V> extends ReentrantLock implements Serializable {
    
    
        private static final long serialVersionUID = 2249069246763182397L;
        final float loadFactor;
        Segment(float lf) {
    
     this.loadFactor = lf; }
    }

必须清楚,JDK8中的segment已经被遗弃了。现在保留这个纯属序列化兼容需要。
那我们看看JDK7中的使用呢?

    public V put(K key, V value) {
    
    
        Segment<K,V> s;
        if (value == null)
            throw new NullPointerException();
        int hash = hash(key.hashCode());
        int j = (hash >>> segmentShift) & segmentMask;
        if ((s = (Segment<K,V>)UNSAFE.getObject          // nonvolatile; recheck
                (segments, (j << SSHIFT) + SBASE)) == null) //  in ensureSegment
            s = ensureSegment(j);
        return s.put(key, hash, value, false);
    }

可见putvalue最后调用的是segment的put

        final V put(K key, int hash, V value, boolean onlyIfAbsent) {
    
    
            HashEntry<K,V> node = tryLock() ? null :
                    scanAndLockForPut(key, hash, value);
            V oldValue;
            try {
    
    
                HashEntry<K,V>[] tab = table;
                int index = (tab.length - 1) & hash;
                HashEntry<K,V> first = entryAt(tab, index);
                for (HashEntry<K,V> e = first;;) {
    
    
                    if (e != null) {
    
    
                        K k;
                        if ((k = e.key) == key ||
                                (e.hash == hash && key.equals(k))) {
    
    
                            oldValue = e.value;
                            if (!onlyIfAbsent) {
    
    
                                e.value = value;
                                ++modCount;
                            }
                            break;
                        }
                        e = e.next;
                    }
                    else {
    
    
                        if (node != null)
                            node.setNext(first);
                        else
                            node = new HashEntry<K,V>(hash, key, value, first);
                        int c = count + 1;
                        if (c > threshold && tab.length < MAXIMUM_CAPACITY)
                            rehash(node);
                        else
                            setEntryAt(tab, index, node);
                        ++modCount;
                        count = c;
                        oldValue = null;
                        break;
                    }
                }
            } finally {
    
    
                unlock();
            }
            return oldValue;
        }

        private HashEntry<K,V> scanAndLockForPut(K key, int hash, V value) {
    
    
            HashEntry<K,V> first = entryForHash(this, hash);
            HashEntry<K,V> e = first;
            HashEntry<K,V> node = null;
            int retries = -1; // negative while locating node
            while (!tryLock()) {
    
    
                HashEntry<K,V> f; // to recheck first below
                if (retries < 0) {
    
    
                    if (e == null) {
    
    
                        if (node == null) // speculatively create node
                            node = new HashEntry<K,V>(hash, key, value, null);
                        retries = 0;
                    }
                    else if (key.equals(e.key))
                        retries = 0;
                    else
                        e = e.next;
                }
                else if (++retries > MAX_SCAN_RETRIES) {
    
    
                    lock();
                    break;
                }
                else if ((retries & 1) == 0 &&
                        (f = entryForHash(this, hash)) != first) {
    
    
                    e = first = f; // re-traverse if entry changed
                    retries = -1;
                }
            }
            return node;
        }

可见,当put value抢不到锁的时候,就会循环等待。如果循环若干次也不行吗,那么就直接调用lock来等待锁。

题目四 写一些JUC中锁的例子来证明你懂

  • Semaphore
import java.util.concurrent.ExecutorService;     
import java.util.concurrent.Executors;     
import java.util.concurrent.Semaphore;     
    
public class TestSemaphore {
    
         
    
    public static void main(String[] args) {
    
         
        // 线程池     
        ExecutorService exec = Executors.newCachedThreadPool();     
    
        // 只能5个线程同时访问     
    
        final Semaphore semp = new Semaphore(5);     
    
        // 模拟20个客户端访问     
        for (int index = 0; index < 20; index++) {
    
         
            final int NO = index;     
            Runnable run = new Runnable() {
    
         
                public void run() {
    
         
                    try {
    
         
                        // 获取许可     
                        semp.acquire();     
    
                        System.out.println("Accessing: " + NO);     
    
                        Thread.sleep((long) (Math.random() * 10000));     
    
                        // 访问完后,释放     
                        semp.release();     
                    } catch (InterruptedException e) {
    
         
    
                    }     
    
                }     
    
            };     
            exec.execute(run);     
        }     
    
        // 退出线程池     
        exec.shutdown();     
    }     
}  
  • CountDownLatchDemo
package com.example.demo.CountDownLatchDemo;

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

/**
 * 主线程等待子线程执行完成再执行
 */
public class CountdownLatchTest1 {
    
    
    public static void main(String[] args) {
    
    
        ExecutorService service = Executors.newFixedThreadPool(3);
        final CountDownLatch latch = new CountDownLatch(3);
        for (int i = 0; i < 3; i++) {
    
    
            Runnable runnable = new Runnable() {
    
    
                @Override
                public void run() {
    
    
                    try {
    
    
                        System.out.println("子线程" + Thread.currentThread().getName() + "开始执行");
                        Thread.sleep((long) (Math.random() * 10000));
                        System.out.println("子线程"+Thread.currentThread().getName()+"执行完成");
                        latch.countDown();//当前线程调用此方法,则计数减一
                    } catch (InterruptedException e) {
    
    
                        e.printStackTrace();
                    }
                }
            };
            service.execute(runnable);
        }

        try {
    
    
            System.out.println("主线程"+Thread.currentThread().getName()+"等待子线程执行完成...");
            latch.await();//阻塞当前线程,直到计数器的值为0
            System.out.println("主线程"+Thread.currentThread().getName()+"开始执行...");
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
    }
}
  • CyclicBarrier
public class CyclicBarrierDemo {
    
    

    static class TaskThread extends Thread {
    
    
        
        CyclicBarrier barrier;
        
        public TaskThread(CyclicBarrier barrier) {
    
    
            this.barrier = barrier;
        }
        
        @Override
        public void run() {
    
    
            try {
    
    
                Thread.sleep(1000);
                System.out.println(getName() + " 到达栅栏 A");
                barrier.await();
                System.out.println(getName() + " 冲破栅栏 A");
                
                Thread.sleep(2000);
                System.out.println(getName() + " 到达栅栏 B");
                barrier.await();
                System.out.println(getName() + " 冲破栅栏 B");
            } catch (Exception e) {
    
    
                e.printStackTrace();
            }
        }
    }
    
    public static void main(String[] args) {
    
    
        int threadNum = 5;
        CyclicBarrier barrier = new CyclicBarrier(threadNum, new Runnable() {
    
    
            
            @Override
            public void run() {
    
    
                System.out.println(Thread.currentThread().getName() + " 完成最后任务");
            }
        });
        
        for(int i = 0; i < threadNum; i++) {
    
    
            new TaskThread(barrier).start();
        }
    }
    
}

猜你喜欢

转载自blog.csdn.net/define_us/article/details/110671840
JUC