diff --git a/Reconstruction/eflowRec/eflowRec/eflowCellIntegrator.h b/Reconstruction/eflowRec/eflowRec/eflowCellIntegrator.h
index 9552c649688ca7ee6decd28e7373266ed1824184..2a6c92a1118f324b5994ca4067d88deacf9aa271 100644
--- a/Reconstruction/eflowRec/eflowRec/eflowCellIntegrator.h
+++ b/Reconstruction/eflowRec/eflowRec/eflowCellIntegrator.h
@@ -1,5 +1,5 @@
 /*
-  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+  Copyright (C) 2002-2021 CERN for the benefit of the ATLAS collaboration
 */
 
 /*
@@ -125,14 +125,14 @@ public:
 
   inline void setEtaSq(double xSq) { m_etaSq = xSq; }
 
-  inline double evaluateStdExp(double rSq) { return m_norm * exp(-rSq * m_oneOverTwoSigmaSq); }
-  inline double evaluateLookupExp(double rSq) { return m_lookupExp->evaluate(rSq * m_oneOverTwoSigmaSq)*m_norm; }
+  inline double evaluateStdExp(double rSq) const { return m_norm * exp(-rSq * m_oneOverTwoSigmaSq); }
+  inline double evaluateLookupExp(double rSq) const { return m_lookupExp->evaluate(rSq * m_oneOverTwoSigmaSq)*m_norm; }
 
   /** The evaluate method for the integration. The implementation depends on the template parameter */
   inline double evaluate(double y);
 
 private:
-  eflowLookupExp* m_lookupExp;
+  const eflowLookupExp* m_lookupExp;
   double m_oneOverTwoSigmaSq;
   double m_norm;
   double m_etaSq;
diff --git a/Reconstruction/eflowRec/eflowRec/eflowLayerIntegrator.h b/Reconstruction/eflowRec/eflowRec/eflowLayerIntegrator.h
index 0b40f27bde0bfa031ee4010784c422b4ade9cacc..3cad7d660950c6a1ae9b4eaf4cd7c2d4736cf8d9 100644
--- a/Reconstruction/eflowRec/eflowRec/eflowLayerIntegrator.h
+++ b/Reconstruction/eflowRec/eflowRec/eflowLayerIntegrator.h
@@ -1,5 +1,5 @@
 /*
-  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+  Copyright (C) 2002-2021 CERN for the benefit of the ATLAS collaboration
 */
 
 #ifndef EFLOWLAYERINTEGRATOR_H
@@ -44,8 +44,8 @@ class eflowLayerIntegrator {
 
   void measureNewClus(const xAOD::CaloCluster* clus, const eflowTrackCaloPoints& trackCalo);
   void measureNewClus(eflowTrackClusterLink* trackClusterLink);
-  void measureNewClus(std::vector<eflowRecCluster*> efRecClusters, eflowRecTrack* track);
-  void measureNewClus(std::vector<xAOD::CaloCluster*> clusVec, const eflowTrackCaloPoints& trackCalo);
+  void measureNewClus(const std::vector<eflowRecCluster*>& efRecClusters, eflowRecTrack* track);
+  void measureNewClus(const std::vector<xAOD::CaloCluster*>& clusVec, const eflowTrackCaloPoints& trackCalo);
 
   eflowFirstIntENUM getFirstIntLayer() const;
 
diff --git a/Reconstruction/eflowRec/eflowRec/eflowLookupExp.h b/Reconstruction/eflowRec/eflowRec/eflowLookupExp.h
index 9515dae2b9a63516b6ae37a5919138972ea64786..253098a40c11c9f0ee425e5fd1a874513ce9220b 100644
--- a/Reconstruction/eflowRec/eflowRec/eflowLookupExp.h
+++ b/Reconstruction/eflowRec/eflowRec/eflowLookupExp.h
@@ -1,5 +1,5 @@
 /*
-  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+  Copyright (C) 2002-2021 CERN for the benefit of the ATLAS collaboration
 */
 
 /*
@@ -21,24 +21,7 @@
 /** Lookup-table based exponential function to save CPU time, which is used by eflowCellIntegrator */
 class eflowLookupExp{
 public:
-  static eflowLookupExp* getInstance(int nExpBins = 50, int nExpSubBins = 1000){
-
-    //Creation of the unique_ptr is not thread-safe - therefore we get a mutex and pass it to a lock-guard
-    //The lock is released automatically when getInstance returns
-    //The mutex is static so that all threads check the status of the *same* mutex
-    static std::mutex mutex_instance;
-    std::lock_guard<std::mutex> lock(mutex_instance);
-
-    if ( !m_instance) {      
-      m_instance = std::make_unique<eflowLookupExp>(nExpBins, nExpSubBins);
-    } else {
-      /* Make sure the requested bin numbers are consistent with the existing instance */
-      if ( (m_instance->m_nExpBins != nExpBins) || (m_instance->m_nExpSubBins != nExpSubBins) ) {
-        throw std::runtime_error("eflowLookupExp: Instance with different bin numbers than existing requested!");
-      }
-    }
-    return m_instance.get();
-  }
+  static const eflowLookupExp* getInstance(int nExpBins = 50, int nExpSubBins = 1000);
 
   //private:
   eflowLookupExp(int nExpBins, int nExpSubBins) :
@@ -52,14 +35,11 @@ public:
     for (int iSub = 0; iSub <= nExpSubBins; ++iSub){
       m_subExp[iSub] = exp(-substep* iSub);
     }
-    //initialise to nullptr, in this explicit instance it wil never be used.
-    //Hence it will always be a nullptr
-    m_instance = nullptr;
   }
 public:
   ~eflowLookupExp(){ }
 
-  double evaluate(double x) {
+  double evaluate(double x) const {
     int iExpBin = (int) x;
     int iSubBin(((x-iExpBin)*m_nExpSubBins));
 
@@ -74,7 +54,6 @@ private:
   int m_nExpSubBins;
   std::vector<double> m_exp;
   std::vector<double> m_subExp;
-  static std::unique_ptr<eflowLookupExp> m_instance;
 };
 
 
diff --git a/Reconstruction/eflowRec/eflowRec/eflowTrackClusterLink.h b/Reconstruction/eflowRec/eflowRec/eflowTrackClusterLink.h
index 8c28ec9ec3149a301c36b0660b54b3984dd9c0e8..22d32cc1cb51bf24390e10aeac4cdaf1ccd61193 100644
--- a/Reconstruction/eflowRec/eflowRec/eflowTrackClusterLink.h
+++ b/Reconstruction/eflowRec/eflowRec/eflowTrackClusterLink.h
@@ -1,5 +1,5 @@
 /*
-  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+  Copyright (C) 2002-2021 CERN for the benefit of the ATLAS collaboration
 */
 
 /*
@@ -13,7 +13,7 @@
 #define EFLOWTRACKCLUSTERLINK_H_
 
 #include <utility>
-#include <map>
+#include <unordered_map>
 #include <vector>
 #include <iostream>
 #include <mutex>  
@@ -27,36 +27,21 @@ class eflowRecTrack;
 class eflowRecCluster;
 
 /**
- Stores pointers to an eflowRecTrack and an eflowRecCluster. These pointers are inserted in the InstanceMap stored. We also store a vector of energy density, around the track impact point in each calorimeter layer, corresponding to the layers in the calorimeter.
+ Stores pointers to an eflowRecTrack and an eflowRecCluster.
+ These pointers kept in a cache internal to the getInstance function.
+ We also store a vector of energy density, around the track impact point
+ in each calorimeter layer, corresponding to the layers in the calorimeter.
 */
 class eflowTrackClusterLink {
-private:
-  typedef std::map<std::pair<eflowRecTrack*, eflowRecCluster*>, std::unique_ptr<eflowTrackClusterLink> > InstanceMap;
-  
 public:
   eflowTrackClusterLink(eflowRecTrack* track, eflowRecCluster* cluster) :
       m_track(track), m_cluster(cluster) { }
 
   virtual ~eflowTrackClusterLink() { }
 
-  static eflowTrackClusterLink* getInstance(eflowRecTrack* track, eflowRecCluster* cluster){
-    std::pair<eflowRecTrack*, eflowRecCluster*> thisPair(std::make_pair(track, cluster));    
-
-    //Read and write from the map is not thread-safe - therefore we get a mutex and pass it to a lock-guard
-    //The lock is released automatically when getInstance returns
-    //The mutex is static so that all threads check the status of the *same* mutex
-    static std::mutex mutex_instance;
-    std::lock_guard<std::mutex> lock(mutex_instance);
-    /* The find returns a valid iterator. If there is no existing entry it returns the end iterator */
-    InstanceMap::iterator mapIterator = m_instances.find(thisPair);
-    
-    if (m_instances.end() == mapIterator){
-      /* If no existing entry we create a new unique_ptr and add an entry into the map */
-      m_instances[thisPair] = std::make_unique<eflowTrackClusterLink>(track,cluster);
-      return m_instances[thisPair].get();
-    }
-    else return (*mapIterator).second.get();
-  }
+  static eflowTrackClusterLink* getInstance(eflowRecTrack* track,
+                                            eflowRecCluster* cluster,
+                                            const EventContext& ctx = Gaudi::Hive::currentContext());
 
   eflowRecCluster* getCluster() const { return m_cluster; }
   eflowRecTrack* getTrack() const { return m_track; }
@@ -67,13 +52,26 @@ public:
   const std::vector<double>& getClusterIntegral() const { return m_clusterIntegral; }
 
 private:
+  using key_t = std::pair<eflowRecTrack*, eflowRecCluster*>;
+  struct Hasher
+  {
+    size_t operator() (const key_t& k) const
+    {
+      return std::hash<eflowRecTrack*>()(k.first) ^
+        std::hash<eflowRecCluster*>()(k.second);
+    }
+  };
+  struct Cache
+  {
+    std::mutex m_mutex;
+    std::unordered_map<key_t, std::unique_ptr<eflowTrackClusterLink>, Hasher > m_map;
+    EventContext::ContextEvt_t m_evt = static_cast<EventContext::ContextEvt_t>(-1);
+  };
+  
   eflowRecTrack* m_track;
   eflowRecCluster* m_cluster;
 
   std::vector<double> m_clusterIntegral;
-
-  static InstanceMap m_instances;
-
 };
 
 #endif /* EFLOWTRACKCLUSTERLINK_H_ */
diff --git a/Reconstruction/eflowRec/src/PFCellLevelSubtractionTool.cxx b/Reconstruction/eflowRec/src/PFCellLevelSubtractionTool.cxx
index 6461f12d191356abc2edb0bbcdc9ad74e464c396..d18a13b629e24cb98f97cfa719e6f70f8486d8a6 100644
--- a/Reconstruction/eflowRec/src/PFCellLevelSubtractionTool.cxx
+++ b/Reconstruction/eflowRec/src/PFCellLevelSubtractionTool.cxx
@@ -1,5 +1,5 @@
 /*
-  Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration
+  Copyright (C) 2002-2021 CERN for the benefit of the ATLAS collaboration
 */
 
 #include "eflowRec/PFCellLevelSubtractionTool.h"
@@ -112,6 +112,8 @@ void PFCellLevelSubtractionTool::execute(eflowCaloObjectContainer* theEflowCaloO
 unsigned int PFCellLevelSubtractionTool::matchAndCreateEflowCaloObj(unsigned int n, eflowData& data) const {
   unsigned int nMatches(0);
 
+  const EventContext& ctx = Gaudi::Hive::currentContext();
+
   /* Create eflowTrackClusterLink after matching */
   unsigned int nTrack = data.tracks->size();
   for (unsigned int iTrack=0; iTrack<nTrack; ++iTrack) {
@@ -131,7 +133,7 @@ unsigned int PFCellLevelSubtractionTool::matchAndCreateEflowCaloObj(unsigned int
     for (auto& matchpair : bestClusters_02) {
       eflowRecCluster* theCluster = matchpair.first;
       float distancesq = matchpair.second;
-      eflowTrackClusterLink* trackClusterLink = eflowTrackClusterLink::getInstance(thisEfRecTrack, theCluster);
+      eflowTrackClusterLink* trackClusterLink = eflowTrackClusterLink::getInstance(thisEfRecTrack, theCluster, ctx);
       if(distancesq<0.15*0.15) {
 	// Narrower cone is a subset of the selected clusters
 	// Distance returned is deltaR^2
@@ -147,7 +149,7 @@ unsigned int PFCellLevelSubtractionTool::matchAndCreateEflowCaloObj(unsigned int
     nMatches++;
     for (auto& matchpair : bestClusters) {
       eflowRecCluster* theCluster = matchpair.first;
-      eflowTrackClusterLink* trackClusterLink = eflowTrackClusterLink::getInstance(thisEfRecTrack, theCluster);
+      eflowTrackClusterLink* trackClusterLink = eflowTrackClusterLink::getInstance(thisEfRecTrack, theCluster, ctx);
       thisEfRecTrack->addClusterMatch(trackClusterLink);
       theCluster->addTrackMatch(trackClusterLink);
     }
diff --git a/Reconstruction/eflowRec/src/PFRecoverSplitShowersTool.cxx b/Reconstruction/eflowRec/src/PFRecoverSplitShowersTool.cxx
index 06b16f46c9774355cd3a667d461bec66565034ec..8991d32b5f540f188da66fb89b9b1d69fe15a7b6 100644
--- a/Reconstruction/eflowRec/src/PFRecoverSplitShowersTool.cxx
+++ b/Reconstruction/eflowRec/src/PFRecoverSplitShowersTool.cxx
@@ -1,5 +1,5 @@
 /*
-  Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration
+  Copyright (C) 2002-2021 CERN for the benefit of the ATLAS collaboration
 */
 
 #include "eflowRec/PFRecoverSplitShowersTool.h"
@@ -141,6 +141,9 @@ void PFRecoverSplitShowersTool::fillTracksToRecover(eflowData& data) const {
 }
 
 unsigned int PFRecoverSplitShowersTool::matchAndCreateEflowCaloObj(eflowData& data) const {
+
+  const EventContext& ctx = Gaudi::Hive::currentContext();
+
   /* Cache the original number of eflowCaloObjects */
   const unsigned int nCaloObj = data.caloObjects->size();
 
@@ -176,7 +179,7 @@ unsigned int PFRecoverSplitShowersTool::matchAndCreateEflowCaloObj(eflowData& da
       eflowRecCluster* thisEFRecCluster = trkClusLink->getCluster();
       // Look up whether this cluster is intended for recovery
       if( data.clustersToConsider.find(trkClusLink->getCluster()) == data.clustersToConsider.end() ) {continue;}
-      eflowTrackClusterLink* trackClusterLink = eflowTrackClusterLink::getInstance(thisEfRecTrack,thisEFRecCluster);
+      eflowTrackClusterLink* trackClusterLink = eflowTrackClusterLink::getInstance(thisEfRecTrack,thisEFRecCluster, ctx);
       thisEfRecTrack->addClusterMatch(trackClusterLink);
       thisEFRecCluster->addTrackMatch(trackClusterLink);
     }
diff --git a/Reconstruction/eflowRec/src/eflowLayerIntegrator.cxx b/Reconstruction/eflowRec/src/eflowLayerIntegrator.cxx
index 39f1e0d5725a6c67a86cae86ab914cb7e9753280..388b0ed874fe507b4c0973c173e9d80658f28e78 100644
--- a/Reconstruction/eflowRec/src/eflowLayerIntegrator.cxx
+++ b/Reconstruction/eflowRec/src/eflowLayerIntegrator.cxx
@@ -1,5 +1,5 @@
 /*
-  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+  Copyright (C) 2002-2021 CERN for the benefit of the ATLAS collaboration
 */
 
 /********************************************************************
@@ -129,23 +129,20 @@ void eflowLayerIntegrator::addToAllClustersIntegral(const std::vector<double>& c
   }
 }
 
-void eflowLayerIntegrator::measureNewClus(std::vector<xAOD::CaloCluster*> clusVec, const eflowTrackCaloPoints& trackCalo) {
+void eflowLayerIntegrator::measureNewClus(const std::vector<xAOD::CaloCluster*>& clusVec, const eflowTrackCaloPoints& trackCalo) {
   resetAllClustersIntegralForNewTrack(trackCalo);
 
-  std::vector<xAOD::CaloCluster*>::iterator itCluster    = clusVec.begin();
-  std::vector<xAOD::CaloCluster*>::iterator itClusterEnd = clusVec.end();
-  for (; itCluster != itClusterEnd; itCluster++){
-    measureCluster(*itCluster, trackCalo);
+  for (xAOD::CaloCluster* cluster : clusVec) {
+    measureCluster(cluster, trackCalo);
   }
 }
 
-void eflowLayerIntegrator::measureNewClus(std::vector<eflowRecCluster*> efRecClusters, eflowRecTrack* track) {
+void eflowLayerIntegrator::measureNewClus(const std::vector<eflowRecCluster*>& efRecClusters, eflowRecTrack* track) {
   resetAllClustersIntegralForNewTrack(track->getTrackCaloPoints());
 
-  std::vector<eflowRecCluster*>::iterator  itCluster = efRecClusters.begin();
-  std::vector<eflowRecCluster*>::iterator endCluster = efRecClusters.end();
-  for (; itCluster != endCluster; itCluster++){
-    measureCluster(eflowTrackClusterLink::getInstance(track, *itCluster));
+  const EventContext& ctx = Gaudi::Hive::currentContext();
+  for (eflowRecCluster* cluster : efRecClusters) {
+    measureCluster(eflowTrackClusterLink::getInstance(track, cluster, ctx));
   }
 }
 
diff --git a/Reconstruction/eflowRec/src/eflowLookupExp.cxx b/Reconstruction/eflowRec/src/eflowLookupExp.cxx
index 32bf9c4e015c8d32252a6e841a87fdf80fae1d9e..4f0609ab200dcad1d35e520a7f2663705109ff0a 100644
--- a/Reconstruction/eflowRec/src/eflowLookupExp.cxx
+++ b/Reconstruction/eflowRec/src/eflowLookupExp.cxx
@@ -1,5 +1,5 @@
 /*
-  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+  Copyright (C) 2002-2021 CERN for the benefit of the ATLAS collaboration
 */
 
 /*
@@ -10,8 +10,21 @@
  */
 
 #include "eflowRec/eflowLookupExp.h"
+#include "CxxUtils/CachedUniquePtr.h"
+#include "CxxUtils/checker_macros.h"
 
-//eflowLookupExp* eflowLookupExp::m_instance = nullptr;
-std::unique_ptr<eflowLookupExp> eflowLookupExp::m_instance  = nullptr;
 
+const eflowLookupExp* eflowLookupExp::getInstance(int nExpBins /*= 50*/,
+                                                  int nExpSubBins /*= 1000*/)
+{
+  static CxxUtils::CachedUniquePtr<eflowLookupExp> cached ATLAS_THREAD_SAFE;
+  if (!cached) {
+    cached.set (std::make_unique<eflowLookupExp>(nExpBins, nExpSubBins));
+  }
 
+  if ( (cached->m_nExpBins != nExpBins) || (cached->m_nExpSubBins != nExpSubBins) )
+  {
+    throw std::runtime_error("eflowLookupExp: Instance with different bin numbers than existing requested!");
+  }
+  return cached.get();
+}
diff --git a/Reconstruction/eflowRec/src/eflowTrackClusterLink.cxx b/Reconstruction/eflowRec/src/eflowTrackClusterLink.cxx
index 1b3fe56f21fc3e8dcdff568fcfda9a03dbf470d6..a207efc41a161826bfdaa2d172d15710b1e3b887 100644
--- a/Reconstruction/eflowRec/src/eflowTrackClusterLink.cxx
+++ b/Reconstruction/eflowRec/src/eflowTrackClusterLink.cxx
@@ -1,5 +1,5 @@
 /*
-  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+  Copyright (C) 2002-2021 CERN for the benefit of the ATLAS collaboration
 */
 
 /*
@@ -10,5 +10,28 @@
  */
 
 #include "eflowRec/eflowTrackClusterLink.h"
+#include "AthenaKernel/SlotSpecificObj.h"
+#include "CxxUtils/checker_macros.h"
 
-std::map<std::pair<eflowRecTrack*, eflowRecCluster*>, std::unique_ptr<eflowTrackClusterLink> > eflowTrackClusterLink::m_instances;
+
+
+eflowTrackClusterLink*
+eflowTrackClusterLink::getInstance(eflowRecTrack* track,
+                                   eflowRecCluster* cluster,
+                                   const EventContext& ctx)
+{
+  static SG::SlotSpecificObj<Cache> slots ATLAS_THREAD_SAFE;
+  Cache& c = *slots.get (ctx);
+  std::lock_guard lock (c.m_mutex);
+  if (c.m_evt != ctx.evt()) {
+    c.m_map.clear();
+    c.m_evt = ctx.evt();
+  }
+
+  key_t key (track, cluster);
+  auto it = c.m_map.try_emplace (key, std::unique_ptr<eflowTrackClusterLink>()).first;
+  if (!it->second) {
+    it->second = std::make_unique<eflowTrackClusterLink>(track,cluster);
+  }
+  return it->second.get();
+}