线程同步的几种实现方案

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

线程同步的实现方案:

一、同步代码块: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锁 —— 同步代码块(已经进入了方法体,分配了相应的资源)—— 同步方法(在方法体之外)

猜你喜欢

转载自www.cnblogs.com/huxiaoyang/p/11924018.html