关于线程你了解多少

线程和进程

线程和进程的概念和区别

概念:
进程:进程是操作系统(资源分配)的最小单位。
线程:线程是(程序执行)的最小单位,组织线程的方式是将PCB对象放到双向链表中。

区别:

 1)进程有独立的堆栈空间和数据段,每启动一个进程,系统就会为它分配地址空间,建立数据表来维护代码段、堆栈段和数据段,这种操作十分昂贵;
 线程是共享进程中的数据,cpu切换一个线程比切换进程花费小,切换速度比进程快,效率高,创建一个线程比创建一个进程成本更低、销毁也是。
 2)通信机制,进程的通信机制相对复杂,譬如信号,管道,共享内存等;而线程由于共享数据段所以通信机制很方便
 
 3)多进程程序更健壮、更安全,因为有独立的地址空间;
    多线程程序只需要一个线程死掉,整个进程都会死掉;而一个进程死掉并不会对另一个进程造成影响,因为进程有自己独立的地址空间。

进程和线程的关系:

 1)一个进程可以有多个线程;一个线程只能属于一个进程
 2)同一个进程中的多个线程之间可以并发执行;一个线程可以创建和撤销另一个线程

线程的基本操作

1、创建线程的五种方式

1)显式继承 Thread

public class ThreadDemo1 {
    //创建线程,重写 run 类,继承 Thread 方法
    static class MyThread extends Thread {
        @Override
        public void run() {   //重写Thread 中的 run方法
            System.out.println("hello world,我是一个线程");
        }
    }

    public static void main(String[] args) {
        //创建线程需要使用 Thread 类,来创建一个 Thread 的实例
        //另一方面还需要给这个线程指定,要执行哪些指令/代码
        //指定指令的方式有很多种方式,此处先用一种最简单的,直接继承Thread类

        //[注意!] 当Thread对象被创建出来时,内核中并没有产生PCB
        Thread t = new MyThread();
        //执行这个 start 方法,才是真的创建出一个线程,此时出现的PCB对应CPU执行上面的run方法
        t.start();

    }
}

2)通过匿名内部类的方式继承 Thread

Thread t = new Thread() {
    @Override
    public void run() {

    }
};
t.start();

3)显式创建一个类,实现 Runnable 接口,然后把这个 Runnable 的实例关联到 Thread 实例上

 static class MyRunnable implements Runnable {
        //Runnable 本质上就是描述了一段要执行的任务代码是啥

        @Override
        public void run() {
            System.out.println("我是一个新线程");
        }
    }
    public static void main(String[] args) {
        Thread t = new Thread(new MyRunnable());
        t.start();
    }

4)通过匿名内部类来实现Runnable 接口

Runnable runnable = new Runnable() {
    @Override
    public void run() {
        System.out.println("我是一个新线程");
    }
};
    Thread t = new Thread(runnable);
    t.start();
}

5)使用lambda 表达式来指定 线程执行的内容

Thread t = new Thread(() -> {
     System.out.println("我是一个新线程");
});
t.start();

2、线程终止的两种判定方法

1)中断线程

public class ThreadDemo1 {
    //对于新线程,run方法执行完成,线程结束;  对于主线程,main方法执行完成,线程结束
    private static boolean isQuit = false;

    public static void main(String[] args) throws InterruptedException {  //sleep可能抛出异常
        Thread t = new Thread() {
            @Override
            public void run() {
                while(!isQuit) {
                    System.out.println("我在继续执行赚钱操作");
                    //防止进程执行太快,加入休眠sleep操作
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("赚钱操作被终止!");
            }
        };
        t.start();

        //sleep作用:让当前线程停下来,这里的5000停5s,不会对其它线程造成影响
        Thread.sleep(5000);
        System.out.println("对方是内鬼,赶快终止交易");
        isQuit = true;
        //run线程 和 主线程 同时执行,循环打印,当程序走到 sleep, run线程 等待5后继续执行
        //此时isQuit = true 不满足循环条件,跳出循环
    }
}

2)Thead.interrupted() 和 Thread.currentThread() 区别

public class ThreadDemo4 {
    public static void main(String[] args) {
        Thread t = new Thread() {
            @Override
            public void run() {
                for(int i = 0; i <10 ; i++) {
                    System.out.println(Thread.interrupted());
                    //运行结果1:true、false、false、false
                    //t.interrupt被调用时,Thead.interrupted()能感知到,只判定一次就把标记位设为false

                    System.out.println(Thread.currentThread().isInterrupted());
                    //运行结果2:true、true、true、true   标记位没有被清除
                }
            }
        };
        t.start();
        t.interrupt();

    }
}

3、多线程并发执行和单线程之间的对比

例:针对一个整数进行大量的循环相加

public class ThreadDemo2 {
    //通过下划线对0进行分割,下划线无任何意义,提高代码可读性
    private static long count  = 100_0000_0000L;

    public static void main(String[] args) {
        serial();  //串行
        concurrency();  //并发
    }

    private static void serial() {
        //记录代码执行时间
        long beg = System.currentTimeMillis();  //记录开始的时间戳
        int a = 0;
        for(long i = 0; i < count; i++) {
            a++;
        }
        int b = 0;
        for(long i = 0; i < count; i++) {
            b++;
        }

        long end = System.currentTimeMillis();  //记录结束的时间戳
        System.out.println("time:" + (end - beg) + "ms");
        //time:28000ms  串行执行是28s
    }

    private static void concurrency() {
        long beg = System.currentTimeMillis();  //记录开始的时间戳
        //创建线程 t1
        Thread t1 = new Thread() {
            //匿名内部类
            //创建了一个没有名字的类,只知道这个类继承自Thread
            //{}中是这个类的具体代码,同时也会 new 出来一个这个类的实例
            @Override
            public void run() {
                int a = 0;
                for(long i = 0; i < count; i++) {
                    a++;
                }
            }
        };
        //创建线程 t2
        Thread t2 = new Thread() {
            @Override
            public void run() {
                int b = 0;
                for(long i = 0; i < count; i++) {
                    b++;
                }
            }
        };
        //启动线程
        t1.start();
        t2.start();
        //线程等待,当主线程等待 t1 和 t2执行结束,然后在执行主线程
        try {
            t1.join();
            t2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //t1 t2 和 main 线程之间都是并发执行的
        //调用 t1.start 和 t2.start 之后,两个线程在计算过程中,此时主线程也会执行,end就随之被计算
        //正确做法:保证 t1 和 t2 都计算完成,再来计算 end 的时间戳
        long end = System.currentTimeMillis();  //记录结束的时间戳
        System.out.println("time:" + (end - beg) + "ms");
        //time:17000ms  并发执行是17s
    }
}

4、Thread 的 run 和 start 之间的区别

public class ThreadDemo3 {
    static class MyThread extends Thread {
        @Override
        public void run() {
            System.out.println("这是一个新线程");
        }
    }

    public static void main(String[] args) {
        Thread t = new MyThread();
        t.run();  //普通方法调用,没有创建新线程,输出语句是在原线程中执行
        t.start();  //创建新线程,由新的线程来执行输出
    }
}

5、线程等待

无线程等待时:

public class ThreadDemo5 {
    public static void main(String[] args) {
        Thread t = new Thread() {
            @Override
            public void run() {
                System.out.println("我是新线程");
            }
        };
        t.start();
        System.out.println("我是主线程");
        //打印结果:可能先打印新线程,也可能先打印主线程
        //解决办法:线程等待,使用 join方法,控制线程结束的先后顺序
        //执行 join方法 的线程会阻塞,一直阻塞到对应线程执行结束后再继续执行
    }
}

开始线程等待:以下程序控制 t1 先执行,再执行 t2

public class ThreadDemo5 {
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread() {
            @Override
            public void run() {
                for(int i = 0; i < 10; i++) {
                    System.out.println("我是线程1");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        };

        Thread t2 = new Thread() {
            @Override
            public void run() {
                for(int i = 0; i < 10; i++) {
                    System.out.println("我是线程2");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        };
        t1.start();
        t1.join();  //join效果是等待线程结束,执行这行代码程序就阻塞了,一直阻塞到t1结束
        t2.start();
        t2.start();
        System.out.println("主线程执行完毕");
    }
}

线程状态

线程一共有哪些状态?

NEW:Thread对象有了,但是PCB还没有.(任务安排了,但是还没开始执行)
RUNNABLE:线程正在CPU上执行,或即将到CPU上执行(PCB在就绪队列中,随时可能被调度到)
WAITING: wait方法导致
TIMED_WAITING: sleep方法导致
BLOCKED: 等待锁导致
(以上三种都表示线程被阻塞,只是阻塞的原因不同)
TERMINATED:Thread对象还在,但是PCB没了

观察以下两个程序状态的转换
观察1、关注 NEW、RUNNABLE、TERMINATED 状态的转换

public class ThreadDemo {
    public static void main(String[] args) {
        //创建线程
        Thread t = new Thread() {
            //重写 run方法
            @Override
            public void run() {
                for (int i = 0; i < 10; i++) {

                }
            }
        };
        System.out.println("线程启动之前:" + t.getState());
        t.start();
        //如果线程存活,就一直打印该状态
        while(t.isAlive()) {
            System.out.println("线程正在运行中:" + t.getState());
        }
        System.out.println("线程结束后:" + t.getState());
    }
}

在这里插入图片描述
观察2、关注 WAITING、BLOCKED、TIMED_WAITING 状态的转换

public class ThreadDemo6 {
    public static void main(String[] args) throws InterruptedException {
        Object object = new Object();

        Thread t = new Thread(() -> {
            synchronized (object) {
                try {
                    object.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                for(int i = 0; i <1000_000; i++) {

                }
                try {
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        System.out.println(t.getState());
        t.start();
        System.out.println(t.getState());
        Thread.sleep(10);
        synchronized (object) {
            for(int i = 0; i > 10; i++) {
                System.out.println(t.getState());
            }
            object.notify();
        }
        while (t.isAlive()) {
            System.out.println(t.getState());
        }
    }
}

在这里插入图片描述
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值