zoukankan      html  css  js  c++  java
  • JUC

    1.多线程操作共享数据

    1.多线程操作共享数据时,会出问题,那么就可以使用java.util.concurrent.Atomic类定义共享数据,这个工具包保证类中的数据都具有原子性.

    具体实例如下:

    package com.atguigu.juc;
    
    import java.util.concurrent.atomic.AtomicInteger;
    
    /*
     * 一、i++ 的原子性问题:i++ 的操作实际上分为三个步骤“读-改-写”
     *           int i = 10;
     *           i = i++; //10
     * 
     *           int temp = i;
     *           i = i + 1;
     *           i = temp;
     * 
     * 二、原子变量:在 java.util.concurrent.atomic 包下提供了一些原子变量。
     *         1. volatile 保证内存可见性
     *         2. CAS(Compare-And-Swap) 算法保证数据变量的原子性
     *             CAS 算法是硬件对于并发操作的支持
     *             CAS 包含了三个操作数:
     *             ①内存值  V
     *             ②预估值  A
     *             ③更新值  B
     *             当且仅当 V == A 时, V = B; 否则,不会执行任何操作。
     */
    public class TestAtomicDemo {
    
        public static void main(String[] args) {
            AtomicDemo ad = new AtomicDemo();
            
            for (int i = 0; i < 10; i++) {
                new Thread(ad).start();
            }
        }
        
    }
    
    class AtomicDemo implements Runnable{
        
    //    private volatile int serialNumber = 0;
        
        private AtomicInteger serialNumber = new AtomicInteger(0);
    
        @Override
        public void run() {
            
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
            }
            
            System.out.println(getSerialNumber());
        }
        
        public int getSerialNumber(){
            return serialNumber.getAndIncrement();
        }
        
        
    }
    View Code

     2.concurentHashMap

    因为hashtable是线程安全的,但是效率低,所以concurrentHashMap用于替换hashtable.hashtable对象只有一个锁,所以效率会极低,但是cocurrentHashMap中使用的是分段锁,即有多个锁,每个锁负责一部分数据.这样多线程访问时就是并行操作而不是像hashtable那样的串行.如下截图:

        package com.atguigu.juc;
    
        import java.util.Iterator;
        import java.util.concurrent.CopyOnWriteArrayList;
    
        /*
         * CopyOnWriteArrayList/CopyOnWriteArraySet : “写入并复制”
         * 注意:添加操作多时,效率低,因为每次添加时都会进行复制,开销非常的大。并发迭代操作多时可以选择。
         */
        public class TestCopyOnWriteArrayList {
    
            public static void main(String[] args) {
                HelloThread ht = new HelloThread();
                
                for (int i = 0; i < 10; i++) {
                    new Thread(ht).start();
                }
            }
            
        }
    
        class HelloThread implements Runnable{
            
        //    private static List<String> list = Collections.synchronizedList(new ArrayList<String>());
            
            private static CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
            
            static{
                list.add("AA");
                list.add("BB");
                list.add("CC");
            }
    
            @Override
            public void run() {
                
                Iterator<String> it = list.iterator();
                
                while(it.hasNext()){
                    System.out.println(it.next());
                    
                    list.add("AA");
                }
                
            }
            
        }
    View Code

    3.countDownLatch闭锁

    闭锁:在完成某些运算时,只有其他所有的线程运算全部完成,当前运算才继续执行.

    例如在主线程中计算多线程执行的时间,因为主线程和在主线程中开启的多线程是同步操作,即主线程不会等待所有子线程执行完才会的操作.所以就需要使用闭锁,让主线程在多线程执行时等待,执行完后计算时间差.如下代码:

    package com.atguigu.juc;
    
    import java.util.concurrent.CountDownLatch;
    
    /*
     * CountDownLatch :闭锁,在完成某些运算是,只有其他所有线程的运算全部完成,当前运算才继续执行
     */
    public class TestCountDownLatch {
    
        public static void main(String[] args) {
            //50的数字必须要和开启的线程数相同,因为每个线程执行完都要让锁个数减一
            final CountDownLatch latch = new CountDownLatch(50);
            LatchDemo ld = new LatchDemo(latch);
    
            long start = System.currentTimeMillis();
    
            for (int i = 0; i < 50; i++) {
                new Thread(ld).start();
            }
    
            try {
                latch.await();
            } catch (InterruptedException e) {
            }
    
            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() {
    
            try {
                for (int i = 0; i < 50000; i++) {
                    if (i % 2 == 0) {
                        System.out.println(i);
                    }
                }
            } finally {
                //线成执行完成后闭锁减一
                latch.countDown();
            }
    
        }
    
    }
    View Code

     4.实现callable接口

    因为callable可以返回线程执行结果,所以主线程也需要等待callable执行完成.所以接收callable的返回值的类也可以用于实现闭锁

    package com.atguigu.juc;
    
    import java.util.concurrent.Callable;
    import java.util.concurrent.ExecutionException;
    import java.util.concurrent.FutureTask;
    
    /*
     * 一、创建执行线程的方式三:实现 Callable 接口。 相较于实现 Runnable 接口的方式,方法可以有返回值,并且可以抛出异常。
     * 
     * 二、执行 Callable 方式,需要 FutureTask 实现类的支持,用于接收运算结果。  FutureTask 是  Future 接口的实现类
     */
    public class TestCallable {
        
        public static void main(String[] args) {
            ThreadDemo td = new ThreadDemo();
            
            //1.执行 Callable 方式,需要 FutureTask 实现类的支持,用于接收运算结果。
            FutureTask<Integer> result = new FutureTask<>(td);
            
            new Thread(result).start();
            
            //2.接收线程运算后的结果
            try {
                Integer sum = result.get();  //FutureTask 可用于 闭锁
                System.out.println(sum);
                System.out.println("------------------------------------");
            } 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 <= 100000; i++) {
                sum += i;
            }
            
            return sum;
        }
        
    }
    
    /*class ThreadDemo implements Runnable{
    
        @Override
        public void run() {
        }
        
    }*/
    View Code

    5.同步锁

    解决多线程安全问题方式:

     1.同步代码块

     2.同步方法

     3.jdk5以后: 注意---是一个显示锁,需要通过lock()方法上锁,必须通过unlock()方法解锁.

    package com.atguigu.juc;
    
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    /*
     * 一、用于解决多线程安全问题的方式:
     * 
     * synchronized:隐式锁
     * 1. 同步代码块
     * 
     * 2. 同步方法
     * 
     * jdk 1.5 后:
     * 3. 同步锁 Lock
     * 注意:是一个显示锁,需要通过 lock() 方法上锁,必须通过 unlock() 方法进行释放锁
     */
    public class TestLock {
        
        public static void main(String[] args) {
            Ticket ticket = new Ticket();
            
            new Thread(ticket, "1号窗口").start();
            new Thread(ticket, "2号窗口").start();
            new Thread(ticket, "3号窗口").start();
        }
    
    }
    
    class Ticket implements Runnable{
        
        private int tick = 100;
        
        private Lock lock = new ReentrantLock();
    
        @Override
        public void run() {
            while(true){
                
                lock.lock(); //上锁
                
                try{
                    if(tick > 0){
                        try {
                            Thread.sleep(200);
                        } catch (InterruptedException e) {
                        }
                        
                        System.out.println(Thread.currentThread().getName() + " 完成售票,余票为:" + --tick);
                    }
                }finally{
                    lock.unlock(); //释放锁
                }
            }
        }
        
    }
    View Code

    6.线程同步Condition

    package com.atguigu.juc;
    
    import java.util.concurrent.locks.Condition;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    /*
     * 生产者消费者案例:
     */
    public class TestProductorAndConsumerForLock {
    
        public static void main(String[] args) {
            Clerk clerk = new Clerk();
    
            Productor pro = new Productor(clerk);
            Consumer con = new Consumer(clerk);
    
            new Thread(pro, "生产者 A").start();
            new Thread(con, "消费者 B").start();
    
    //         new Thread(pro, "生产者 C").start();
    //         new Thread(con, "消费者 D").start();
        }
    
    }
    
    class Clerk {
        private int product = 0;
    
        private Lock lock = new ReentrantLock();
        private Condition condition = lock.newCondition();
    
        // 进货
        public void get() {
            lock.lock();
    
            try {
                if (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() {
            lock.lock();
    
            try {
                if (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) {
                    e.printStackTrace();
                }
    
                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();
            }
        }
    
    }
    View Code

    7.线程按顺序交替

    package com.atguigu.juc;
    
    import java.util.concurrent.locks.Condition;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    /*
     * 编写一个程序,开启 3 个线程,这三个线程的 ID 分别为 A、B、C,每个线程将自己的 ID 在屏幕上打印 10 遍,要求输出的结果必须按顺序显示。
     *    如:ABCABCABC…… 依次递归
     */
    public class TestABCAlternate {
        
        public static void main(String[] args) {
            AlternateDemo ad = new AlternateDemo();
            
            new Thread(new Runnable() {
                @Override
                public void run() {
                    
                    for (int i = 1; i <= 20; i++) {
                        ad.loopA(i);
                    }
                    
                }
            }, "A").start();
            
            new Thread(new Runnable() {
                @Override
                public void run() {
                    
                    for (int i = 1; i <= 20; i++) {
                        ad.loopB(i);
                    }
                    
                }
            }, "B").start();
            
            new Thread(new Runnable() {
                @Override
                public void run() {
                    
                    for (int i = 1; i <= 20; i++) {
                        ad.loopC(i);
                        
                        System.out.println("-----------------------------------");
                    }
                    
                }
            }, "C").start();
        }
    
    }
    
    class AlternateDemo{
        
        private int number = 1; //当前正在执行线程的标记
        
        private Lock lock = new ReentrantLock();
        private Condition condition1 = lock.newCondition();
        private Condition condition2 = lock.newCondition();
        private Condition condition3 = lock.newCondition();
        
        /**
         * @param totalLoop : 循环第几轮
         */
        public void loopA(int totalLoop){
            lock.lock();
            
            try {
                //1. 判断
                if(number != 1){
                    condition1.await();
                }
                
                //2. 打印
                for (int i = 1; i <= 1; i++) {
                    System.out.println(Thread.currentThread().getName() + "	" + i + "	" + totalLoop);
                }
                
                //3. 唤醒
                number = 2;
                condition2.signal();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
        
        public void loopB(int totalLoop){
            lock.lock();
            
            try {
                //1. 判断
                if(number != 2){
                    condition2.await();
                }
                
                //2. 打印
                for (int i = 1; i <= 1; i++) {
                    System.out.println(Thread.currentThread().getName() + "	" + i + "	" + totalLoop);
                }
                
                //3. 唤醒
                number = 3;
                condition3.signal();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
        
        public void loopC(int totalLoop){
            lock.lock();
            
            try {
                //1. 判断
                if(number != 3){
                    condition3.await();
                }
                
                //2. 打印
                for (int i = 1; i <= 1; i++) {
                    System.out.println(Thread.currentThread().getName() + "	" + i + "	" + totalLoop);
                }
                
                //3. 唤醒
                number = 1;
                condition1.signal();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
        
    }
    View Code

    8.ThreadReadWriteLock读写锁

    属于乐观锁,因为使用Lock那么不管是读还是写都上锁,这样会影响效率.使用读写锁那么读读(没有写)的时候不需要上锁,写的时候要上锁,这样效率会高些.即只有读的时候可以多线程,写的时候需要单线程.

    package com.atguigu.juc;
    
    import java.util.concurrent.locks.ReadWriteLock;
    import java.util.concurrent.locks.ReentrantReadWriteLock;
    
    /*
     * 1. ReadWriteLock : 读写锁
     * 
     * 写写/读写 需要“互斥”
     * 读读 不需要互斥
     * 
     */
    public class TestReadWriteLock {
    
        public static void main(String[] args) {
            ReadWriteLockDemo rw = new ReadWriteLockDemo();
            
            new Thread(new Runnable() {
                
                @Override
                public void run() {
                    rw.set((int)(Math.random() * 101));
                }
            }, "Write:").start();
            
            
            for (int i = 0; i < 100; i++) {
                new Thread(new Runnable() {
                    
                    @Override
                    public void run() {
                        rw.get();
                    }
                }).start();
            }
        }
        
    }
    
    class ReadWriteLockDemo{
        
        private int number = 0;
        
        private ReadWriteLock lock = new ReentrantReadWriteLock();
        
        //
        public void get(){
            lock.readLock().lock(); //上锁
            
            try{
                System.out.println(Thread.currentThread().getName() + " : " + number);
            }finally{
                lock.readLock().unlock(); //释放锁
            }
        }
        
        //
        public void set(int number){
            lock.writeLock().lock();
            
            try{
                System.out.println(Thread.currentThread().getName());
                this.number = number;
            }finally{
                lock.writeLock().unlock();
            }
        }
    }
    View Code

    9.线程八锁

    package com.atguigu.juc;
    
    /*
     * 题目:判断打印的 "one" or "two" ?
     * 
     * 1. 两个普通同步方法,两个线程,标准打印, 打印? //one  two
     * 2. 新增 Thread.sleep() 给 getOne() ,打印? //one  two
     * 3. 新增普通方法 getThree() , 打印? //three  one   two
     * 4. 两个普通同步方法,两个 Number 对象,打印?  //two  one
     * 5. 修改 getOne() 为静态同步方法,打印?  //two   one
     * 6. 修改两个方法均为静态同步方法,一个 Number 对象?  //one   two
     * 7. 一个静态同步方法,一个非静态同步方法,两个 Number 对象?  //two  one
     * 8. 两个静态同步方法,两个 Number 对象?   //one  two
     * 
     * 线程八锁的关键:
     * ①非静态方法的锁默认为  this,  静态方法的锁为 对应的 Class 实例
     * ②某一个时刻内,只能有一个线程持有锁,无论几个方法。
     */
    public class TestThread8Monitor {
        
        public static void main(String[] args) {
            Number number = new Number();
            Number number2 = new Number();
            
            new Thread(new Runnable() {
                @Override
                public void run() {
                    number.getOne();
                } 
            }).start();
            
            new Thread(new Runnable() {
                @Override
                public void run() {
    //                number.getTwo();
                    number2.getTwo();
                }
            }).start();
            
            /*new Thread(new Runnable() {
                @Override
                public void run() {
                    number.getThree();
                }
            }).start();*/
            
        }
    
    }
    
    class Number{
        
        public static synchronized void getOne(){//Number.class
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
            }
            
            System.out.println("one");
        }
        
        public synchronized void getTwo(){//this
            System.out.println("two");
        }
        
        public void getThree(){
            System.out.println("three");
        }
        
    }
    View Code

    10.线程池

    1.线程池与callable使用:获取每个线程的执行结果

    package com.atguigu.juc;
    
    import java.util.ArrayList;
    import java.util.List;
    import java.util.concurrent.Callable;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    import java.util.concurrent.Future;
    
    /*
     * 一、线程池:提供了一个线程队列,队列中保存着所有等待状态的线程。避免了创建与销毁额外开销,提高了响应的速度。
     * 
     * 二、线程池的体系结构:
     *     java.util.concurrent.Executor : 负责线程的使用与调度的根接口
     *         |--**ExecutorService 子接口: 线程池的主要接口
     *             |--ThreadPoolExecutor 线程池的实现类
     *             |--ScheduledExecutorService 子接口:负责线程的调度
     *                 |--ScheduledThreadPoolExecutor :继承 ThreadPoolExecutor, 实现 ScheduledExecutorService
     * 
     * 三、工具类 : Executors 
     * ExecutorService newFixedThreadPool() : 创建固定大小的线程池
     * ExecutorService newCachedThreadPool() : 缓存线程池,线程池的数量不固定,可以根据需求自动的更改数量。
     * ExecutorService newSingleThreadExecutor() : 创建单个线程池。线程池中只有一个线程
     * 
     * ScheduledExecutorService newScheduledThreadPool() : 创建固定大小的线程,可以延迟或定时的执行任务。
     */
    public class TestThreadPool {
        
        public static void main(String[] args) throws Exception {
            //1. 创建线程池
            ExecutorService pool = Executors.newFixedThreadPool(5);
            
            List<Future<Integer>> list = new ArrayList<>();
            
            for (int i = 0; i < 10; i++) {
                Future<Integer> future = pool.submit(new Callable<Integer>(){
    
                    @Override
                    public Integer call() throws Exception {
                        int sum = 0;
                        
                        for (int i = 0; i <= 100; i++) {
                            sum += i;
                        }
                        
                        return sum;
                    }
                    
                });
    
                list.add(future);
            }
            
            pool.shutdown();
            
            for (Future<Integer> future : list) {
                System.out.println(future.get());
            }
            
            
            
            /*ThreadPoolDemo tpd = new ThreadPoolDemo();
            
            //2. 为线程池中的线程分配任务
            for (int i = 0; i < 10; i++) {
                pool.submit(tpd);
            }
            
            //3. 关闭线程池
            pool.shutdown();*/
        }
        
    //    new Thread(tpd).start();
    //    new Thread(tpd).start();
    
    }
    
    class ThreadPoolDemo implements Runnable{
    
        private int i = 0;
        
        @Override
        public void run() {
            while(i <= 100){
                System.out.println(Thread.currentThread().getName() + " : " + i++);
            }
        }
        
    }
    View Code

    2.线程池调度

    package com.atguigu.juc;
    
    import java.util.Random;
    import java.util.concurrent.Callable;
    import java.util.concurrent.Executors;
    import java.util.concurrent.Future;
    import java.util.concurrent.ScheduledExecutorService;
    import java.util.concurrent.TimeUnit;
    
    /*
     * 一、线程池:提供了一个线程队列,队列中保存着所有等待状态的线程。避免了创建与销毁额外开销,提高了响应的速度。
     * 
     * 二、线程池的体系结构:
     *     java.util.concurrent.Executor : 负责线程的使用与调度的根接口
     *         |--**ExecutorService 子接口: 线程池的主要接口
     *             |--ThreadPoolExecutor 线程池的实现类
     *             |--ScheduledExecutorService 子接口:负责线程的调度
     *                 |--ScheduledThreadPoolExecutor :继承 ThreadPoolExecutor, 实现 ScheduledExecutorService
     * 
     * 三、工具类 : Executors 
     * ExecutorService newFixedThreadPool() : 创建固定大小的线程池
     * ExecutorService newCachedThreadPool() : 缓存线程池,线程池的数量不固定,可以根据需求自动的更改数量。
     * ExecutorService newSingleThreadExecutor() : 创建单个线程池。线程池中只有一个线程
     * 
     * ScheduledExecutorService newScheduledThreadPool() : 创建固定大小的线程,可以延迟或定时的执行任务。
     */
    public class TestScheduledThreadPool {
    
        public static void main(String[] args) throws Exception {
            ScheduledExecutorService pool = Executors.newScheduledThreadPool(5);
            
            for (int i = 0; i < 5; i++) {
                Future<Integer> result = pool.schedule(new Callable<Integer>(){
    
                    @Override
                    public Integer call() throws Exception {
                        int num = new Random().nextInt(100);//生成随机数
                        System.out.println(Thread.currentThread().getName() + " : " + num);
                        return num;
                    }
                    
                }, 1, TimeUnit.SECONDS);
                
                System.out.println(result.get());
            }
            
            pool.shutdown();
        }
        
    }
    View Code

    11.ForkJoinPool分支合并框架

    多线程问题: 当启动多线程时,比如四个线程,那么有两个顺利执行完成,另外两个还在阻塞,那么就会出现两个繁忙,两个很闲的情况.

  • 相关阅读:
    Java不带.classpath的svn项目下载,转成到eclipse中
    eclipse 实用快捷键(最全)
    加密算法IV的作用
    Hadoop环境常用命令
    Centos网络配置
    apache指定的网络名不再可用
    Toritoisegit记住用户名密码
    用JavaScript修改CSS属性的代码
    div 旋转
    过滤器、监听器、拦截器的区别
  • 原文地址:https://www.cnblogs.com/zhulibin2012/p/10464107.html
Copyright © 2011-2022 走看看