Java 以同步、线程安全的方式加载共享资源

Java 以同步、线程安全的方式加载共享资源,java,multithreading,thread-safety,Java,Multithreading,Thread Safety,问题 我只想加载一次共享资源并将它们保存在内存中。目前,我正在使用一种同步方法进行加载,并使用HashMap将加载的资源保存在内存中 问题 问题1: 有没有更好的方法使用标准Java方法来实现这一点 问题2: 下面的代码生成6个线程,其中3个访问资源1,另3个访问资源2。日志输出如下所示: Get resource id: 1 Load resource id: 1 Counter: 1 Get resource id: 2 Thread Thread[Thread-0,5,main], loa

问题

我只想加载一次共享资源并将它们保存在内存中。目前,我正在使用一种同步方法进行加载,并使用HashMap将加载的资源保存在内存中

问题

问题1:

有没有更好的方法使用标准Java方法来实现这一点

问题2:

下面的代码生成6个线程,其中3个访问资源1,另3个访问资源2。日志输出如下所示:

Get resource id: 1
Load resource id: 1
Counter: 1
Get resource id: 2
Thread Thread[Thread-0,5,main], loaded resource id: 1
Load resource id: 2
Counter: 2
Thread Thread[Thread-5,5,main], loaded resource id: 2
Get resource id: 2
Thread Thread[Thread-4,5,main], loaded resource id: 2
Get resource id: 2
Thread Thread[Thread-3,5,main], loaded resource id: 2
Get resource id: 1
Get resource id: 1
Thread Thread[Thread-2,5,main], loaded resource id: 1
Thread Thread[Thread-1,5,main], loaded resource id: 1
问题是加载资源id 1的线程被阻塞,直到加载资源id 2的线程完成。即使资源id为2仍在加载,如何允许资源id为1的线程继续

代码

下面是示例代码:

import java.util.HashMap;
import java.util.Map;

public class SharedResourceLoader {

    /**
     * Resource loading counter, shows how often the loading is invoked.
     */
    public static int loadCounter = 0;

    /**
     * Map of type <Resource Id, Resource> for keeping loaded resources in memory
     */
    public static Map<Integer,Resource> resourceMap = new HashMap<>();

    /**
     * Get a resource by Id
     * @param id
     * @return
     */
    public static Resource getResource( int resourceId) {

        Resource resource = resourceMap.get( resourceId);

        if( resource == null) {
            resource = loadResource( resourceId);
        }

        return resource;
    }

    /**
     * Get a resource by Id synchronized. If it isn't found, load it.
     * @param resourceId
     * @return
     */
    public static synchronized Resource loadResource( int resourceId)  {

        System.out.println("Get resource id: " + resourceId);

        Resource resource = resourceMap.get( resourceId);

        if( resource == null) {

            System.out.println("Load resource id: " + resourceId);

            // load resource
            resource = new Resource( resourceId);
            resource.load();

            // keep resource in memory
            resourceMap.put( resourceId, resource);

            // just a counter to see how often this method is accessed
            loadCounter++;
            System.out.println("Counter: " + loadCounter);
        }

        return resource;

    }

    /**
     * Start a thread that accesses the resource with the given id
     * @param resourceId
     */
    public static void spawnThread( int resourceId) {

        Thread thread = new Thread( new Runnable() {
            @Override
            public void run() {

                Resource resource = getResource(resourceId);

                System.out.println( "Thread " + Thread.currentThread() + ", loaded resource id: " + resource.id);


            }
        });
        thread.start();

    }

    public static class Resource {

        int id;

        public Resource( int id) {
            this.id = id;
        }

        public void load() {

            // dummy sleep, e. g. resource loading happens here
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
            }
        }
    }

    public static void main(String[] args) {

        spawnThread( 1);
        spawnThread( 1);
        spawnThread( 1);

        spawnThread( 2);
        spawnThread( 2);
        spawnThread( 2);

    }

}
import java.util.HashMap;
导入java.util.Map;
公共类SharedResourceLoader{
/**
*资源加载计数器,显示调用加载的频率。
*/
公共静态int loadCounter=0;
/**
*将加载的资源保留在内存中的类型映射
*/
public static Map resourceMap=new HashMap();
/**
*通过Id获取资源
*@param-id
*@返回
*/
公共静态资源getResource(int resourceId){
Resource=resourceMap.get(resourceId);
if(资源==null){
resource=loadResource(resourceId);
}
返回资源;
}
/**
*按Id同步获取资源。如果找不到,请加载它。
*@param resourceId
*@返回
*/
公共静态同步资源loadResource(int resourceId){
System.out.println(“获取资源id:+resourceId”);
Resource=resourceMap.get(resourceId);
if(资源==null){
System.out.println(“加载资源id:+resourceId”);
//加载资源
资源=新资源(资源ID);
resource.load();
//将资源保存在内存中
resourceMap.put(resourceId,resource);
//只需一个计数器即可查看访问此方法的频率
loadCounter++;
System.out.println(“计数器:“+loadCounter”);
}
返回资源;
}
/**
*启动使用给定id访问资源的线程
*@param resourceId
*/
公共静态线程(int-resourceId){
Thread Thread=新线程(new Runnable(){
@凌驾
公开募捐{
Resource=getResource(resourceId);
System.out.println(“线程”+Thread.currentThread()+”,加载的资源id:“+resource.id”);
}
});
thread.start();
}
公共静态类资源{
int-id;
公共资源(int id){
this.id=id;
}
公共空荷载(){
//虚拟睡眠,例如资源加载在这里发生
试一试{
睡眠(5000);
}捕捉(中断异常e){
}
}
}
公共静态void main(字符串[]args){
产卵线程(1);
产卵线程(1);
产卵线程(1);
产卵线程(2);
产卵线程(2);
产卵线程(2);
}
}
有没有更好的方法使用标准Java方法来实现这一点

可能吧,但这取决于你的要求。我倾向于在
ConcurrentMap
上使用
computeIfAbsent
延迟加载所需的值

e、 g

静态最终ConcurrentMap=新ConcurrentHashMap();
静态资源loadResource(int resourceId){
返回map.computeIfAbsent(资源ID,r->{
资源=新资源(r);
resource.load();
返回资源;
}
}
这将允许并发访问不同的密钥,但如果正在加载密钥,它将阻止在加载之前尝试使用它的任何其他线程

即使资源id为2仍在加载,如何允许资源id为1的线程继续

假设线程1不需要线程2正在加载的资源,如果它是相同的资源,则可以执行此操作。如果您引用的是不同的资源,请参见上文。您可以检查数据是否正在加载,以及数据是否正在加载,而不尝试在映射中查找。很可能这不是一个好主意

有没有更好的方法使用标准Java方法来实现这一点

可能吧,但这取决于您的需求。我倾向于在
ConcurrentMap
上使用
computeIfAbsent
延迟加载所需的值

e、 g

静态最终ConcurrentMap=新ConcurrentHashMap();
静态资源loadResource(int resourceId){
返回map.computeIfAbsent(资源ID,r->{
资源=新资源(r);
resource.load();
返回资源;
}
}
这将允许并发访问不同的密钥,但如果正在加载密钥,它将阻止在加载之前尝试使用它的任何其他线程

即使资源id为2仍在加载,如何允许资源id为1的线程继续


假设线程1不需要线程2正在加载的资源,如果它是相同的资源,则可以执行此操作。如果您引用的是不同的资源,请参见上文。您可以检查数据是否正在加载,并且不尝试查看映射。这很可能不是一个好主意。

请查看
java.util.nachrrent
java.util.concurrent.atomic

如果我是你,我会使用
ConcurrentHashMap

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;

public class SharedResourceLoader {

    /**
     * Resource loading counter, shows how often the loading is invoked.
     */
    private static final AtomicInteger loadCounter = new AtomicInteger();

    /**
     * Map of type <Resource Id, Resource> for keeping loaded resources in memory
     */
    public static ConcurrentMap<Integer, Resource> resourceMap = new ConcurrentHashMap<>();

    /**
     * Get a resource by Id
     *
     * @param resourceId
     * @return
     */
    public static Resource getResource(int resourceId) {

        Resource resource = resourceMap.get(resourceId);

        if (resource == null) {
            resource = loadResource(resourceId);
        }

        return resource;
    }

    /**
     * Get a resource by Id synchronized. If it isn't found, load it.
     *
     * @param resourceId
     * @return
     */
    public static Resource loadResource(int resourceId) {

        System.out.println("Get resource id: " + resourceId);

        Resource resource = resourceMap.get(resourceId);

        if (resource == null) {

            System.out.println("Load resource id: " + resourceId);

            // load resource
            final Resource r = resourceMap.putIfAbsent(resourceId, resource = new Resource(resourceId));

            // important!
            if (r != null) {
                resource = r;
            }

            if (resource.load()) {
                // just a counter to see how often this method is accessed
                loadCounter.getAndIncrement();
            }

            System.out.println("Counter: " + loadCounter);
        }

        return resource;

    }

    public static int loadCounter() {
        return loadCounter.get();
    }

    /**
     * Start a thread that accesses the resource with the given id
     *
     * @param resourceId
     */
    public static void spawnThread(int resourceId) {

        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {

                Resource resource = getResource(resourceId);

                System.out.println("Thread " + Thread.currentThread() + ", loaded resource id: " + resource.id);


            }
        });
        thread.start();

    }

    public static class Resource {

        final int id;
        final AtomicBoolean loaded = new AtomicBoolean(false);

        public Resource(int id) {
            this.id = id;
        }

        public boolean load() {
            if (loaded.compareAndSet(false, true)) {
                // dummy sleep, e. g. resource loading happens here
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException ignored) {
                }
                return true;
            }
            return false;
        }
    }

    public static void main(String[] args) {

        spawnThread(1);
        spawnThread(1);
        spawnThread(1);

        spawnThread(2);
        spawnThread(2);
        spawnThread(2);

    }

}

只需看看
java.util.concurrent
java.util.concurrent.atomic

如果我是你,我会使用
ConcurrentHashMap

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;

public class SharedResourceLoader {

    /**
     * Resource loading counter, shows how often the loading is invoked.
     */
    private static final AtomicInteger loadCounter = new AtomicInteger();

    /**
     * Map of type <Resource Id, Resource> for keeping loaded resources in memory
     */
    public static ConcurrentMap<Integer, Resource> resourceMap = new ConcurrentHashMap<>();

    /**
     * Get a resource by Id
     *
     * @param resourceId
     * @return
     */
    public static Resource getResource(int resourceId) {

        Resource resource = resourceMap.get(resourceId);

        if (resource == null) {
            resource = loadResource(resourceId);
        }

        return resource;
    }

    /**
     * Get a resource by Id synchronized. If it isn't found, load it.
     *
     * @param resourceId
     * @return
     */
    public static Resource loadResource(int resourceId) {

        System.out.println("Get resource id: " + resourceId);

        Resource resource = resourceMap.get(resourceId);

        if (resource == null) {

            System.out.println("Load resource id: " + resourceId);

            // load resource
            final Resource r = resourceMap.putIfAbsent(resourceId, resource = new Resource(resourceId));

            // important!
            if (r != null) {
                resource = r;
            }

            if (resource.load()) {
                // just a counter to see how often this method is accessed
                loadCounter.getAndIncrement();
            }

            System.out.println("Counter: " + loadCounter);
        }

        return resource;

    }

    public static int loadCounter() {
        return loadCounter.get();
    }

    /**
     * Start a thread that accesses the resource with the given id
     *
     * @param resourceId
     */
    public static void spawnThread(int resourceId) {

        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {

                Resource resource = getResource(resourceId);

                System.out.println("Thread " + Thread.currentThread() + ", loaded resource id: " + resource.id);


            }
        });
        thread.start();

    }

    public static class Resource {

        final int id;
        final AtomicBoolean loaded = new AtomicBoolean(false);

        public Resource(int id) {
            this.id = id;
        }

        public boolean load() {
            if (loaded.compareAndSet(false, true)) {
                // dummy sleep, e. g. resource loading happens here
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException ignored) {
                }
                return true;
            }
            return false;
        }
    }

    public static void main(String[] args) {

        spawnThread(1);
        spawnThread(1);
        spawnThread(1);

        spawnThread(2);
        spawnThread(2);
        spawnThread(2);

    }

}

非常感谢。你有枪吗
Get resource id: 1
Get resource id: 2
Get resource id: 2
Get resource id: 2
Get resource id: 1
Get resource id: 1
Load resource id: 1
Load resource id: 2
Load resource id: 2
Load resource id: 2
Load resource id: 1
Load resource id: 1
Counter: 0
Counter: 0
Counter: 0
Counter: 0
Thread Thread[Thread-5,5,main], loaded resource id: 2
Thread Thread[Thread-4,5,main], loaded resource id: 2
Thread Thread[Thread-1,5,main], loaded resource id: 1
Thread Thread[Thread-2,5,main], loaded resource id: 1
Counter: 1
Thread Thread[Thread-3,5,main], loaded resource id: 2
Counter: 2
Thread Thread[Thread-0,5,main], loaded resource id: 1