zoukankan      html  css  js  c++  java
  • 线程同步的几种实现方案

    当多个线程对同一数据进行访问时,容易出现线程安全问题,这个时候就需要让线程同步来保证数据的安全。线程同步就是说在两个或两个以上的线程访问同一资源的时候,需要用到某种方式来保证资源在某一时刻只能被一个线程访问

    线程同步的实现方案:

    一、同步代码块:synchronized(同步监视器)

      1、认识同步监视器(锁子

        synchronized(同步监视器){}

        1)必须是引用数据类型,不能是基本数据类型

        2)在同步代码块中可以改变同步监视器对象的值,不能改变其引用

        3)尽量不要使用String和包装类Integer做同步监视器,如果要使用,则必须保证代码快啊中不对其做任何操作

        4)一般使用共享资源做同步器

        5)可以创建一个专门的同步监视器,没有任何含义

        6)建议使用final来修饰同步监视器

      2、同步代码块的执行过程

        1)第一个线程来到同步代码块,发现同步监视器是open状态,需要close,然后执行其中的代码

        2)第一个线程执行过程中,发生了线程切换(阻塞 就绪),第一个线程失去了CPU,但是没有开锁

        3)第二个线程获取了CPU,来到同步代码块,发现同步监视器close状态,无法执行其中的代码,第二个也进入了阻塞状态

        4)第一个线程再次获得CPU,执行后续代码,执行完毕释放锁

        5)第二个线程再次获得CPU,来到同步代码块发现是开锁状态,重复第一个线程的处理过程 

      3、下面的代码是用同步代码块来实现线程同步(多个窗口实现安全售票)

     

    public class TiketsTest {
        public static void main(String[] args) {
            for(int i = 0;i<5;i++){//运用循环来开启五个线程(模拟五个售票员)
                new Thread(new TiketsRunnable(),"售票员"+(i+1)).start();//此处为了方便直接使用匿名对象
            }       
    }
    
    public class TiketsRunnable implements  Runnable {
        private int tikets = 100;//要卖票的总数
        private Object obj = new Object();
        @Override
        public void run() {
            while (true){
              synchronized (obj) { 
                  try {
                      Thread.sleep(10);
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
                  if (tikets <= 0) {
                      break;
                  }
                  System.out.println(Thread.currentThread().getName() + "卖了第" + tikets-- + "票");
              }
            }
        }
    }

    二、同步方法:修饰符 synchronized 返回值类型 方法名(参数){}

      1、不要将run()定义为同步方法

      2、同步方法的同步监视器是this

      3、同步代码块的效率要高于同步方法

        1)同步方法的锁是this,一旦锁住一个方法,就锁住了所有的同步方法;同步代码块只是锁住了使用该同步代码块,而没有锁住使用其他监视器的代码块

        2)同步方法是将线程锁在了方法的外部,而同步代码块将线程锁在了代码块的外部,但是却是方法的内部

      4、下面的代码是用同步方法来实现线程同步(多个窗口实现安全售票)

    public class TiketsTest {
        public static void main(String[] args) {
            for(int i = 0;i<5;i++){//运用循环来开启五个线程(模拟五个售票员)
                new Thread(new TiketsRunnable(),"售票员"+(i+1)).start();//此处为了方便直接使用匿名对象
            }
        }
    }
    
    public class TiketsRunnable implements  Runnable {
        private int tikets = 3;
        private Object obj = new Object();
        @Override
        public void run() {
            while (true) {
                sell();
                if (tikets <= 0) {
                    break;
                }
            }
        }
        public  synchronized  void sell(){//同步方法
            if(tikets<=0){
                return;
            }
            try {
                Thread.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "卖了第" + tikets+ "票");
            tikets --;
        }
    }

    三、Lock锁

      1、Lock锁

        1)JDK1.5后新增功能,与采用synchronized想比,lock锁可提供多种锁方案,更灵活

        2)java.util.concurrent.lock 中的 Lock 框架是锁定的一个抽象,它允许把锁定的实现作为 Java 类,而不是作为语言的特性来实现。这就为 Lock 的多种实现留下了空间,各种实现可能有不同的调度算

        法、性能特性或者锁定语义。

        3)ReentrantLock 类实现了 Lock ,它拥有与 synchronized 相同的并发性和内存语义,  但是添加了类似锁投票、定时锁等候和可中断锁等候的一些特性。此外,它还提供了在激烈争用情况下更佳的

        性能。

        注意:如果同步代码有异常,要将unlock()写入finally语句块中,确保关锁 

      2、Lock和synchronized的区别

        1)Lock是显示锁(需要手动开锁、关锁,不要忘记关锁),synchronized是隐式锁,遇到异常自动解锁

        2)Lock锁只有代码块锁,synchronized有代码块锁和方法锁

        3)使用Lock锁,JVM将花费较少的时间来调度线程,性能更好。并且具有更好的扩展性(提供更多的子类) 

      3、下面的代码是用Lock锁来实现线程同步(多个窗口实现安全售票)

    public class TiketsTest {
        public static void main(String[] args) {
            for(int i = 0;i<5;i++){//运用循环来开启五个线程(模拟五个售票员)
                new Thread(new TiketsRunnable(),"售票员"+(i+1)).start();//此处为了方便直接使用匿名对象
            }  
        }
    }
    
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    public class TiketsRunnable implements  Runnable {
        private int tikets = 100;
        Lock lock = new ReentrantLock();
        @Override
        public void run() {
            while (true) {
                lock.lock();//开
                try {
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    if (tikets <= 0) {
                        break;
                    }
                    System.out.println(Thread.currentThread().getName() + "卖了第" + tikets-- + "票");
                }finally {
                    lock.unlock();//放在finally语句块中确保关锁
                }
            }
        }
    }

    四、三种锁的优先使用顺序

      Lock锁 —— 同步代码块(已经进入了方法体,分配了相应的资源)—— 同步方法(在方法体之外)

  • 相关阅读:
    安装HDP时的报错信息
    K-近邻(KNN)算法
    linux复杂命令
    azkaban报错记录
    azkaban的安装部署
    安装centOS后要解决的问题
    AI之微信跳一跳
    Python的lambda
    关于在vim中的查找和替换
    cdh6.3.2 hue集成hbase
  • 原文地址:https://www.cnblogs.com/huxiaoyang/p/11924018.html
Copyright © 2011-2022 走看看