zoukankan      html  css  js  c++  java
  • Java高并发之同步异步

    1、概念理解:

    2、同步的解决方案:

    1).基于代码

    synchronized 关键字

      修饰普通方法:作用于当前实例加锁,进入同步代码前要获得当前实例的锁。

      修饰静态方法:作用于当前类对象加锁,进入同步代码前要获得当前类对象的锁。

      修饰代码块:指定加锁对象,对给定对象加锁,进入同步代码块前要获得给定对象的锁。

     code1

    package com.thread;
    
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    /**
     * 同步方法
     * @author Administrator
     *
     */
    public class SynchronizedMethod implements Runnable{
    
        //静态共享变量 i
        static int i = 0;
        
        /**
         * 自增
         */
        public synchronized void increase(){
            i++;
        }
        
        @Override
        public void run() {
            for (int j = 0; j < 100; j++) {
                increase();
            }
        }
        
        public static void main(String[] args) throws InterruptedException {
            SynchronizedMethod instance = new SynchronizedMethod();
            ExecutorService executorService = Executors.newFixedThreadPool(2);
            for (int i = 0; i < 3; i++) {
                //同一实例,线程共享静态变量i
    //            executorService.execute(instance);
                //不同实例,线程单独享有变量i,达不到同步目的
                executorService.execute(new SynchronizedMethod());
                /**
                 * 由于线程执行时间过短,在不同实例下,可能会得到类似于同步的结果。
                 */
                Thread.sleep(100);
            }
            
            executorService.shutdown();
            
            System.out.println(i);  //300
            
        }
    }
    View Code

    code2

    package com.thread;
    
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    /**
     * 同步代码块
     * @author Administrator
     *
     */
    public class SynchronizedCodeBlock implements Runnable{
    
        //静态共享变量 i
        static int i = 0;
        
        @Override
        public void run() {
            //同步进来的对象
            synchronized(this){  //SynchronizedCodeBlock.class
                for (int j = 0; j < 100; j++) {
                    i++;
                }
            }
        }
        
        public static void main(String[] args) throws InterruptedException {
            SynchronizedCodeBlock instance = new SynchronizedCodeBlock();
            ExecutorService executorService = Executors.newFixedThreadPool(2);
            for (int i = 0; i < 3; i++) {
    //            executorService.execute(instance);
                executorService.execute(new SynchronizedCodeBlock());
                Thread.sleep(10);
            }
            
            executorService.shutdown();
            
            System.out.println(i);  //300
            
        }
    }
    View Code

    wait与notify运用

    wait():使一个线程处于等待状态,并且释放所持有的对象的lock。

    sleep():使一个正在运行的线程处于睡眠状态,是一个静态方法,调用此方法要捕捉InterruptedException异常。

    notify():唤醒一个处于等待状态的线程,注意的是在调用此方法的时候,并不能确切的唤醒某一个等待状态的线程,而是由JVM确定唤醒哪个线程,而且不是按优先级。

    notifyAll():唤醒所有处入等待状态的线程,注意并不是给所有唤醒线程一个对象的锁,而是让它们竞争。

    code3

    package com.test;
    
    /**
     * 多线程实现 生产者-消费者模式
     *     关键点:wait和notifyAll或者notify时机的运用,确保先生产后消费
     * @author Administrator
     *
     */
    public class Test
    {
        private static Integer count = 0;  //数据仓库计数
        private final Integer FULL = 5;  //数据仓库最大存储量
        private static String lock = "lock";  //锁标识
    
        public static void main(String[] args) 
        {
            Test t = new Test();
            new Thread(t.new Producer()).start();
            new Thread(t.new Consumer()).start();
            new Thread(t.new Producer()).start();
            new Thread(t.new Consumer()).start();
        }
        
        //生产者
        class Producer implements Runnable
        {
            @Override
            public void run()
            {
                for (int i = 0; i < 5; i++)
                {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e1) {
                        e1.printStackTrace();
                    }
                    synchronized (lock)
                    {
                        while (count == FULL)
                        {
                            try {
                                lock.wait();
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                        count++;
                        System.out.println(Thread.currentThread().getName() + "produce:: " + count);
                        //唤醒lock锁上的所有线程
                        lock.notifyAll();
                    }
                }
            }
        }
        
        //消费者
        class Consumer implements Runnable
        {
            @Override
            public void run()
            {
                for (int i = 0; i < 5; i++)
                {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e1) {
                        e1.printStackTrace();
                    }
                    synchronized (lock)
                    {
                        //如果首次消费者竞争得到锁,进入后等待
                        while (count == 0)
                        {
                            try {
                                lock.wait();
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                        count--;
                        System.out.println(Thread.currentThread().getName()+ "consume:: " + count);
                        lock.notifyAll();
                    }
                }
            }
        }
    }
    View Code

    volatile实现线程同步

    原理:volatile保证不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,新值对其他线程来说是立即可见的,并且禁止进行指令重排序。

    注意:volatile不保证原子性,凡是不是原子性的操作,都不能保证可见性,也即不能保证同步

    应用:

      1)对变量的写操作不依赖于当前值 类似 i++、i=j 等操作 不能对 i 用volatile。解决办法:类似操作增加 synchronized、Lock、AtomicInteger 保证原子性。

      2)该变量没有包含在具有其他变量的不变式中 

      常用在多线程状态标志 flag、

    ReentrantLock重入锁

    重入锁:外层函数获取锁后,内层函数依然有获取该锁的代码,则重入锁无需再次获取锁,即可进入内层代码

    code1

    package com.lock;
    
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    import java.util.concurrent.locks.ReentrantLock;
    
    /**
     * 重入锁
     *     外层函数获取锁后,内层函数依然有获取该锁的代码,则重入锁无需再次获取锁,即可进入内层代码
     *     ReentrantLock 和synchronized 都是 可重入锁
     * @author Administrator
     *
     */
    public class ReentranLockTest implements Runnable{
    
        public static ReentrantLock lock = new ReentrantLock();
        public static int i = 0;
        
        @Override
        public void run() {
            for (int j = 0; j < 10; j++) {
                lock.lock();  //加锁
                try {
                    i++;
                } finally {
                    lock.unlock();  //释放锁
                }
            }
        }
    
        public static void main(String[] args) throws InterruptedException {
            ReentranLockTest test = new ReentranLockTest();
            
            ExecutorService executorService = Executors.newFixedThreadPool(2);
            for (int i = 0; i < 2; i++) {
                executorService.execute(test);
                Thread.sleep(1000);
            }
            executorService.shutdown();
            System.out.println(i);
            
        }
    }
    View Code

    code2

    package com.lock;
    
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    /**
     * 可中断的重入锁
     *     条件中断等待
     * @author Administrator
     *
     */
    public class InterruptiblyLockTest {
    
        public static Lock lock = new ReentrantLock();
        
        public void function(){
            String tName = Thread.currentThread().getName();
            try {
                System.out.println(tName + "-开始获取锁......");
                lock.lockInterruptibly();
                System.out.println("获取到锁了......");
                Thread.sleep(10000);
                System.out.println("睡眠10秒后,开始干活......");
                for (int i = 0; i < 5; i++) {
                    System.out.println(tName + ":" + i);
                }
                System.out.println("活干完了......");
            } catch (Exception e) {
                System.out.println(tName + "-我好像被人中断了!");
                e.printStackTrace();
            }finally {
                lock.unlock();
                System.out.println(tName + "-释放了锁");
            }
        }
    
        public static void main(String[] args) throws InterruptedException {
            InterruptiblyLockTest test = new InterruptiblyLockTest();
            //定义两个线程
            Thread t0 = new Thread() {
                public void run() {
                    test.function();
                }
            };
            
            Thread t1 = new Thread() {
                public void run() {
                    test.function();
                }
            };
            
            String tName = Thread.currentThread().getName();
            System.out.println(tName + "-启动t0");
            t0.start();
            System.out.println(tName + "-等5秒,再启动t1");
            Thread.sleep(5000);
            System.out.println(tName + "-启动t1");
            t1.start();
            //t0先占据了锁还在睡眠
            System.out.println(tName + "-不等了,把t1中断掉!");
            t1.interrupt();  //中断:只能中断处于等待锁的线程,不能中断已经获取锁的线程
        }
    }
    View Code

    code3

    package com.lock;
    
    import java.text.SimpleDateFormat;
    import java.util.Date;
    import java.util.concurrent.TimeUnit;
    import java.util.concurrent.locks.ReentrantLock;
    
    /**
     * 尝试型锁
     *     拒绝阻塞
     * @author Administrator
     *
     */
    public class TryLockTest {
    
        public ReentrantLock lock = new ReentrantLock();
        
        /**
         * tryLock
         *     当前资源没有被占用,则tryLock获取锁
         *  当前资源被当前锁占用,则tryLock返回true
         *  当前资源被其他线程占用,则tryLock返回false
         * @throws InterruptedException
         */
        public void tryLockFunction() throws InterruptedException{
            String tName = Thread.currentThread().getName();
            if(lock.tryLock()){
                try {
                    System.out.println(tName + "-获取到锁了");
                    Thread.sleep(3000);
                    System.out.println(tName + "工作了3秒钟......");
                } finally {
                    lock.unlock();
                    System.out.println(tName + "-释放锁");
                }
            }else{
                System.out.println(tName + "-无法获取到锁");
            }
        }
        
        /**
         * tryLock(long timeout, TimeUnit unit)
         *     timeout时间内尝试请求锁,请求到了则返回true,可被中断
         *     
         * @throws InterruptedException
         */
        public void tryLockInterruptFunction() throws InterruptedException{
            String tName = Thread.currentThread().getName();
            SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd:hh:mm:ss");
            System.out.println(tName + "-开始尝试获取锁,当前时间:"+format.format(new Date()));
            if(lock.tryLock(3,TimeUnit.SECONDS)){
                try {
                    System.out.println(tName + "-获取到锁了");
                    Thread.sleep(5000);
                    System.out.println(tName + "工作了3秒钟......");
                } finally {
                    lock.unlock();
                    System.out.println(tName + "-释放锁");
                }
            }else{
                System.out.println(tName + "-无法获取到锁");
                System.out.println(tName + "-结束尝试获取锁,当前时间:"+format.format(new Date()));
            }
        }
        
        public static void main(String[] args) {
            TryLockTest test = new TryLockTest();
            new Thread("Lock-Thread1") {
                public void run() {
                    try {
                        test.tryLockFunction();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }.start();
            new Thread("Lock-Thread2") {
                public void run() {
                    try {
                        test.tryLockFunction();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }.start();
            
            //Lock-Thread1-获取到锁了
            //Lock-Thread2-无法获取到锁
            //Lock-Thread1工作了3秒钟......
            //Lock-Thread1-释放锁
            
            new Thread("LockInterrupt-Thread1") {
                public void run() {
                    try {
                        test.tryLockInterruptFunction();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }.start();
            new Thread("LockInterrupt-Thread2") {
                public void run() {
                    try {
                        test.tryLockInterruptFunction();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }.start();
            
            //LockInterrupt-Thread2-开始尝试获取锁,当前时间:2019-01-17:05:12:32
            //LockInterrupt-Thread1-开始尝试获取锁,当前时间:2019-01-17:05:12:32
            //LockInterrupt-Thread2-获取到锁了
            //LockInterrupt-Thread1-无法获取到锁
            //LockInterrupt-Thread1-结束尝试获取锁,当前时间:2019-01-17:05:12:35
            //LockInterrupt-Thread2工作了3秒钟......
            //LockInterrupt-Thread2-释放锁
    
        }
    }
    View Code

    code4

    package com.lock;
    
    import java.util.concurrent.locks.ReentrantLock;
    
    /**
     * 公平锁
     *     按时间先后获取锁
     * @author Administrator
     *
     */
    public class FairLockTest {
        //创建一个公平锁
        public static ReentrantLock lock = new ReentrantLock(true);
    
        public void fairLockFunction(){
            while(true){
                try {
                    lock.lock();
                    System.out.println(Thread.currentThread().getName()+"获取到锁......");
                } finally {
                    lock.unlock();
                }
            }
        }
        
        public static void main(String[] args) {
            FairLockTest test = new FairLockTest();
            Thread t1 = new Thread("线程1") {
                public void run() {
                    test.fairLockFunction();
                }
            };
            Thread t2 = new Thread("线程2") {
                public void run() {
                    test.fairLockFunction();
                }
            };
            t1.start();
            t2.start();
        }
        
        //线程1获取到锁......
        //线程2获取到锁......
        //线程1获取到锁......
        //线程2获取到锁......
        //线程1获取到锁......
        //线程2获取到锁......
        //线程1获取到锁......
        //线程2获取到锁......
    }
    View Code

    ThreadLocal创建线程间变量副本 

    参考博客:聊一聊Spring中的线程安全性

     final实现volatile可见性

      通过final不可变性的特点,替代volatile的可见性。

     参考博客:

    https://blog.csdn.net/javazejian/article/details/72828483

    https://www.cnblogs.com/duanxz/p/3709608.html?utm_source=tuicool&utm_medium=referral

    http://www.cnblogs.com/duanxz/p/5066726.html

    2).基于数据库

  • 相关阅读:
    预备作业03
    2017-2018-2《程序设计与数据结构》实验2报告
    2017-2018-2 《程序设计与数据结构》第7周学习总结
    2017-2018-2 《程序设计与数据结构》第六周学习总结
    20172312 《程序设计与数据结构》第五周学习总结
    20172312 《程序设计与数据结构》第四周学习总结
    20172312 《程序设计与数据结构》第三周学习总结
    20172312 实验一《Java开发环境的熟悉》实验报告
    第二周作业
    第一周作业
  • 原文地址:https://www.cnblogs.com/x-jingxin/p/10250482.html
Copyright © 2011-2022 走看看