简单的多线程,一句话概括“可以同时调用多个方法”

多线程的学习

1、要学习多线程,就要知道什么是线程

       一、 知道线程是什么,就要先了解进程线程

           1、进程:进程就是一个实例,例如idea就是进程,进程可以开多个或者一个,idea就可以开多个,任务管理器就只能开一个

           2、线程:线程是进程内真正要去执行的操作,比如,要执行一段for循环,真正是由线程需执行的,线程可以有多个,多个线程可以并行或并发的处理方法,以下我们解释什么叫并行并发

      二、了解并行和并发

           1、并发:单核处理器下,线程轮流使用cpu的情况叫做并发, 比如家庭主妇,同一段时间需要洗衣服,洗碗,这就叫并发

           2、并行:多核处理器下、一个线程使用一个cpu的情况叫做并行,比如家庭主妇请了个保姆,可以帮助洗碗,这时,保姆可以洗碗,家庭主妇可以洗衣服,同时做两件事,叫并行

           比如在单核的处理器下,开启了多个线程,执行速度不会比开一个线程块

      三:多线程优缺点

           1、优点:可以同时访问多个方法,不需要再去等待一个方法执行完,再去执行另外一个方法

           2、缺点:多线程是不安全的,原因是java处理数据的机制导致的,以下举例

                   例如在一个线程处理 a = 1的时候java是如何处理的:

                   第一步:将内存中a放在寄存器中

                   第二步:将寄存器中的a + 1,此时也就得到了a = 2

                   第三步:将寄存器中的a返回给内存中 此时比如开启了第二个线程也要将a+1 ,第二个线程也去拿a,例如此时可能线程1正进行到第二步,还没有把a = 2放回到内存中,进程2拿到的值还是1,就会出现数据错误的情况

                  下方代码测试,用两个线程操作将1加到100 测试结果:不用线程正常需要50次,结果是五十多次,以此证明是不安全的:

    public static void main(String[] args) {
        //查看线程执行顺序  结果:执行没有顺序  两个线程操作将1加到100 正常需要50次,结果是五十多次,以此证明是不安全的
        int count = 0;
        AtomicInteger a = new AtomicInteger(1);
        while (true){
            count ++ ;
            new Thread(() -> {
                a.getAndIncrement();
                System.out.println(Thread.currentThread().getName() + " run,加到了:" + a);
            }, "thread2").start();
            new Thread(() -> {
                a.getAndIncrement();
                System.out.println(Thread.currentThread().getName() + " run,加到了:" + a);
            }, "thread3").start();

            if(a.intValue() >= 100){
                break;
            }
        }
        System.out.println("增加100个 操作了 :" + count + "次");
    }

        四:创建线程的方式

             1、匿名内部类方式

public static void main(String[] args) {
        Thread thread1 = new Thread("thread1") {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + " run");

            }
        };
        thread1.start();
 }

              2、Runnable与thread配合的方式创建线程,更灵活,推荐使用

       //第一种 runnable的匿名内部类
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() +  "run");

            }
        };
        Thread thread2 = new Thread(runnable, "thread2");
        thread2.start();

        //第二种 lambda方式  更简洁
        Thread thread3 = new Thread(() ->{
            System.out.println(Thread.currentThread().getName() +  "run");
        }, "thread3");
        thread3.start();

         五:线程的常用方法

                  1、start():线程启动需要用到的方法

                  2、run():项目启动后会调用的方法,如果将start改成run,则线程不会启动,执行后还会是主线程在打印

                  3、join():等待线程结束,等待该线程后才能启用其他线程,以下例子为 thread1睡眠了一秒钟,正常是thread2先执行完,加上join,就会是线程2等待线程1

                  4、join(Long n):等待线程运行结束,最多等待几秒

                  5、sleep(int n):睡眠一段时间,此时时间片会被让出,不会释放锁,睡眠结束后不一定会立即执行,会等待cpu获得到cpu的使用权,也可使用 TimeUnit.SECONDS.sleep(1);方法来睡眠,SECONDS可以定义单位为秒,分等等,更方便使用

                  6、interrupt():增加打断线程标记,如果判断线程正在sleep,wait,join,会导致被打断的线程抛异常,并且清除打断标记。如果打断正在进行中的线程,则会设置打断标记。(关闭时使用)

                  7、isInterrupted():判断当前线程是否被打断,不会清除打断标记(关闭时使用)

                  8、interrupted():判断当前线程是否被打断,会清除打断标记

                  9、setDaemon();设置为守护线程,默认为非守护线程,守护线程会跟着主线程的终止而终止

                  10、yield();让出当前正在执行的线程,并执行其他线程

                  11、getStatus():获取线程中的状态

                   1. 初始(NEW):刚new出来的线程
                   2. 运行(RUNNABLE): 获得了cpu的时间片,正在进行中的线程。注意,没有抢到cpu时间片的线程也是运行状态
                   3. 阻塞(BLOCKED):如果第一个线程获得锁,第二个线程再去获取这个锁的时候就会进去阻塞状态。
                   4. 等待(WAITING):例如join方法、sync锁都会导致线程进入等待状态
                   5. 有时限等待(TIMED_WAITING):例如调用Thread.sleep(1)方法 会导致线程进入超时等待状态

                   6. 终止(TERMINATED):表示该线程已经执行完毕。 

                    

        六:优雅的关闭线程(两阶段终止)

                   错误:使用stop停止线程,会直接将线程杀死,没有机会再去处理线程死亡后的事情

                   正确:使用interrupt方法设置打断标记,isInterrupted判断如果为以打断,则跳出循环,跳出前可以对线程作出处理,此时线程被自然关闭。注意,需要在catch块中也设置打断标记,也就保证了即使异常了也会正常退出线程了

    public static void main(String[] args)  {
        new Thread(() ->{
            Thread thread = Thread.currentThread();
            System.out.println(thread.getName() + "开启线程");

            //循环查看这个线程是否被关闭了
            while (true){
                try {

                    //判断这个线程是否被打断
                    if(thread.isInterrupted()){
                        System.out.println(thread.getName() + " -- 我被打断了");
                        break;
                    }

                    //处理自己的逻辑代码开始
                    Thread.sleep(2000);
                    int a = 1/0; //测试处理业务时出现了异常是否会正常结束线程
                    System.out.println(thread.getName() + "执行业务代码");
                    //处理自己的逻辑代码结束

                    thread.interrupt();
                    System.out.println(thread.getName() + "打断线程");
                }catch (Exception e){
                    e.printStackTrace();
                    //如果出现了异常  调用interrupt方法再次标记为打断状态
                    thread.interrupt();
                }

            }

        },"线程1").start();
    }

         七:线程的查看以及测试方法

                   1、查看线程信息

                      1、  linux操作系统下使用 top -H -p 进程号(PID) ,进程号获取方式:ps -ef | grep 端口

                      2、 java给我们提供了查看线程的工具,在window 下 window + R 输入jconsole 可以查看所有线程状态,提示:需要先打开线程  在输入jconsole

                  2、测试线程

                      1、在开启线程的debug小红点上,右键切换为thread的debug模式 

                            

                       2、之后开启debug运行,在最下方Debug控制台中,找到Debugger下的Frames,可以切换每个线程进行执行

                           

                      

猜你喜欢

转载自blog.csdn.net/qq_38384460/article/details/111143858