Java线程的常用知识

作者: AnonyPer | 来源:发表于2019-03-20 15:16 被阅读8次

    之前梳理了进程和线程的概念以及如何创建、结束进程和线程,本文总结一下线程使用过程中的知识点:

    生命周期及状态变迁

    • 线程的生命周期包括:新建、就绪、运行、阻塞、死亡这几个状态。

    • 当线程被new之后,处于新建状态,并不是立马就被执行了,此时线程被虚拟机分配了内存以及初始化了线程内的变量。

    • 调用thread对象的start方法时,线程处于就绪状态,就绪的线程并不一定立马就被执行,仅仅只是创建了方法栈和计数器,需要等待JVM根据优先级、算法等规则调度之后才会进入运行状态。

    • 线程运行之后,因为前文说过的,多个线程之间通过轮流切换的方式使用CPU资源,所以JVM将CPU资源调度给别的线程后,当前线程就从运行状态转为就绪状态。

    • 当线程调用sleep方法、或者执行一个阻塞时的IO方法且没有返回结果时、调用了suspend(挂起)方法、调用一个同步对象但当前对象正在被其他线程锁持有时,线程就会进入阻塞状态。

    • 上文说到的几种方法,可以使线程进入死亡状态,该线程就结束了。

    状态过程相关注意事项

    • 线程只能调用start方法,不能调用run方法,调用run方法只是将thread对象当作一个普通的对象来运行。
    • 除了新建状态外,其他状态不能再调用start方法,否则会抛出IllegalThreadStateException异常。
    • Thread.sleep()方法会使当前正在被执行的线程睡眠,让CPU可以去启动另一个处于就绪的线程,但是该方法在睡眠期间被唤醒会抛出InterruptedException异常。(上文停止线程也用到该原理)
    • 当时sleep时间结束后、调用的IO方法等待返回结果时、获取同步锁对象时、挂起的线程调用了resume方法时该线程就出于就绪状态,等待CPU调度。
    • 主线程执行完之后,不影响其他线程的运行,每一个线程运行起来后,级别相同。
    • 线程分守护线程普通线程,守护线程时JVM自己使用的线程,如垃圾回收线程,用户创建的线程基本上为普通线程,也可以自动设置Thread对象的setDaemon(true)将线程改为守护线程(在start之前调用)。Thread对象的isDaemon方法可以查询是否时守护线程。
    • 用户创建的线程设置为守护线程后,不管是否执行完,当进程中所有普通线程执行完,整个进程也就结束。
    public class ThreadTest {
        public static void main(String[] args) {
            FutureTask futureTask = new FutureTask(new Callable<Integer>() {
                @Override
                public Integer call() throws Exception {
                    sleep(50000);
                    System.out.println("FutureTask 等待50秒后返回100");
                    return 100;
                }
            });
            Thread thread = new Thread(futureTask, "futureTask");
            thread.setDaemon(true);
            thread.start();
            System.out.println("主线程执行完毕");
        }
    
    }
    
    //执行结果:
    
    主线程执行完毕
    
    Process finished with exit code 0
    
    • Thread对象的setPriority(int newPriority)可以设置线程优先级,范围从0~10,值越大,级别越高,级别越高,执行的机会就越多,不设置的话,默认级别和其父进程级别相同。

    线程sleep和yield方法区别

    • sleep方法会让线程进入阻塞状态,在其睡眠时间内,该线程不会被执行。
    • yield方法只是将线程处于就绪状态,可能调度器又立马调度了该线程。
    • yield方法暂停后,和该线程优先级相同或者高的线程优先执行。
    • sleep之后,其他线程执行的机会有JVM决定,不一定就是高优先级的。
    • yield不会抛出异常,sleep会抛出InterruptedException异常。
    public class ThreadTest {
        public static void main(String[] args) {
            SellRunnable sellRunnable = new SellRunnable();
            Thread thread1 = new Thread(sellRunnable, "1");
            thread1.setPriority(1);
    
            Thread thread2 = new Thread(sellRunnable, "2");
            thread2.setPriority(5);
    
            Thread thread3 = new Thread(sellRunnable, "3");
            thread3.setPriority(5);
            thread2.start();
            thread1.start();
            thread3.start();
        }
    
    }
    
    class SellRunnable implements Runnable {
        //有十张票
        int index = 10;
    
        public synchronized void sell() {
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if(index>=1){
                index--;
                System.out.println("售货窗口:" + Thread.currentThread().getName() + "  卖出了一张票,剩余:" + index);
            }else{
                System.out.println("售货窗口:" + Thread.currentThread().getName() + " 买票时没票了");
            }
    
        }
    
        @Override
        public void run() {
    
            while (index > 0) {
                System.out.println("售货窗口:" + Thread.currentThread().getName() + " 开始买票");
                sell();
            }
    
        }
    }
    
    //执行结果:
    售货窗口:2 开始买票
    售货窗口:3 开始买票
    售货窗口:1 开始买票
    售货窗口:2  卖出了一张票,剩余:9
    售货窗口:2 开始买票
    售货窗口:1  卖出了一张票,剩余:8
    售货窗口:1 开始买票
    售货窗口:3  卖出了一张票,剩余:7
    售货窗口:3 开始买票
    售货窗口:3  卖出了一张票,剩余:6
    售货窗口:3 开始买票
    售货窗口:1  卖出了一张票,剩余:5
    售货窗口:1 开始买票
    售货窗口:2  卖出了一张票,剩余:4
    售货窗口:2 开始买票
    售货窗口:2  卖出了一张票,剩余:3
    售货窗口:2 开始买票
    售货窗口:2  卖出了一张票,剩余:2
    售货窗口:2 开始买票
    售货窗口:2  卖出了一张票,剩余:1
    售货窗口:2 开始买票
    售货窗口:2  卖出了一张票,剩余:0
    售货窗口:1 买票时没票了
    售货窗口:3 买票时没票了
    
    Process finished with exit code 0  //可以看出,2、3执行的相对较多
        
        
    //加入sleep 
    @Override
        public void run() {
    
            while (index > 0) {
                try {
                    Thread.sleep(100);
                    System.out.println("售货窗口:" + Thread.currentThread().getName() + " 开始买票");
                    sell();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    
    //执行结果:
    
    售货窗口:1 开始买票
    售货窗口:2 开始买票
    售货窗口:3 开始买票
    售货窗口:1  卖出了一张票,剩余:9
    售货窗口:3  卖出了一张票,剩余:8
    售货窗口:2  卖出了一张票,剩余:7
    售货窗口:3 开始买票
    售货窗口:2 开始买票
    售货窗口:1 开始买票
    售货窗口:3  卖出了一张票,剩余:6
    售货窗口:1  卖出了一张票,剩余:5
    售货窗口:2  卖出了一张票,剩余:4
    售货窗口:3 开始买票
    售货窗口:2 开始买票
    售货窗口:1 开始买票
    售货窗口:3  卖出了一张票,剩余:3
    售货窗口:1  卖出了一张票,剩余:2
    售货窗口:2  卖出了一张票,剩余:1
    售货窗口:3 开始买票
    售货窗口:2 开始买票
    售货窗口:1 开始买票
    售货窗口:3  卖出了一张票,剩余:0
    售货窗口:1 买票时没票了
    售货窗口:2 买票时没票了
    
    Process finished with exit code 0  // sleep之后,各个窗口差不多
        
    
        
        
    //加入yield
        
    @Override
        public void run() {
    
            while (index > 0) {
                try {
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("售货窗口:" + Thread.currentThread().getName() + " 开始买票");
                sell();
    
                if ("2".equals(Thread.currentThread().getName())) {
                    Thread.yield();
                }
            }
        }
    
    //执行结果:
    
    售货窗口:2 开始买票
    售货窗口:3 开始买票
    售货窗口:1 开始买票
    售货窗口:2  卖出了一张票,剩余:9
    售货窗口:2 开始买票
    售货窗口:1  卖出了一张票,剩余:8
    售货窗口:1 开始买票
    售货窗口:3  卖出了一张票,剩余:7
    售货窗口:1  卖出了一张票,剩余:6
    售货窗口:3 开始买票
    售货窗口:1 开始买票
    售货窗口:3  卖出了一张票,剩余:5
    售货窗口:3 开始买票
    售货窗口:2  卖出了一张票,剩余:4
    售货窗口:2 开始买票
    售货窗口:3  卖出了一张票,剩余:3
    售货窗口:3 开始买票
    售货窗口:1  卖出了一张票,剩余:2
    售货窗口:1 开始买票
    售货窗口:3  卖出了一张票,剩余:1
    售货窗口:3 开始买票
    售货窗口:2  卖出了一张票,剩余:0
    售货窗口:3 买票时没票了
    售货窗口:1 买票时没票了
    
    Process finished with exit code 0 // 2变少,相对3变多(执行太快,yield看不出效果)
    
    

    以上!
    进程与线程的创建和销毁
    线程知识点总结
    线程同步

    相关文章

      网友评论

        本文标题:Java线程的常用知识

        本文链接:https://www.haomeiwen.com/subject/wjazmqtx.html