国产成人精品18p,天天干成人网,无码专区狠狠躁天天躁,美女脱精光隐私扒开免费观看

Java基礎之代碼死循環(huán)詳解

發(fā)布時(shí)間:2021-07-17 21:51 來(lái)源:腳本之家 閱讀:0 作者:蘇三說(shuō)技術(shù) 欄目: 編程語(yǔ)言 歡迎投稿:712375056

目錄

            一、前言

            代碼死循環(huán)這個(gè)話(huà)題,個(gè)人覺(jué)得還是挺有趣的。因為只要是開(kāi)發(fā)人員,必定會(huì )踩過(guò)這個(gè)坑。如果真的沒(méi)踩過(guò),只能說(shuō)明你代碼寫(xiě)少了,或者是真正的大神。

            盡管很多時(shí)候,我們在極力避免這類(lèi)問(wèn)題的發(fā)生,但很多時(shí)候,死循環(huán)卻悄咪咪的來(lái)了,坑你于無(wú)形之中。我敢保證,如果你讀完這篇文章,一定會(huì )對代碼死循環(huán)有一些新的認識,學(xué)到一些非常實(shí)用的經(jīng)驗,少走一些彎路。

            二、死循環(huán)的危害

            我們先來(lái)一起了解一下,代碼死循環(huán)到底有哪些危害?

            • 程序進(jìn)入假死狀態(tài), 當某個(gè)請求導致的死循環(huán),該請求將會(huì )在很大的一段時(shí)間內,都無(wú)法獲取接口的返回,程序好像進(jìn)入假死狀態(tài)一樣。
            • cpu使用率飆升,代碼出現死循環(huán)后,由于沒(méi)有休眠,一直不斷搶占cpu資源,導致cpu長(cháng)時(shí)間處于繁忙狀態(tài),必定會(huì )使cpu使用率飆升。
            • 內存使用率飆升,如果代碼出現死循環(huán)時(shí),循環(huán)體內有大量創(chuàng )建對象的邏輯,垃圾回收器無(wú)法及時(shí)回收,會(huì )導致內存使用率飆升。同時(shí),如果垃圾回收器頻繁回收對象,也會(huì )造成cpu使用率飆升。
            • StackOverflowError,在一些遞歸調用的場(chǎng)景,如果出現死循環(huán),多次循環(huán)后,最終會(huì )報StackOverflowError棧溢出,程序直接掛掉。

            三、哪些場(chǎng)景會(huì )產(chǎn)生死循環(huán)?

            3.1 一般循環(huán)遍歷

            這里說(shuō)的一般循環(huán)遍歷主要是指:

            • for語(yǔ)句
            • foreach語(yǔ)句
            • while語(yǔ)句

            這三種循環(huán)語(yǔ)句可能是我們平常使用最多的循環(huán)語(yǔ)句了,但是如果沒(méi)有用好,也是最容易出現死循環(huán)的問(wèn)題的地方。讓我們一起看看,哪些情況會(huì )出現死循環(huán)。

            3.1.1 條件恒等

            很多時(shí)候我們使用for語(yǔ)句循環(huán)遍歷,不滿(mǎn)足指定條件,程序會(huì )自動(dòng)退出循環(huán),比如:

            for(int i=0; i<10; i++) {
               System.out.println(i);
            }

            但是,如果不小心把條件寫(xiě)錯了,變成這樣的:

            for(int i=0; i>=0; i++) {
               System.out.println(i);
            }

            結果就悲劇了,必定會(huì )出現死循環(huán),因為循環(huán)中的條件變成恒等的了。

            很多朋友看到這里,心想這種錯誤我肯定不會(huì )犯的。不過(guò)我需要特別說(shuō)明的是,這里舉的例子相對來(lái)說(shuō)比較簡(jiǎn)單,如果i>=0這里是個(gè)非常復雜的計算,還真說(shuō)不準一定不會(huì )出現死循環(huán)。

            3.1.2 不正確的continue

            for語(yǔ)句在循環(huán)遍歷數組list時(shí)更方便,而while語(yǔ)句的使用場(chǎng)景卻更多。

            有時(shí)候,在使用while語(yǔ)句遍歷數據時(shí),如果遇到特別的條件,可以使用continue關(guān)鍵字跳過(guò)本次循環(huán),直接執行下次循環(huán)。

            例如:

            int count = 0;
            while(count < 10) {
               count++;
               if(count == 4) {
                  continue;
               }
               System.out.println(count);
            }

            當count等于4時(shí),不打印count。

            但如果continue沒(méi)有被正確使用,可能會(huì )出現莫名奇怪的問(wèn)題:

            int count = 0;
            while(count < 10) {
               if(count == 4) {
                  continue;
               }
               System.out.println(count);
               count++;
            }

            當count等于4時(shí)直接推出本次循環(huán),count沒(méi)有加1,而直接進(jìn)入下次循環(huán),下次循環(huán)時(shí)count依然等4,最后無(wú)限循環(huán)了。

            這種是我們要千萬(wàn)小心的場(chǎng)景,說(shuō)不定,已經(jīng)進(jìn)入了死循環(huán)你還不知道呢。

            3.1.3 flag線(xiàn)程間不可見(jiàn)

            有時(shí)候我們的代碼需要一直做某件事情,直到某個(gè)條件達到,有個(gè)狀態(tài)告訴它,要終止任務(wù)了,它就會(huì )自動(dòng)退出。

            這時(shí)候,很多人都會(huì )想到用while(flag)實(shí)現這個(gè)功能:

            public class FlagTest {
                private boolean flag = true;
            
                public void setFlag(boolean flag) {
                    this.flag = flag;
                }
            
                public void fun() {
                    while (flag) {
                    }
                    System.out.println("done");
                }
            
                public static void main(String[] args) throws InterruptedException {
                    final FlagTest flagTest = new FlagTest();
                    new Thread(() -> flagTest.fun()).start();
                    Thread.sleep(200);
                    flagTest.setFlag(false);
                }
            }
            

            這段代碼在子線(xiàn)程中執行無(wú)限循環(huán),當主線(xiàn)程休眠200毫秒后,將flag變成false,這時(shí)子線(xiàn)程就會(huì )自動(dòng)退出了。想法是好的,但是實(shí)際上這段代碼進(jìn)入了死循環(huán),不會(huì )因為flag變成false而自動(dòng)退出。

            為什么會(huì )這樣?

            線(xiàn)程間flag是不可見(jiàn)的,這時(shí)如果flag加上了volatile關(guān)鍵字,變成:

            private volatile boolean flag = true;

            會(huì )強制把共享內存中的值刷新到主內存中,讓多個(gè)線(xiàn)程間可見(jiàn),程序可以正常退出。

            3.2 Iterator遍歷

            除了前面介紹過(guò)的一般循環(huán)遍歷之外,遍歷集合的元素,還可以使用Iterator遍歷。當然并非所有集合都能使用Iterator遍歷,只有實(shí)現了Iterator接口的集合,或者該集合的內部類(lèi)實(shí)現了Iterator接口才可以。

            例如:

            public class IteratorTest {
                public static void main(String[] args) {
                    List<String> list = new ArrayList<>();
                    list.add("123");
                    list.add("456");
                    list.add("789");
            
                    Iterator<String> iterator = list.iterator();
                    while(iterator.hasNext()) {
                        System.out.println(iterator.next());
                    }
                }
            }
            

            但如果程序改成這樣:

            public class IteratorTest {
                public static void main(String[] args) {
                    List<String> list = new ArrayList<>();
                    list.add("123");
                    list.add("456");
                    list.add("789");
            
                    while(list.iterator().hasNext()) {
                        System.out.println(list.iterator().next());
                    }
                }
            }
            

            就會(huì )出現死循環(huán)。

            這是什么呢?

            如果看過(guò)ArrayList源碼的朋友,會(huì )發(fā)現它的底層iterator方法是這樣的實(shí)現的:

            public Iterator<E> iterator() {
                return new Itr();
            }

            每次都new了一個(gè)新的Itr對象。而hasNext方法的底層是通過(guò)判斷游標和元素個(gè)數是否相等實(shí)現的:

            public boolean hasNext() {
                return cursor != size;
            }

            每次new了一個(gè)新的Itr對象的時(shí)候cursor值是默認值0,肯定和元素個(gè)數不相等。所以導致while語(yǔ)句中的條件一直都成立,所以才會(huì )出現死循環(huán)。

            我們都需要注意:在while循環(huán)中使用list.iterator().hasNext(),是個(gè)非常大的坑,千萬(wàn)小心。

            3.3 類(lèi)中使用自己的對象

            在某個(gè)類(lèi)中把自己的對象定義成成員變量,不知道你有沒(méi)有這樣做過(guò)。

            有些可能會(huì )很詫異,為什么要這么做。

            假如,你需要在一個(gè)方法中調用另一個(gè)打了@Transactional注解的方法,這時(shí)如果直接方法調用,另外一個(gè)方法由于無(wú)法走代理事務(wù)會(huì )失效。比如:

            @Service
            public class ServiceA {
            
               public void save(User user) {
                     System.out.println("業(yè)務(wù)處理");
                     doSave(user);
               }
            
               @Transactional(rollbackFor=Exception.class)
               public void doSave(User user) {
                   System.out.println("保存數據");
                }
             }
            

            這種場(chǎng)景事務(wù)會(huì )失效。

            這時(shí)可以通過(guò)把該類(lèi)自己定義成一個(gè)成員變量,通過(guò)該變量調用doSave方法就能有效的避免該問(wèn)題。

            @Service
            public class ServiceA {
               @Autowired
               private ServiceA serviceA;
               
               public void save(User user) {
                     System.out.println("業(yè)務(wù)處理");
                     serviceA.doSave(user);
               }
            
               @Transactional(rollbackFor=Exception.class)
               public void doSave(User user) {
                   System.out.println("保存數據");
                }
             }
            

            當然還有其他辦法解決這個(gè)問(wèn)題,不過(guò)這種方法是最簡(jiǎn)單的。

            問(wèn)題來(lái)了,如果成員變量不是通過(guò)@Autowired注入,而是直接new出來(lái)的,可以嗎?

            成員變量改成這樣之后:

            private ServiceA serviceA = new ServiceA();

            項目在啟動(dòng)的時(shí)候,程序進(jìn)入無(wú)限循環(huán),不斷創(chuàng )建ServiceA對象,但一直都無(wú)法成功。最后會(huì )報java.lang.StackOverflowError棧溢出,當棧深度超過(guò)虛擬機分配給線(xiàn)程的棧大小時(shí)就會(huì )出現此錯誤。

            為什么會(huì )出現這個(gè)問(wèn)題?

            因為程序在實(shí)例化ServiceA對象時(shí),要先實(shí)例化它的成員變量serviceA,但是它的成員變量serviceA,又需要實(shí)例化它自己的成員變量serviceA,如此一層層實(shí)例化下去,最終也沒(méi)能實(shí)例化。

            @Autowired注入為什么沒(méi)有問(wèn)題?

            因為@Autowired是在ServiceA對象實(shí)例化成功之外,在依賴(lài)注入階段,把實(shí)例注入到成員變量serviceA的。在spring中使用了三級緩存,通過(guò)提前暴露ObjectFactory對象來(lái)解決這個(gè)自己依賴(lài)自己的循環(huán)依賴(lài)問(wèn)題。

            對spring循環(huán)依賴(lài)問(wèn)題有興趣的朋友,可以看看我之前寫(xiě)的一篇文章《》。

            3.4 無(wú)限遞歸

            在日常工作中,我們需要經(jīng)常使用樹(shù)形結構展示數據,比如:分類(lèi)、地區、組織、菜單等功能。

            很多時(shí)候需要從根節點(diǎn)遍歷找到所有葉子節點(diǎn),也需要從葉子節點(diǎn),往上一直追溯到根節點(diǎn)。

            我們以通過(guò)根節點(diǎn)遍歷找到所有葉子節點(diǎn)為例。由于每次需要一層層遍歷查找,而且調用的方法基本相同。為了簡(jiǎn)化代碼,我們一般都會(huì )選擇使用遞歸來(lái)實(shí)現這個(gè)功能。

            這里我們以根據葉子節點(diǎn)找到根節點(diǎn)為例,大致代碼如下:

            public Category findRoot(Long categoryId) {
                Category category = categoryMapper.findCategoryById(categoryId);
                if(null == category) {
                   throw new BusinessException("分類(lèi)不存在");
                }
                Long parentId = category.getParentId();
                if(null == categoryId || 0 == categoryId) {
                   return category;
                }
                return findRoot(parentId);
            }
            

            根據categoryId往上遞歸查找,如果發(fā)現parentId為null或者0的時(shí)候,就是根節點(diǎn)了,這時(shí)直接返回。

            這可能是最普通不過(guò)的遞歸調用了,但是如果有人使壞,或者由于數據庫誤操作,把根節點(diǎn)的parentId改成了二級分類(lèi)的categoryId一樣,比如都改成:1222。這樣遞歸調用會(huì )進(jìn)入無(wú)限循環(huán),最終會(huì )報java.lang.StackOverflowError異常。

            為了避免這種慘案的發(fā)生,其實(shí)是有辦法的。

            可以定義一個(gè)運行遞歸的最大層級MAX_LEVEL,達到了最大層級則直接退出。以上代碼可以做如下調整:

            private static final int MAX_LEVEL = 6;
            
            public Category findRoot(Long categoryId, int level) {
                if(level >= MAX_LEVEL) {
                   return null;
                }
                Category category = categoryMapper.findCategoryById(categoryId);
                if(null == category) {
                   throw new BusinessException("分類(lèi)不存在");
                }
                Long parentId = category.getParentId();
                if(null == categoryId || 0 == categoryId) {
                   return category;
                }
                return findRoot(parentId, ++level);
            }
            

            先定義MAX_LEVEL的值,然后第一次調用遞歸方法的時(shí)候level字段的值傳1,每遞歸一次level的值加1,當發(fā)現level的值大于等于MAX_LEVEL時(shí),說(shuō)明出現了異常情況,則直接返回null。

            我們在寫(xiě)遞歸方法的時(shí)候,要養成好習慣,最好定義一個(gè)最大遞歸層級MAX_LEVEL,防止由于代碼bug,或者數據異常,導致出現無(wú)限遞歸的情況。

            3.5 hashmap

            我們在寫(xiě)代碼時(shí),為了提高效率,使用集合的概率非常大。通常情況下,我們喜歡先把數據收集到集合當中,然后對數據進(jìn)行批處理,比如批量insert或update,提升數據庫操作的性能。

            我們使用比較多的集合有:ArrayList、HashSet、HashMap等。我個(gè)人非常喜歡使用HashMap,特別是在java8中需要嵌套循環(huán)的地方,將其中一層循環(huán)的數據(list或者set)轉換成HashMap,可以減少一層遍歷,提升代碼的執行效率。

            但是如果HashMap使用不當,可能會(huì )出現死循環(huán),怎么回事呢?

            3.5.1 jdk1.7的HashMap

            jdk1.7的HashMap中采用 數組 + 鏈表 的結構存儲數據。在多線(xiàn)程環(huán)境下,同時(shí)往HaspMap中put數據時(shí),會(huì )觸發(fā)resize方法中的transfer方法,進(jìn)行數據重新分配的過(guò)程,需要重新組織鏈表的數據。

            由于采用了頭插法,最終會(huì )形成key3的next等于key7,而key7的next又等于key3的情況,從而構成了死循環(huán)。

            3.5.2 jdk1.8的HashMap

            有了解決jdk1.7擴容時(shí)出現死循環(huán)的問(wèn)題,在jdk1.8中對HashMap進(jìn)行了優(yōu)化,將jdk1.7中的頭插法改成了尾插法,另外采用 數組 + 鏈表 + 紅黑樹(shù) 的結構存儲數據。如果鏈表中元素超過(guò)8個(gè)時(shí),就將鏈表轉化為紅黑樹(shù),以減少查詢(xún)的復雜度,將時(shí)間復雜度降低為O(logN)。

            在多線(xiàn)程環(huán)境下,同時(shí)往HaspMap中put數據時(shí),會(huì )觸發(fā)root方法重新組織樹(shù)形結構的數據。

            在for循環(huán)中會(huì )出現兩個(gè)TreeNode節點(diǎn)的Parent引用都是對方,從而構成死循環(huán)的情況。

            3.5.3 ConcurrentHashMap

            由于在多線(xiàn)程環(huán)境下,使用無(wú)論是jdk1.7,還是jdk1.8的HashMap會(huì )有死循環(huán)的問(wèn)題。所以很多人建議,不用在多線(xiàn)程環(huán)境下,使用HashMap,而應該改用ConcurrentHashMap。

            ConcurrentHashMap是線(xiàn)程安全的,同樣采用了 數組 + 鏈表 + 紅黑樹(shù) 的結構存儲數據,此外還是使用了 cas + 分段鎖,默認是16段鎖,保證并發(fā)寫(xiě)入時(shí),數據不會(huì )產(chǎn)生錯誤。

            在多線(xiàn)程環(huán)境下,同時(shí)往ConcurrentHashMapcomputeIfAbsent數據時(shí),如果里面還有一個(gè)computeIfAbsent,它們的key對應的hashCode是一樣的,這時(shí)就會(huì )產(chǎn)生死循環(huán)。

            意不意外,驚不驚喜?

            幸好這個(gè)bug在jdk1.9中已經(jīng)被Doug Lea修復了。

            3.6 動(dòng)態(tài)代理

            我們在實(shí)際工作中,即使沒(méi)有自己動(dòng)手寫(xiě)過(guò)動(dòng)態(tài)代理程序,但也聽(tīng)過(guò)或者接觸過(guò),因為很多優(yōu)秀的開(kāi)發(fā)框架,它們的底層必定都會(huì )使用動(dòng)態(tài)代理,實(shí)現一些附加的功能。通常情況下,我們使用最多的動(dòng)態(tài)代理是:JDK動(dòng)態(tài)代理Cglib,spring的AOP就是通過(guò)這兩種動(dòng)態(tài)代理技術(shù)實(shí)現的。

            我們在這里以JDK動(dòng)態(tài)代理為例:

            public interface IUser {
                String add();
            }
            public class User implements IUser {
                @Override
                public String add() {
                    System.out.println("===add===");
                    return "success";
                }
            }
            
            public class JdkProxy implements InvocationHandler {
            
                private Object target;
            
                public Object getProxy(Object target) {
                    this.target = target;
                    return Proxy.newProxyInstance(this.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
                }
            
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    before();
                    Object result = method.invoke(target,args);
                    after();
                    return result;
                }
            
                private void before() {
                    System.out.println("===before===");
                }
            
                private void after() {
                    System.out.println("===after===");
                }
            }
            
            public class Test {
                public static void main(String[] args) {
                    User user = new User();
                    JdkProxy jdkProxy = new JdkProxy();
                    IUser proxy = (IUser)jdkProxy.getProxy(user);
                    proxy.add();
                }
            }
            
            

            實(shí)現起來(lái)主要有三步:

            1.實(shí)現某個(gè)具體業(yè)務(wù)接口

            2.InvocationHandler接口,創(chuàng )建調用關(guān)系

            3.使用Proxy創(chuàng )建代理類(lèi),指定被代理類(lèi)的相關(guān)信息

            這樣在調用proxy的add方式時(shí),會(huì )自動(dòng)調用before和after方法,實(shí)現了動(dòng)態(tài)代理的效果,是不是很酷?

            通常情況下,這種寫(xiě)法是沒(méi)有問(wèn)題的,但是如果在invoke方法中調用了proxy對象的toString方法,加了這段代碼:

            proxy.toString();

            程序再次運行,循環(huán)很多次之后,就會(huì )報java.lang.StackOverflowError異常。

            很多人看到這里可能一臉懵逼,到底發(fā)生了什么?

            代理對象本身并沒(méi)有自己的方法,它的所有方法都是基于被代理對象的。通常情況下,如果訪(fǎng)問(wèn)代理對象的方法,會(huì )經(jīng)過(guò)攔截器的invoke方法。但是如果在invoke方法調了代理對象的方法,比如:toString方法,會(huì )經(jīng)過(guò)另外一個(gè)攔截器的invoke方法,如此一直反復調用,最終形成死循環(huán)。

            切記不要在invoke方法中調用代理對象的方法,不然會(huì )產(chǎn)生死循環(huán),坑你于無(wú)形之中。

            3.7 我們自己寫(xiě)的死循環(huán)

            很多朋友看到這個(gè)標題,可能會(huì )質(zhì)疑,我們自己會(huì )寫(xiě)死循環(huán)?

            沒(méi)錯,有些場(chǎng)景我們還真的會(huì )寫(xiě)。

            3.7.1 定時(shí)任務(wù)

            不知道你有沒(méi)有手寫(xiě)過(guò)定時(shí)任務(wù),反正我寫(xiě)過(guò),是非常簡(jiǎn)單的那種(當然復雜的也寫(xiě)過(guò),在這里就不討論了)。如果有個(gè)需求要求每隔5分鐘,從遠程下載某個(gè)文件最新的版本,覆蓋當前文件。

            這時(shí)候,如果你不想用其他的定時(shí)任務(wù)框架,可以實(shí)現一個(gè)簡(jiǎn)單的定時(shí)任務(wù),具體代碼如下:

            public static void downLoad() {
                new Thread(() -> {
                    while (true) {
                        try {
                            System.out.println("download file");
                            Thread.sleep(1000 * 60 * 5);
                        } catch (Exception e) {
                            log.error(e);
                        }
                    }
                }).start();
            }
            

            其實(shí)很多JDK中的定時(shí)任務(wù),比如:Timer類(lèi)的底層,也是用了while(true)的無(wú)限循環(huán)(也就是死循環(huán))來(lái)實(shí)現的。

            3.7.2 生產(chǎn)者消費者

            不知道你有沒(méi)有手寫(xiě)過(guò)生產(chǎn)者和消費者。假設有個(gè)需求需要把用戶(hù)操作日志寫(xiě)入表中,但此時(shí)消費中還沒(méi)有引入消息中間件,比如:kafka等。

            最常規的做法是在接口中同步把日志寫(xiě)入表中,保存邏輯跟業(yè)務(wù)邏輯可能在同一個(gè)事務(wù)中,但為了性能考慮,避免大事務(wù)的產(chǎn)生,一般建議不放在同一個(gè)事務(wù)。

            原本挺好的,但是如果接口并發(fā)量上來(lái)了,為了優(yōu)化接口性能,可能會(huì )把同步寫(xiě)日志到表中的邏輯,拆分出來(lái),做成異步處理的。

            這時(shí)候,就可以手動(dòng)擼一個(gè)生產(chǎn)者消費者解決這個(gè)問(wèn)題了。

            @Data
            public class Car {
                private Integer id;
                private String name;
            }
            @Slf4j
            public class Producer implements Runnable {
            
                private final ArrayBlockingQueue<Car> queue;
            
                public Producer(ArrayBlockingQueue<Car> queue) {
                    this.queue = queue;
                }
            
                @Override
                public void run() {
                    int i = 1;
                    while (true) {
                        try {
                            Car car = new Car();
                            car.setId(i);
                            car.setName("汽車(chē)" + i);
                            queue.put(car);
                            System.out.println("Producer:" + car + ", queueSize:" + queue.size());
                        } catch (InterruptedException e) {
                            log.error(e.getMessage(),e);
                        }
                        i++;
                    }
                }
            }
            
            @Slf4j
            public class Consumer implements Runnable {
            
                private final ArrayBlockingQueue<Car> queue;
            
                public Consumer(ArrayBlockingQueue<Car> queue) {
                    this.queue = queue;
                }
            
                @Override
                public void run() {
                    while (true) {
                        try {
                            Car car = queue.take();
                            System.out.println("Consumer:" + car + ",queueSize:" + queue.size());
                        } catch (InterruptedException e) {
                            log.error(e.getMessage(), e);
                        }
                    }
                }
            }
            
            public class ClientTest {
            
                public static void main(String[] args) {
                    ArrayBlockingQueue<Car> queue = new ArrayBlockingQueue<Car>(20);
                    new Thread(new Producer(queue)).start();
                    new Thread(new Producer(queue)).start();
                    new Thread(new Consumer(queue)).start();
                }
            }
            
            

            由于ArrayBlockingQueue阻塞隊列內部通過(guò)notEmptynotFull 這兩個(gè)Condition實(shí)現了阻塞和喚醒機制,所以我們無(wú)需再做額外控制,用它實(shí)現生產(chǎn)者消費者相對來(lái)說(shuō)要容易多了。

            四、自己寫(xiě)的死循環(huán)要注意什么?

            不知道聰明的小伙伴們有沒(méi)有發(fā)現,我們自定義的定時(shí)任務(wù)生產(chǎn)者消費者例子中,也寫(xiě)了死循環(huán),但跟上面其他的例子都不一樣,我們寫(xiě)的死循環(huán)沒(méi)有出現問(wèn)題,這是為什么?

            定時(shí)任務(wù)中我們用了sleep方法做休眠:Thread.sleep(300000);。

            生產(chǎn)者消費者用了Condition類(lèi)的awaitsignal方法實(shí)現了阻塞和喚醒機制。

            這兩種機制說(shuō)白了,都會(huì )主動(dòng)讓出cpu一段時(shí)間,讓其他的線(xiàn)程有機會(huì )使用cpu資源。這樣cpu有上下文切換的過(guò)程,有一段時(shí)間是處于空閑狀態(tài)的,不會(huì )像其他的列子中一直處于繁忙狀態(tài)。

            一直處于繁忙狀態(tài)才是cpu使用率飆高的真正原因,我們要避免這種情況的產(chǎn)生。

            就像我們平時(shí)騎共享單車(chē)(cpu資源)一樣,我們一般騎1-2小時(shí)就會(huì )歸還了,這樣其他人就有機會(huì )使用這輛共享單車(chē)。但如果有個(gè)人,騎了一個(gè)天還沒(méi)歸還,那么這一天當中自行車(chē)一直處于繁忙之中,其他人就沒(méi)有機會(huì )騎這輛自行車(chē)了。

            到此這篇關(guān)于Java基礎之代碼死循環(huán)詳解的文章就介紹到這了,更多相關(guān)Java代碼死循環(huán)內容請搜索腳本之家以前的文章或繼續瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

            免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng )、來(lái)自本網(wǎng)站內容采集于網(wǎng)絡(luò )互聯(lián)網(wǎng)轉載等其它媒體和分享為主,內容觀(guān)點(diǎn)不代表本網(wǎng)站立場(chǎng),如侵犯了原作者的版權,請告知一經(jīng)查實(shí),將立刻刪除涉嫌侵權內容,聯(lián)系我們QQ:712375056,同時(shí)歡迎投稿傳遞力量。

            久久精品国产亚洲AV高清漫画| 无码精品国产DVD在线观看9久| 国产乱妇无乱码大黄AA片| A级毛片不卡在线播放| 激情无码人妻又粗又大| 亚洲国产AV美女网站|