Libgdx 使用多線程來(lái)加載資源牧愁,提升資源加載速度

在Libgdx中加載游戲的資源素邪,我們一般使用AssetManager來(lái)進(jìn)行異步加載,但是其是單線程的猪半,有時(shí)候資源過(guò)多加載實(shí)在是太慢兔朦,而且現(xiàn)在手機(jī)的cpu核數(shù)越來(lái)越多斜姥,下面是自己動(dòng)手實(shí)現(xiàn)的一個(gè)多線程版本馍刮,其實(shí)原理是通過(guò)查手機(jī)cpu核數(shù)將資源動(dòng)態(tài)的分配給多個(gè)AssetsManager加載

package com.mytian.mgarden.utils.libgdxutil;

import com.badlogic.gdx.assets.AssetDescriptor;
import com.badlogic.gdx.assets.AssetErrorListener;
import com.badlogic.gdx.assets.loaders.resolvers.InternalFileHandleResolver;
import com.badlogic.gdx.files.FileHandle;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.Disposable;
import com.badlogic.gdx.utils.GdxRuntimeException;

import java.lang.reflect.Field;
import java.util.HashMap;

/**
 * Created by gang.zhou on 2018/1/5.
 */

public class AssetsManagerPool implements Disposable {
    public static int SIZE = Math.max(Runtime.getRuntime().availableProcessors() / 2, 1);
    private Array<Assets> mAssetsArray = new Array(SIZE);
    private int counter = 0;
    private static HashMap<Class, Array<String>> pathM = new HashMap<>();
    private static AssetsManagerPool instance;
    private volatile boolean isLoad;

    public static AssetsManagerPool getInstance() {
        synchronized (AssetsManagerPool.class) {
            if (null == instance) {
                instance = new AssetsManagerPool();
            }
        }
        return instance;
    }

    private AssetsManagerPool() {
        for (int i = 0; i < SIZE; i++) {
            mAssetsArray.add(new Assets(new InternalFileHandleResolver()));
        }
        for (final Assets assets : mAssetsArray) {
            assets.setErrorListener(new AssetErrorListener() {
                @Override
                public void error(AssetDescriptor asset, Throwable throwable) {
                    throw new GdxRuntimeException(throwable);
                }
            });
        }
    }

    public synchronized void add(final String filePath) {
        for (final Assets assets : mAssetsArray) {
            if (assets.isLoaded(filePath)) {
                return;
            }
        }
        mAssetsArray.get(counter % SIZE).add(filePath);
        ++counter;
        isLoad = true;
    }


    public static Array<String> getPaths(Class<?> cls) {
        if (pathM.containsKey(cls)) {
            return pathM.get(cls);
        }
        final Array<String> filePaths = new Array<>();
        final Class<?>[] classes = cls.getDeclaredClasses();
        if (null != classes) {
            for (final Class<?> c : classes) {
                filePaths.addAll(getPaths(c));
            }
        }
        final Field[] fields = cls.getFields();
        try {
            for (final Field f : fields) {
                if (String.class == f.getType()) {
                    f.setAccessible(true);
                    filePaths.add((String) f.get(null));
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        pathM.put(cls, filePaths);
        return filePaths;
    }

    public synchronized void addDir(final Class<?> cls) {
        final Array<String> filePaths = getPaths(cls);
        for (final String path : filePaths) {
            if (path.endsWith(".png")) {
                if (filePaths.contains(path.replace(".png", ".fnt"), false)
                        || filePaths.contains(path.replace(".png", ".atlas"), false)
                        || filePaths.contains(path.replace(".png", ".skel"), false)
                        || filePaths.contains(path.replace(".png", ".json"), false)) {
                    continue;
                }
                boolean isFind = false;
                for (int i = 2; i < 20; i++) {
                    if (path.endsWith(i + ".png")) {
                        if (filePaths.contains(path.replace(i + ".png", ".atlas"), false)
                                || filePaths.contains(path.replace(i + ".png", ".fnt"), false)
                                || filePaths.contains(path.replace(i + ".png", ".skel"), false)
                                || filePaths.contains(path.replace(i + ".png", ".json"), false)) {
                            isFind = true;
                        }
                        break;
                    }
                }
                if (isFind) {
                    continue;
                }
            } else if (path.endsWith(".atlas")) {
                if (filePaths.contains(path.replace(".atlas", ".json"), false)
                        || filePaths.contains(path.replace(".atlas", ".skel"), false)) {
                    continue;
                }
            } else if (path.endsWith(".mp3") || path.endsWith(".wav")) {
                if (!path.contains("sfx") && !path.contains("svo")) {
                    continue;
                }
            }
            add(path);
        }
    }

    public synchronized void removeDir(Class<?> cls) {
        final Array<String> filePaths = getPaths(cls);
        for (final String path : filePaths) {
            if (path.endsWith(".png")) {
                if (filePaths.contains(path.replace(".png", ".fnt"), false)
                        || filePaths.contains(path.replace(".png", ".atlas"), false)
                        || filePaths.contains(path.replace(".png", ".skel"), false)
                        || filePaths.contains(path.replace(".png", ".json"), false)) {
                    continue;
                } else {
                    boolean isFind = false;
                    for (int i = 2; i < 15; i++) {
                        if (path.endsWith(i + ".png")) {
                            if (filePaths.contains(path.replace(i + ".png", ".atlas"), false)
                                    || filePaths.contains(path.replace(i + ".png", ".fnt"), false)
                                    || filePaths.contains(path.replace(i + ".png", ".skel"), false)
                                    || filePaths.contains(path.replace(i + ".png", ".json"),
                                    false)) {
                                isFind = true;
                            }
                            break;
                        }
                    }
                    if (isFind) {
                        continue;
                    }
                }
            } else if (path.endsWith(".atlas")) {
                if (filePaths.contains(path.replace(".atlas", ".json"), false)
                        || filePaths.contains(path.replace(".atlas", ".skel"), false)) {
                    continue;
                }
            } else if (path.endsWith(".mp3") || path.endsWith(".wav")) {
                if (!path.contains("sfx") && !path.contains("svo")) {
                    continue;
                }
            }
            unload(path);
        }
    }


    public synchronized void unload(final String path) {
        for (final Assets assets : mAssetsArray) {
            try {
                assets.unload(path);
            } catch (Exception e) {

            }
        }
    }

    public synchronized Array<String> getAssetNames(){
        final Array<String> assetNameArray = new Array<>();
        for (final Assets assets : mAssetsArray) {
            assetNameArray.addAll(assets.getAssetNames());
        }
        return assetNameArray;
    }

    public boolean update() {
        boolean complete = true;
        for (final Assets assets : mAssetsArray) {
            complete = assets.update() && complete;
        }
        if (complete) {
            counter = 0;
        }
        return complete;
    }

    public float getProgress() {
        float progress = 0;
        for (final Assets assets : mAssetsArray) {
            progress += assets.getProgress();
        }
        return progress / SIZE;
    }

    public synchronized <T> T get(String path) {
        for (final Assets assets : mAssetsArray) {
            if (assets.isLoaded(path)) {
                return (T) assets.get(path, assets.getAssetType(path));
            }
        }
        mAssetsArray.get(counter % SIZE).add(path);
        mAssetsArray.get(counter % SIZE).finishLoadingAsset(path);
        return mAssetsArray.get(counter++ % SIZE).get(path);
    }

    public void render() {
        if (null != instance && isLoad) {
            isLoad = !update();
        }
    }

    public FileHandle file(String path) {
        return mAssetsArray.get(0).getFileHandleResolver().resolve(path);
    }

    public synchronized void clear() {
        for (final Assets assets : mAssetsArray) {
            assets.finishLoading();
            assets.clear();
        }
    }

    public void finishLoading() {
        for (final Assets assets : mAssetsArray) {
            assets.finishLoading();
        }
    }

    @Override
    public synchronized void dispose() {
        synchronized (AssetsManagerPool.class) {
            instance = null;
        }
        for (final Assets assets : mAssetsArray) {
            try {
                assets.dispose();
            } catch (Exception e) {

            }
        }
        mAssetsArray.clear();
    }
}
package com.mytian.mgarden.utils.libgdxutil;

import com.badlogic.gdx.assets.AssetLoaderParameters;
import com.badlogic.gdx.assets.AssetManager;
import com.badlogic.gdx.assets.loaders.BitmapFontLoader;
import com.badlogic.gdx.assets.loaders.FileHandleResolver;
import com.badlogic.gdx.assets.loaders.TextureLoader;
import com.badlogic.gdx.audio.Sound;
import com.badlogic.gdx.files.FileHandle;
import com.badlogic.gdx.graphics.Pixmap;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.graphics.g2d.TextureAtlas;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.ObjectMap;
import com.badlogic.gdx.utils.async.AsyncExecutor;
import com.esotericsoftware.spine.SkeletonData;
import com.mytian.mgarden.stages.classes.BaseClassLoadingGroup;
import com.mytian.mgarden.stages.classes.ClassLoadingGroup;
import com.mytian.mgarden.stages.classes.hh.ClassLoadingGroupHH;
import com.mytian.mgarden.utils.particleutil.CCDictionaryLoader;

import java.lang.reflect.Field;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;


/**
 * Created by ayo on 2016/12/9.
 */

public class Assets extends AssetManager {
    private static Assets instance;
    public final static TextureLoader.TextureParameter RGB4444_TEXTURE_PARAMETER = new TextureLoader.TextureParameter();
    public final static TextureLoader.TextureParameter RGB888_TEXTURE_PARAMETER = new TextureLoader.TextureParameter();
    public final static TextureLoader.TextureParameter RGB565_TEXTURE_PARAMETER = new TextureLoader.TextureParameter();


    public final static  BitmapFontLoader.BitmapFontParameter BITMAP_FONT_PARAMETER_TEXTURE_FILTER =
            new BitmapFontLoader.BitmapFontParameter();
    static{
        BITMAP_FONT_PARAMETER_TEXTURE_FILTER.minFilter = Texture.TextureFilter.Linear;
        BITMAP_FONT_PARAMETER_TEXTURE_FILTER.magFilter = Texture.TextureFilter.Linear;
    }

    private volatile boolean isLoad;

    static {
        RGB4444_TEXTURE_PARAMETER.format = Pixmap.Format.RGBA4444;
        RGB888_TEXTURE_PARAMETER.format = Pixmap.Format.RGB888;
        RGB565_TEXTURE_PARAMETER.format = Pixmap.Format.RGB565;
    }

    protected Assets(FileHandleResolver resolver) {
        super(resolver);
        instance = this;
        setLoader(SkeletonData.class, new SkeletonLoader(resolver));
        setLoader(mytian.esotericsoftware.spine.SkeletonData.class
                , new SkeletonLoader3(resolver));
        setLoader(ObjectMap.class, new CCDictionaryLoader(resolver));
        try {
            Field executor = AssetManager.class.getDeclaredField("executor");
            executor.setAccessible(true);
            final AsyncExecutor mAsyncExecutor = (AsyncExecutor) executor.get(this);
            executor = AsyncExecutor.class.getDeclaredField("executor");
            executor.setAccessible(true);
            final ThreadPoolExecutor mThreadPoolExecutor = (ThreadPoolExecutor) executor.get(mAsyncExecutor);
            mThreadPoolExecutor.setThreadFactory(new ThreadFactory() {
                @Override
                public Thread newThread(Runnable runnable) {
                    final Thread thread = new Thread(runnable, "AsynchExecutor-Thread " + System.currentTimeMillis());
                    thread.setPriority(Thread.MIN_PRIORITY);
                    thread.setDaemon(true);
                    return thread;
                }
            });
            mThreadPoolExecutor.setCorePoolSize(0);
            mThreadPoolExecutor.setMaximumPoolSize(1);
            mThreadPoolExecutor.setKeepAliveTime(3, TimeUnit.MINUTES);
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }


    /**
     * 可以獲取到已經(jīng)加載好的texture砂碉、textureAtlas顽分、particleEffect虫几、sound寓娩。
     * 如果未加載或未加載完成趟径,會(huì)采用同步方式加載一個(gè)并返回雾家。
     *
     * @param <T>
     */

    @SuppressWarnings("unchecked")
    public <T> T get(String path) {
        if (isLoaded(path)) {
            return (T) get(path, getAssetType(path));
        } else {
            add(path);
            finishLoadingAsset(path);
            return get(path);
        }
    }

    public FileHandle file(String path) {
        return getFileHandleResolver().resolve(path);
    }

    /**
     * 添加要加載的資源到隊(duì)列英融,此時(shí)并未加載盏檐。加載為異步。 默認(rèn)僅加載png/jpg驶悟、p/plist(粒子文件)胡野、atlas、mp3/wav格式的文件痕鳍。
     *
     * @param filePath 資源的internal路徑硫豆。可直接傳入com.mytian.R包中的變量
     */
    public void add(final String filePath) {
        if (isLoaded(filePath)) {
            return;
        }
        final String lowerPath = filePath.toLowerCase();
        if (lowerPath.endsWith(".png") || lowerPath.endsWith(".jpg")) {
            if (lowerPath.contains("rgb4444_")) {
                load(filePath, Texture.class, RGB4444_TEXTURE_PARAMETER);
            } else if (lowerPath.contains("rgb888_")) {
                load(filePath, Texture.class, RGB888_TEXTURE_PARAMETER);
            } else if (lowerPath.contains("rgb565_")) {
                load(filePath, Texture.class, RGB565_TEXTURE_PARAMETER);
            } else {
                load(filePath, Texture.class);
            }
        } else if (lowerPath.endsWith(".mp3") || lowerPath.endsWith(".wav")) {
            load(filePath, Sound.class);
        } else if (lowerPath.endsWith(".json") || lowerPath.endsWith(".skel")) {
            if (lowerPath.contains("3_6_52_1")) {
                if (lowerPath.endsWith("erji59.skel") || lowerPath.endsWith("erji60.skel")
                    || lowerPath.endsWith("erji61.skel") || lowerPath.endsWith("erji62.skel")
                    || lowerPath.endsWith("erji63.skel")) {
                    load(filePath, mytian.esotericsoftware.spine.SkeletonData.class,new
                        SkeletonLoader3.SkeletonDataParam(0.90f));
                } else {
                    load(filePath, mytian.esotericsoftware.spine.SkeletonData.class);
                }
            } else {
                load(filePath, SkeletonData.class);
            }
        } else if (lowerPath.endsWith(".atlas")) {
            load(filePath, TextureAtlas.class);
        } else if (lowerPath.endsWith(".fnt")) {
            load(filePath, BitmapFont.class, BITMAP_FONT_PARAMETER_TEXTURE_FILTER);
        } else if (lowerPath.endsWith(".plist")) {
            load(filePath, ObjectMap.class);
        }
    }

    /**
     * 遍歷所傳R包中的類下的所有文件路徑笼呆。
     *
     * @param cls 傳入com.mytian.R包中的類或內(nèi)部類熊响。
     */
    public synchronized void addDir(final Class<?> cls) {
        Array<String> filePaths = getPaths(cls);
        for (String path : filePaths) {
            if (path.endsWith(".png") && (filePaths.contains(path.replace(".png", ".atlas"), false)
                    || filePaths.contains(path.replace(".png", ".p"), false))) {
                continue;
            }
            if (path.endsWith(".json") && !filePaths.contains(path.replace(".json", ".atlas"), false)) {
                continue;
            }
            if (path.endsWith(".mp3") || path.endsWith(".wav")) {
                if (!path.contains("sfx") && !path.contains("svo")) {
                    continue;
                }
            }
            add(path);
        }
    }

    public synchronized void addDir(String className) {
        try {
            addDir(Class.forName(className));
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    // 反射遍歷R類下的文件路徑
    protected Array<String> getPaths(Class<?> cls) {
        Array<String> filePaths = new Array<String>();
        Class<?>[] classes = cls.getDeclaredClasses();
        if (classes != null) {
            for (Class<?> c : classes) {
                filePaths.addAll(getPaths(c));
            }
        }
        Field[] fileds = cls.getFields();
        try {
            for (Field f : fileds) {
                if (f.getType() == String.class) {
                    f.setAccessible(true);
                    String value = (String) f.get(null);
                    filePaths.add(value);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return filePaths;
    }

    /**
     * 釋放該R包中的類下的所有文件路徑對(duì)應(yīng)的資源
     *
     * @param cls
     */

    public synchronized void removeDir(Class<?> cls) {
        Array<String> filePaths = getPaths(cls);
        for (String path : filePaths) {
            if (isLoaded(path)) {
                unload(path);
            }
        }
    }


    /**
     * 銷毀所有已加載的資源,銷毀異步執(zhí)行器诗赌,并停止仍在進(jìn)行的異步加載
     */
    @Override
    public synchronized void dispose() {
        synchronized (Assets.class) {
            instance = null;
            try {
                super.dispose();
            } catch (Exception e) {

            }
            try {
                if (null != ClassLoadingGroup.mNativeFont) {
                    ClassLoadingGroup.mNativeFont.dispose();
                    ClassLoadingGroup.mNativeFont = null;
                }
            } catch (Exception e) {

            }
            try {
                if (null != ClassLoadingGroupHH.mNativeFont) {
                    ClassLoadingGroupHH.mNativeFont.dispose();
                    ClassLoadingGroupHH.mNativeFont = null;
                }
            } catch (Exception e) {

            }
            try {
                if (null != BaseClassLoadingGroup.mNativeFont) {
                    BaseClassLoadingGroup.mNativeFont.dispose();
                    BaseClassLoadingGroup.mNativeFont = null;
                }
            } catch (Exception e) {

            }
        }
    }

    @Override
    public synchronized <T> void load(final String fileName, final Class<T> type
            , AssetLoaderParameters<T> parameter) {
        super.load(fileName, type, parameter);
        isLoad = true;
    }

    public void render() {
        if (null != instance && isLoad) {
            isLoad = !update();
        }
    }

}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末汗茄,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子铭若,更是在濱河造成了極大的恐慌洪碳,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,126評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件叼屠,死亡現(xiàn)場(chǎng)離奇詭異瞳腌,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)镜雨,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門纯趋,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人冷离,你說(shuō)我怎么就攤上這事吵冒。” “怎么了西剥?”我有些...
    開(kāi)封第一講書人閱讀 152,445評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵痹栖,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我瞭空,道長(zhǎng)揪阿,這世上最難降的妖魔是什么疗我? 我笑而不...
    開(kāi)封第一講書人閱讀 55,185評(píng)論 1 278
  • 正文 為了忘掉前任,我火速辦了婚禮南捂,結(jié)果婚禮上吴裤,老公的妹妹穿的比我還像新娘。我一直安慰自己溺健,他們只是感情好麦牺,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,178評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著鞭缭,像睡著了一般剖膳。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上岭辣,一...
    開(kāi)封第一講書人閱讀 48,970評(píng)論 1 284
  • 那天吱晒,我揣著相機(jī)與錄音,去河邊找鬼沦童。 笑死仑濒,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的偷遗。 我是一名探鬼主播躏精,決...
    沈念sama閱讀 38,276評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼鹦肿!你這毒婦竟也來(lái)了矗烛?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書人閱讀 36,927評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤箩溃,失蹤者是張志新(化名)和其女友劉穎瞭吃,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體涣旨,經(jīng)...
    沈念sama閱讀 43,400評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡歪架,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,883評(píng)論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了霹陡。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片和蚪。...
    茶點(diǎn)故事閱讀 37,997評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖烹棉,靈堂內(nèi)的尸體忽然破棺而出攒霹,到底是詐尸還是另有隱情,我是刑警寧澤浆洗,帶...
    沈念sama閱讀 33,646評(píng)論 4 322
  • 正文 年R本政府宣布催束,位于F島的核電站,受9級(jí)特大地震影響伏社,放射性物質(zhì)發(fā)生泄漏抠刺。R本人自食惡果不足惜塔淤,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,213評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望速妖。 院中可真熱鬧高蜂,春花似錦、人聲如沸罕容。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 30,204評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)杀赢。三九已至烘跺,卻和暖如春湘纵,著一層夾襖步出監(jiān)牢的瞬間脂崔,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 31,423評(píng)論 1 260
  • 我被黑心中介騙來(lái)泰國(guó)打工梧喷, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留砌左,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,423評(píng)論 2 352
  • 正文 我出身青樓铺敌,卻偏偏與公主長(zhǎng)得像汇歹,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子偿凭,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,722評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容

  • 前言 個(gè)人認(rèn)為产弹,學(xué)習(xí),內(nèi)容越多弯囊、越雜的知識(shí)痰哨,越需要進(jìn)行深刻的總結(jié),這樣才能記憶深刻匾嘱,將知識(shí)變成自己的斤斧。這篇文章主要...
    touch_The_Sky閱讀 387評(píng)論 0 2
  • 前言 個(gè)人認(rèn)為,學(xué)習(xí)霎烙,內(nèi)容越多撬讽、越雜的知識(shí),越需要進(jìn)行深刻的總結(jié)悬垃,這樣才能記憶深刻游昼,將知識(shí)變成自己的。這篇文章主要...
    堯淳閱讀 669評(píng)論 0 17
  • 前言 這篇文章主要是對(duì)多線程的問(wèn)題進(jìn)行總結(jié)的尝蠕,因此羅列了40個(gè)多線程的問(wèn)題酱床。 這些多線程的問(wèn)題,有些來(lái)源于各大網(wǎng)站...
    java成功之路閱讀 827評(píng)論 0 9
  • 40個(gè)問(wèn)題匯總 1趟佃、多線程有什么用扇谣? 一個(gè)可能在很多人看來(lái)很扯淡的一個(gè)問(wèn)題:我會(huì)用多線程就好了昧捷,還管它有什么用?在...
    寫代碼的杰西閱讀 440評(píng)論 0 6
  • 我從來(lái)沒(méi)有輸過(guò),更不想輸在分手這件事上鸯绿。 在一個(gè)沉寂了半年的微信老鄉(xiāng)群里面跋破,突然發(fā)來(lái)了一條信息。 老高:有誰(shuí)在深圳...
    方不見(jiàn)閱讀 5,289評(píng)論 30 99