Java基础(29)线程与进程、并发与并行、多线程的三种启动方式、买票案例

1. 进程与线程

1. 进程与线程的概述:

(1)进程:

  • 进程就是正在运行的程序,是系统进行资源分配和调用的独立单位,每一个进程都有他自己的内存空间和系统资源
  • 比如正在运行的应用程序(QQ,微信,QQ音乐)

(2)多进程

  • 现在的计算机系统(Win,Mac,Linux)是支持多进程的,可以同时运行多个程序
  • 我们的计算机在同一个时间点上,是同时执行多个进程吗?不是,我们的(单核CPU)CPU在同一个时间点上只能执行一个进程
  • 你的感觉多个进程在同时执行,因为CPU在某个时间点上只能做一件事情,CPU在多个进程间进行一个高速的切换执行,你的人耳和眼睛根本就感觉不到
  • 多进程的意义:多进程的作用不是提高执行速度,而是提高CPU的使用率

(3)线程

  • 在进程开启后,会执行多个任务,每一个任务就称为一个线程(比如QQ音乐,听歌是一个线程,显示歌词是一个线程)
  • 线程依赖于进程而存在,一个进程中至少有一个线程
  • 线程是程序使用CPU的基本单位。所以,进程是拥有资源的基本单位,线程是CPU调度的基本单位
  • 多线程的意义:多线程的作用不是提高执行速度,而是为了提高应用程序的使用率

2. 并发与并行

1. 并发

  • 指的是多个任务,高速的交替执行
  • 比如说:吃一口饭喝一口水

2. 并行

  • 指的是多个任务同时执行
  • 比如说:边吃饭边打电话

3. Java程序的运行原理

  1. Java命令会启动Java虚拟机(JVM),启动JVM等于启动了一个程序,也就是启动了一个进程
  2. 该进程会自动启动一个"主线程",主线程会去调用某个类中的main方法。所以main方法运行在主线程中
  3. JVM的启动是多线程的吗:JVM启动至少启动了垃圾回收线程和主线程,所以是多线程的

4. 多线程的启动方式1:继承Thread类

1. Java给我们提供好了一个Thread类,通过Thread类创建线程和开启线程

2. 创建新线程的方法

  • 将一个类声明为Thread的子类
  • 该子类重写Thread类中的run()方法
  • 分配并启动该子类的示例

3. 注意事项:

  • run()方法中写的一般是比较耗时的代码
  • 同一个线程在一个程序中不要重复开启,重复开启会抛出异常
public class TestDemo01 {
    public static void main(String[] args) {
        System.out.println("主线程的代码1");
        System.out.println("主线程的代码2");

        System.out.println("当主线程执行到这块的时候,开启多线程");
        MyThread myThread1 = new MyThread();
        myThread1.start();

        System.out.println("myThread后面的代码1");
        System.out.println("myThread后面的代码2");

        System.out.println("在开启一条线程");

        MyThread myThread2 = new MyThread();
        myThread2.start();

        System.out.println("最后的代码1");
        System.out.println("最后的代码2");
    }
}

class MyThread extends Thread{
    /**
     * run()方法里面的代码写的是比较耗时的代码,让子线程来进行
     * 模拟耗时操作
     */
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println(i);
        }
    }
}

4. 获取和设置线程的名称

  • 获取和设置线程名称的方法
	public final String getName()  //获取线程名称
	public final void setName(String name)   //设置线程名称
	public MyThread(String name) {
        super(name);
    }       //通过构造方法直接设置线程名
	public static Thread currentThread()   //获取当前执行的线程
  • 代码示例
public class TestDemo01 {
    public static void main(String[] args) {
        Thread thMain = Thread.currentThread();
        thMain.setName("主线程");
        System.out.println(thMain.getName());

        //多个线程并发执行,多个抢占CPU的执行权,哪个线程抢到,在某一个时刻,就会执行哪个线程。线程的执行具有随机性
        MyThread th1 = new MyThread();
        th1.setName("艾欧尼亚");
        th1.start();

        MyThread th2 = new MyThread();
        th2.setName("诺克萨斯");
        th2.start();

        MyThread th3 = new MyThread();
        th3.setName("战争学院");
        th3.start();
    }
}

class MyThread extends Thread{

    //无参构造
    public MyThread() {
    }
    //有参构造:设置线程名
    public MyThread(String name) {
        super(name);
    }

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            //Thread.currentThread().getName():获取当前线程的名称
            System.out.println(Thread.currentThread().getName() + "--->" + i);
        }
    }
}

5. 代码示例:使用多线程来复制音乐和文本文件

public class TestDemo01 {
    public static void main(String[] args) {
        System.out.println("主线程执行");

        copyMp3Thread th1 = new copyMp3Thread();
        th1.start();

        copyTxtThread th2 = new copyTxtThread();
        th2.start();

        System.out.println("主线程后面的代码1");
        System.out.println("主线程后面的代码2");
        System.out.println("主线程后面的代码3");
    }
}

class copyMp3Thread extends Thread{
    @Override
    public void run() {
        //ctrl+alt+t:快速try/catch
        try {
            FileInputStream fis = new FileInputStream("阿刁_赵雷.mp3");
            FileOutputStream fos = new FileOutputStream("newMp3.mp3");

            byte[] bytes = new byte[1024 * 8];
            int len = 0;
            while ((len = fis.read(bytes)) != -1){
                fos.write(bytes,0,len);
                fos.flush();
            }

            fis.close();
            fos.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

class copyTxtThread extends Thread{
    @Override
    public void run() {
        try {
            BufferedReader br = new BufferedReader(new FileReader("aaa.txt"));
            BufferedWriter bw = new BufferedWriter(new FileWriter("newAaa.txt"));

            String line = null;
            while ((line = br.readLine()) != null){
                bw.write(line);
                bw.newLine();
                bw.flush();
            }

            bw.close();
            br.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

6. 线程调度及设置线程优先级

(1)线程的调度模型:

  • 分时调度模型:所有线程轮流使用CPU的使用权,平均分配每个线程占用的CPU时间片
  • 抢占式调度模型:优先让优先级高的线程使用CPU,如果线程的优先级相同,则随机选取一个执行。线程优先级高的获取CPU时间会多一些
  • Java使用的是抢占式调度模型

(2)获取和设置线程的优先级

	public final int getPriority()   //获取线程的优先级
	public final void setPriority(int newPriority)   //设置线程的优先级
	【注意】
		1.线程优先级共分成1~10,1是最低优先级(Thread.MIN_PRIORITY == 1),10是最高优先级(Thread.MAX_PRIORITY == 10)
		2.线程的默认优先级是5(Thread.NORM_PRIORITY == 5)

(3)注意事项:有的时候我们给线程设置了指定的优先级,但是该线程并不是按照优先级高的线程执行,那是为什么呢?

  • 因为线程的优先级的大小仅仅表示这个线程被CPU执行的概率增大了.但是我们都知道多线程具有随机性
  • 所以有的时候一两次的运行说明不了问题

(4)代码示例

public class TestDemo02 {
    public static void main(String[] args) {
        MyThread th1 = new MyThread();
        MyThread th2 = new MyThread();
        MyThread th3 = new MyThread();

        th1.setName("艾欧尼亚");
        th2.setName("诺克萨斯");
        th3.setName("战争学院");

        //设置线程优先级
        th1.setPriority(Thread.MIN_PRIORITY);
        th2.setPriority(Thread.MAX_PRIORITY);
        th3.setPriority(Thread.NORM_PRIORITY);
        
        //获取线程优先级
        System.out.println(th1.getPriority());   //1
        System.out.println(th2.getPriority());   //10
        System.out.println(th3.getPriority());   //5

        th1.start();
        th2.start();
        th3.start();
    }
}

class MyThread extends Thread{
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName() + "--->" + i);
        }
    }
}

7. 线程控制

(1)睡眠线程:public static void sleep(long millis):设置线程睡眠,单位:毫秒

public class TestDemo01 {
    public static void main(String[] args) throws InterruptedException {
        System.out.println("主线程开启了");
        Thread.sleep(1000);
        System.out.println("主线程接着执行");

        MyThread th1 = new MyThread("A线程");
        MyThread th2 = new MyThread("B线程");

        th1.start();
        th2.start();
    }
}

class MyThread extends Thread{

    public MyThread() {
    }

    public MyThread(String name) {
        super(name);
    }

    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "--->" + i);
        }
    }
}

(2)加入线程:public final void join()

  • 意思就是:等待该线程执行完毕了以后, 其他线程才能再次执行
  • 注意事项:
    在线程启动之后, 在调用方法
    join ()可以让多个线程并发执行,变成串行(挨个排队执行,不用抢)
public class TestDemo01 {
    public static void main(String[] args) throws InterruptedException {
        MyThread th1 = new MyThread("艾欧尼亚");
        MyThread th2 = new MyThread("诺克萨斯");
        MyThread th3 = new MyThread("战争学院");

        //注意:在线程启动之后, 在调用join()方法
        th1.start();
        th1.join();
        th2.start();
        th2.join();
        th3.start();
        th3.join();

        /**
         * 艾欧尼亚--->0
         * 艾欧尼亚--->1
         * 艾欧尼亚--->2
         * 艾欧尼亚--->3
         * 艾欧尼亚--->4
         * 艾欧尼亚--->5
         * 艾欧尼亚--->6
         * 艾欧尼亚--->7
         * 艾欧尼亚--->8
         * 艾欧尼亚--->9
         * 诺克萨斯--->0
         * 诺克萨斯--->1
         * 诺克萨斯--->2
         * 诺克萨斯--->3
         * 诺克萨斯--->4
         * 诺克萨斯--->5
         * 诺克萨斯--->6
         * 诺克萨斯--->7
         * 诺克萨斯--->8
         * 诺克萨斯--->9
         * 战争学院--->0
         * 战争学院--->1
         * 战争学院--->2
         * 战争学院--->3
         * 战争学院--->4
         * 战争学院--->5
         * 战争学院--->6
         * 战争学院--->7
         * 战争学院--->8
         * 战争学院--->9
         */
    }
}

class MyThread extends Thread{

    public MyThread() {
    }

    public MyThread(String name) {
        super(name);
    }

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName() + "--->" + i);
        }
    }
}

(3)礼让线程:public static void yield():暂停当前执行程序,并执行其他的线程【用处不大】

public class TestDemo01 {
    public static void main(String[] args) {
        MyThread th1 = new MyThread("艾欧尼亚");
        MyThread th2 = new MyThread("诺克萨斯");

        th1.start();
        th2.start();
    }
}

class MyThread extends Thread{

    public MyThread() {
    }

    public MyThread(String name) {
        super(name);
    }

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            Thread.yield();
            System.out.println(Thread.currentThread().getName() + "--->" + i);
        }
    }
}

(4)守护线程:public final void setDaemon(boolean on):将该线程标记为守护线程

  • true:开启守护线程。false:关闭守护线程
  • 当正在运行的线程都是守护线程时,JVM退出,守护线程就立马死掉
  • 该方法必须在启动线程前调用
public class TestDemo01 {
    public static void main(String[] args) {
        Thread.currentThread().setName("刘备:主线程");
        for (int i = 0; i < 10000; i++) {
            System.out.println(Thread.currentThread().getName() + "--->" + i);
        }

        MyThread th1 = new MyThread("关羽");
        MyThread th2 = new MyThread("张飞");
        th1.setDaemon(true);
        th2.setDaemon(true);
        th1.start();
        th2.start();

    }
}

class MyThread extends Thread{

    public MyThread() {
    }

    public MyThread(String name) {
        super(name);
    }

    @Override
    public void run() {
        for (int i = 0; i < 1000; i++) {
            System.out.println(Thread.currentThread().getName() + "--->" + i);
        }
    }
}

5. 创建线程的第二种方式:Runnable接口

1. 创建线程的第二种方式
(1)定义一个类实现Runnable接口
(2)重写该接口的run()方法
(3)然后可以分配该类的实例
(4)在创建Thread对象时作为一个参数来传递并启动

2. 这种方式扩展性强,实现一个接口,还可以再去继承其他类

3. 代码示例

public class TestDemo01 {
    public static void main(String[] args) {
        MyRunnable1 myRunnable1 = new MyRunnable1();
        Thread th1 = new Thread(myRunnable1);
        th1.setName("A线程");
        MyRunnable2 myRunnable2 = new MyRunnable2();
        Thread th2 = new Thread(myRunnable2);
        th2.setName("B线程");

        th1.start();
        th2.start();
    }
}

class MyRunnable1 implements Runnable{

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName() + "--->" + i);
        }
    }
}

class MyRunnable2 implements Runnable{

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName() + "--->" + i);
        }
    }
}

6. 创建线程的第三种方式:实现Callable接口

1. 创建线程的第三种方式:
(1)创建一个类实现Callable 接口,重写接口中的call方法
(2)创建一个FutureTask类将Callable接口的子类对象作为参数传进去
(3)创建Thread类, 将FutureTask对象作为参数传进去
(4)开启线程

2. 这种创建线程的方式可以有返回值

3. 代码示例

public class TestDemo01 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        MyCallable myCallable = new MyCallable(100);
        FutureTask<Integer> task = new FutureTask<>(myCallable);
        Thread th = new Thread(task);
        th.start();

        //获取线程的执行结果
        Integer sum = task.get();
        System.out.println(sum);
    }
}

class MyCallable implements Callable<Integer> {
    private int num;

    public MyCallable(int num) {
        this.num = num;
    }

    @Override
    public Integer call() throws Exception {
        //求1~num之间的和
        int sum = 0;
        for (int i = 1; i <= num ; i++) {
            sum += i;
        }
        return sum;
    }
}

7. 买电影票案例

1. 需求:某电影院目前正在上映贺岁大片,共有100张票,而它有3个售票窗口售票,请设计一个程序模拟该电影院售票

2. 继承Thread类的方式实现

public class TestDemo01 {
    public static void main(String[] args) {
        SaleTicketThread th1 = new SaleTicketThread("窗口1");
        SaleTicketThread th2 = new SaleTicketThread("窗口2");
        SaleTicketThread th3 = new SaleTicketThread("窗口3");

        th1.start();
        th2.start();
        th3.start();
    }
}

class SaleTicketThread extends Thread{
    //设置票数,把票数设置为共享变量,让三个线程来共享
    static int ticket = 100;

    public SaleTicketThread(String name) {
        super(name);
    }

    @Override
    public void run() {
        while (true){
            if (ticket >= 1){
                System.out.println(Thread.currentThread().getName() + "正在出售第:" + ticket-- + "张票");
            }
        }
    }
}

3. 实现Runnable接口的方式实现

public class TestDemo02 {
    public static void main(String[] args) {
        SaleTicketRunnable saleTicketRunnable = new SaleTicketRunnable();
        Thread th1 = new Thread(saleTicketRunnable, "窗口1");
        Thread th2 = new Thread(saleTicketRunnable, "窗口2");
        Thread th3 = new Thread(saleTicketRunnable, "窗口3");

        th1.start();
        th2.start();
        th3.start();
    }
}

class SaleTicketRunnable implements Runnable{

    static int ticket = 100;

    @Override
    public void run() {
        while (true){
            if (ticket >= 1){
                System.out.println(Thread.currentThread().getName() + "正在出售第:" + ticket-- + "张票");
            }
        }
    }
}

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值