多线程的概述与创建

线程相关概念

进程

进程试试指运行中的程序,比如使用的QQ,就启动了一个程序,操作系统就会为该进程分配内存空间。当我们使用其他app就又启动了进程,操作系统就为app又分配了一个新的内存空间。

进程是程序的一次执行过程,或是正在运行的一个程序。是动态过程:有自身的产生、存在和消亡的过程。

线程

线程是由进程创建的,是进程的一个实体

一个进程可以拥有多个线程

单线程和多线程

单线程:同一时刻,只运行执行一个线程。

多线程:同一时刻,可以执行多个线程。

并发和并行

并发:同一时刻,多个任务交替执行,造成一种“貌似同时”的错觉,简单理解就是单核CPU实现的多线程就是并发

如:一个CPU对应QQ和vx两个应用,那么系统在运行的时候是交替执行的,只是交替时间短


并行:同一时刻,多个任务同时执行,多核CPU可以实现并行

如:如一个CPU对应一个QQ,另外一个CPU对应VX,那么系统在运行的时候就是两个CPU同时进行

注意:多核CPU可能存在并发和并行都有的情况。

线程的基本使用

创建线程的两种方式:

1、继承Thread类,重写run方法

2、实现Runnable接口,重写run方法

多线程的概述与创建

继承Thread类实现线程的创建

要求:

1、编写一个程序,开启一个线程,该线程每隔1秒。在控制台输出“喵,我是小猫”

2、对上题改进:当输出10次后,结束线程

3、使用Jconsole监控线程执行情况,并画出程序示意图。

多线程的概述与创建

package com.li.thread;

/**
 * @author 喂S别闹
 * @create 2021/11/9-9:13
 * @Version 1.0
 * @Description:通过Thread类实现小猫题
 */
public class Thread1 {
    public static void main(String[] args) throws InterruptedException {
        //创建一个Cat对象
        Cat cat = new Cat();
        cat.start();  //最终执行cat的run方法
        //cat.run()  //run方法就是一个普通的方法  没有开启新的线程,就会自顶向下的执行下去,run执行完毕才继续向下执行
        //当main线程启动一个子线程 Thread-0  主线程不会阻塞,会继续执行
        //这是主线程与子线程是交替执行的
        for (int i = 0; i < 10; i++) {
            System.out.println("主线程:" + i);
            Thread.sleep(1000);
        }
    }

    //当一个类继承了Thread类,那么该类就是一个线程
    //run方法 Thread类是实现了Runnable接口run方法
//    @Override
//    public void run() {
//        if (target != null) {
//            target.run();
//        }
//    }
//    上面的代码是Thread重写Runnable的run方法
    static class Cat extends Thread {
        int times = 0;

        @Override
        public void run() { //重写run,写自己的业务逻辑
            while (true) {
                System.out.println("小猫,喵喵" + (++times) + "线程名:" + Thread.currentThread().getName());
                //休眠1S
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                if (times == 10) {
                    break;
                }
            }
        }
    }
}

注意:start()方法和run方法:

.start(); 最终执行cat的run方法
.run() //run方法就是一个普通的方法 没有开启新的线程,就会自顶向下的执行下去,run执行完毕才继续向下执行


start()方法调用了Start0()方法后,该线程并不一定会立马执行,只是将线程变成了可运行状态。具体什么时候执行,取决于CPU,由CPU统一调度

通过实现Runnable接口线程的创建

1、Java是单继承,在某些情况下一个类可能已经继承了某个父类,在这时用继承Thread类方法来创建线程就不可能了

2、Java设计者提供了另外一个方式创建线程,就是通过实现Runnable接口来创建线程

案例:请编写程序,该程序可以每隔1秒。在控制台输出“hi!”,当输出10次后,自动退出。请使用实现Runnable接口的方式实现. 这是静态代理模式!

public class Runnable1 {
    public static void main(String[] args) {
        Dog dog = new Dog();
        //没有start对象
        //创建了Thread对象,把dog对象实现(Runnable),放入Thread
        Thread thread = new Thread(dog);
        thread.start();
    }

    static class Dog implements Runnable {
        int count = 0;

        @Override
        public void run() {  //普通方法
            while (true) {
                System.out.println("hi!" + (++count) + Thread.currentThread().getName());

                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                if (count == 10) {
                    break;
                }
            }
        }
    }
}

创建2个多线程

案例:编写一个程序,创建两个线程,一个线程每隔1秒输出“Hello World”,输出10次,退出,一个线程每隔1秒输出“Hi”输出5次后退出。

public class Thread2 {
    public static void main(String[] args) {
        T1 t1 = new T1();
        T2 t2 = new T2();
        Thread thread1 = new Thread(t1);
        Thread thread2 = new Thread(t2);
        thread1.start();
        thread2.start();

    }

    static class T1 implements Runnable {
        int count = 0;

        @Override
        public void run() {
            while (true) {
                System.out.println("Hello World" + (++count));
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                if (count == 8) {
                    break;
                }
            }
        }
    }

    static class T2 implements Runnable {
        int count = 0;

        @Override
        public void run() {
            while (true) {

                System.out.println("hi" + (++count));
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                if (count == 6) {
                    break;
                }
            }

        }
    }
}

Thread与Runnable的区别

从Java的设计来说,继承Thread或者实现Runnable接口来创建线程没有本质区别,从JDK文档知道Thread类本身就是实现了Runnable接口

实现Runnable接口方式更加合适多个线程共享一个资源的情况,并且避免了单继承的限制,建议使用Runnable接口


案例:模拟三个售票窗口售票100,分别使用继承Thread和实现Runnable方式,并分析问题

分析:都会被脏读

会卖到102张

public class SellTicket {
    public static void main(String[] args) {
//        SellTicket01 sellTicket01 = new SellTicket01();
//        SellTicket01 sellTicket02 = new SellTicket01();
//        SellTicket01 sellTicket03 = new SellTicket01();
        //出现票数超卖
//        sellTicket01.start();
//        sellTicket02.start();
//        sellTicket03.start();

        SellTicket02 sellTicket02 = new SellTicket02();
        new Thread(sellTicket02).start(); //第一个线程
        new Thread(sellTicket02).start(); //第2个线程
        new Thread(sellTicket02).start(); //第3个线程

    }

    //使用Thread类
    static class SellTicket01 extends Thread {
        private static int Ticketnum = 100;

        @Override
        public void run() {
            while (true) {
                if (Ticketnum <= 0) {
                    System.out.println("售票结束了");
                    break;
                }
                //休眠
                try {
                    Thread.sleep(50);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("窗口 " + Thread.currentThread().getName() + " 卖出了一张票 " + "还剩" + (--Ticketnum) + "张票");
            }
        }
    }

    //实现runnable接口
    static class SellTicket02 implements Runnable {
        private int Ticketnum = 100;

        @Override
        public void run() {
            while (true) {
                if (Ticketnum <= 0) {
                    System.out.println("售票结束了");
                    break;
                }
                //休眠
                try {
                    Thread.sleep(50);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("窗口 " + Thread.currentThread().getName() + " 卖出了一张票 " + "还剩" + (--Ticketnum) + "张票");
            }
        }
    }
}
上一篇:java多线程学习 基础篇(二) Thread类和Runnable接口


下一篇:java 线程二 多线程