Java线程同步的四种方式详解(建议收藏)

 Java线程同步的四种方式详解(建议收藏)

Java线程同步属于Java多线程与并发编程的核心点,需要重点掌握,下面我就来详解Java线程同步的4种主要的实现方式@mikechen

什么是线程同步

当使用多个线程来访问同一个数据时,将会导致数据不准确,相互之间产生冲突,非常容易出现线程安全问题,如下图所示:

Java线程同步的四种方式详解(建议收藏)

比如多个线程都在操作同一数据,都打算修改商品库存,这样就会导致数据不一致的问题。

线程同步的真实意思,其实是“排队”:几个线程之间要排队,一个一个对共享资源进行操作,而不是同时进行操作。

所以我们用同步机制来解决这些问题,加入同步锁以避免在该线程没有完成操作之前,被其他线程的调用,从而保证了该变量的唯一性和准确性。

 

线程同步的几种方式

Java线程同步的四种方式详解(建议收藏)

1、使用synchronized关键字

这种方式比较灵活,修饰一个代码块,被修饰的代码块称为同步语句块。

其作用的范围是大括号{}括起来的代码,作用的对象是调用这个代码块的对象,如下格式:

synchronized(对象) {                        //得到对象的锁,才能操作同步代码     需要被同步代码; }

Java线程同步的四种方式详解(建议收藏)Java线程同步的四种方式详解(建议收藏)

通常没有必要同步整个方法,使用synchronized代码块同步关键代码即可。

具体的示例如下:

public class SynchronizedThread {       class Bank {           private int account = 200;           public int getAccount() {             return account;         }           /**          * 用同步方法实现          *          * @param money          */         public synchronized void save(int money) {             account += money;         }           /**          * 用同步代码块实现          *          * @param money          */         public void save1(int money) {             synchronized (this) {                 account += money;             }         }     }       class NewThread implements Runnable {         private Bank bank;           public NewThread(Bank bank) {             this.bank = bank;         }           @Override         public void run() {             for (int i = 0; i < 10; i++) {                 // bank.save1(10);                 bank.save(10);                 System.out.println(i + "账户余额为:" + bank.getAccount());             }         }       }       /**      * 建立线程,调用内部类      */     public void useThread() {         Bank bank = new Bank();         NewThread new_thread = new NewThread(bank);         System.out.println("线程1");         Thread thread1 = new Thread(new_thread);         thread1.start();         System.out.println("线程2");         Thread thread2 = new Thread(new_thread);         thread2.start();     }       public static void main(String[] args) {         SynchronizedThread st = new SynchronizedThread();         st.useThread();     }   }

 

Java线程同步的四种方式详解(建议收藏)

2.使用ReentrantLock

ReentrantLock类是可重入、互斥、实现了Lock接口的锁,它与使用synchronized方法具有相同的基本行为和语义,并且扩展了其能力。

Java线程同步的四种方式详解(建议收藏)

private int account = 100;             //需要声明这个锁             private Lock lock = new ReentrantLock();             public int getAccount() {                 return account;             }             //这里不再需要synchronized              public void save(int money) {                 lock.lock();                 try{                     account += money;                 }finally{                     lock.unlock();                 }               }         }

 

synchronized 与 Lock 的对比

ReentrantLock是显示锁,手动开启和关闭锁,别忘记关闭锁;

synchronized 是隐式锁,出了作用域自动释放;

ReentrantLock只有代码块锁,synchronized 有代码块锁和方法锁;

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

优先使用顺序:

ReentrantLocksynchronized 同步代码块> synchronized 同步方法

 

3.使用原子变量实现线程同步

为了完成线程同步,我们将使用原子变量(Atomic***开头的)来实现。

比如典型代表:AtomicInteger类存在于java.util.concurrent.atomic中,该类表示支持原子操作的整数,采用getAndIncrement方法以原子方法将当前的值递加。

具体示例如下:

private AtomicInteger account = new AtomicInteger(100);           public AtomicInteger getAccount() {             return account;         }           public void save(int money) {             account.addAndGet(money);         }

Java线程同步的四种方式详解(建议收藏)Java线程同步的四种方式详解(建议收藏)

4.ThreadLocal实现线程同步

如果使用ThreadLocal管理变量,则每一个使用该变量的线程都获得该变量的副本,副本之间相互独立,这样每一个线程都可以随意修改自己的变量副本,而不会对其他线程产生影响,从而实现线程同步。

具体代码示例如下:

//只改Bank类,其余代码与上同         public class Bank{             // 创建一个线程本地变量 ThreadLocal             private static ThreadLocal<Integer> account = new ThreadLocal<Integer>(){                 @Override                 //返回当前线程的"初始值"                  protected Integer initialValue(){                     return 100;                 }             };             public void save(int money){                 //设置线程副本中的值                 account.set(account.get()+money);             }             public int getAccount(){                 //返回线程副本中的值                  return account.get();             }         }

 

Java线程同步的四种方式详解(建议收藏)Java线程同步的四种方式详解(建议收藏)

以上

作者简介

陈睿|mikechen,10年+大厂架构经验,《BAT架构技术500期》系列文章作者,专注于互联网架构技术。

阅读mikechen的互联网架构更多技术文章合集

Java并发|JVM|MySQL|Spring|Redis|分布式|高并发

发表评论

相关文章

当前内容话题
  • 0