Java学习09-多线程-1

Java学习09-多线程-1

1 Java线程的概念

1.1 程序 进程 线程

  • 程序(program)是为完成特定任务、用某种语言编写的一组指令的集合。即指一段静态的代码,静态对象。
  • 进程(process)是程序的一次执行过程,或是正在运行的一个程序。是一个动态的过程:有它自身的产生、存在和消亡的过程。——生命周期
  • 线程(thread),进程可进一步细化为线程,是一个程序内部的一条执行路径
    若一个进程同一时间 并行执行多个线程,就是支持多线程的。
    线程作为调度和执行的单位,每个线程拥有独立的运行栈和程序计数(pc),线程切换的开销小一个进程中的多个线程共享相同的内存单元/内存地址空间->它们从同一堆中分配对象,可以访问相同的变量和对象。这就使得线程间通信更简便、高效。但多个线程操作共享的系统资源可能就会带来安全的隐患
    在这里插入图片描述

1.2 单核CPU 和多核CPU

  • 单核CPU,其实是一种假的多线程,因为在一个时间单元内,也只能执行一个线程的任务。间单元特别短,因此感觉不出来。
  • 如果是多核的话,才能更好的发挥多线程的效率。
  • 一个Java应用程序java.exe,其实至少有三个线程:main()主线程,gc()垃圾回收线程,异常处理线程。当然如果发生异常,会影响主线程。

1.3使用多线程的优点

  1. 提高应用程序的响应。对图形化界面更有意义(在一个应用中同时做多件事),可增强用户体验。
  2. 提高计算机系统CPU的利用率
  3. 改善程序结构。将既长又复杂的进程分为多个线程,独立运行,利于理解和修改

1.4何时需要多线程

  • 程序需要同时执行两个或多个任务。
  • 程序需要实现一些需要等待的任务时,如用户输入、文件读写操作、网络操作、搜索等。
  • 需要一些后台运行的程序时。

2 线程的创建和使用

Java语言的JVM允许程序运行多个线程,它通过java.lang.Thread类来体现。

2.1 Thread类

2.1.1Thread类的特性:

  • 每个线程都是通过某个特定Thread对象的run()方法来完成操作的,经常把run()方法的主体称为
  • 通过该Thread对象的start()方法来启动这个线程,而非直接调用run()

2.1.2构造器:

  • Thread() :创建新的Thread对象
  • Thread(String threadname): :创建线程并指定线程实例名
  • Thread(Runnable target) :指定创建线程的目标对象,它实现了Runnable接口中的run方法
  • Thread(Runnable target, String name) :创建新的Thread对象

2.1.3Thread类的有关方法:

  • void start(): 启动线程,并执行对象的run()方法
  • run(): 线程在被调度时执行的操作,将创建的线程要执行的操作声明在此方法中
  • String getName(): 返回当前线程的名称
  • void setName(String name):设置该线程名称
  • static Thread currentThread(): 静态方法,返回执行当前代码的线程。在Thread子类中就是this,通常用于主线程和Runnable实现类
  • static void yield() : 线程让步
    暂停当前正在执行的线程,把执行机会让给优先级相同或更高的线程
    若队列中没有同优先级的线程,忽略此方法
  • join() : 在线程A中调用线程B的jion()方法,此时线程A就进入阻塞状态,直到线程B完全执行完以后,线程A才结束阻塞状态。
  • static void sleep(long millis) :(指定时间:毫秒)
    令当前活动线程在指定时间段内放弃对CPU控制,使其他线程有机会被执行,时间到后重排队。
    抛出InterruptedException异常
  • stop(): 强制线程生命期结束,不推荐使用,已过时。
  • boolean isAlive() : 返回boolean,判断线程是否还活着

2.2创建线程的两种方式

2.2.1方式一:继承Thread类

  1. 定义子类继承Thread类。
  2. 子类中重写Thread类中的run方法。–将此线程执行的操作写在run()方法中。
  3. 创建Thread子类对象,即创建了线程对象。
  4. 调用线程对象start()方法:启动线程,调用run方法。
    【例】
//定义子类继承Thread类。
class MyThread extends Thread {
    //子类中重写Thread类中的run方法
    public void run() {
        for (int i = 0; i < 100; i++) {
            if (i % 2 == 0) {
                System.out.println(i);
            }
        }
    }
}

public class ThreadTest {
    public static void main(String[] args) {
        //创建Thread子类对象
        MyThread t1 = new MyThread();
        // 调用线程对象start方法
        t1.start();
        //如下的操作仍然是在main线程中执行的
        for (int i = 0; i < 100; i++) {
            if (i % 2 != 0) {
                System.out.println(i + "*********************");
            }
        }
    }
}
//main()方法主线程开始
//主线程new了一个对象
//主线程调用start()方法
//t1线程开始,遍历100之内的偶数

start()方法的作用:

  1. 启动当前线程
  2. 调用当前线程的run()方法

【注意】

  • 如果自己手动调用run()方法,那么就只是普通方法,没有启动多线程模式。
  • run()方法由JVM调用,什么时候调用,执行的过程控制都有操作系统的CPU调度决定。
  • 想要启动多线程,必须调用start()方法。
  • 一个线程对象只能调用一次start()方法启动,如果重复调用了,则将抛出以上的异常“IllegalThreadStateException”。

【练习】

/**
 * 练习:创建两个分线程,其中一个线程遍历100以内的偶数,另一个线程遍历100以内的奇数
 */

public class ThreadDemo {
    public static void main(String[] args) {
        MyThread1 my1 = new MyThread1();
        MyThread2 my2 = new MyThread2();

        my1.start();
        my2.start();
    }

}

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

class MyThread2 extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            if (i % 2 != 0)
                System.out.println(Thread.currentThread().getName() + ";" + i);
        }
    }
}

2.2.2方式二:实现Runnable 接口(推荐)

  1. 定义子类,实现Runnable接口。
  2. 子类中实现Runnable接口中的抽象run()方法。
  3. 创建实现类的对象。
  4. 将Runnable接口的子类对象作为实际参数传递给Thread类的构造器中。
  5. 调用Thread类的start()方法:开启线程,调用Runnable子类接口的run()方法。
    【例】

//1. 定义子类,实现Runnable接口。
class MThread implements Runnable {
    @Override
    public void run() {
//2. 子类中实现Runnable接口中的抽象run()方法。
        for (int i = 0; i < 100; i++) {
            if (i % 2 == 0)
                System.out.println(Thread.currentThread().getName()+":"+i);
        }
    }
}

public class ThreadTest1 {
    public static void main(String[] args) {
        //3.创建实现类的对象
        MThread mThread = new MThread();
        // 4. 将Runnable接口的子类对象作为实际参数传递给Thread类的构造器中。
        Thread t1 = new Thread(mThread);
        t1.setName("线程1");
        //5. 调用Thread类的start()方法
        t1.start();

        //再启动一个线程,遍历100以内的偶数
        Thread t2 = new Thread(mThread);
        t2.setName("线程2");
        t2.start();//t1和t2共用一个run()
    }
}

2.2.3创建多线程方式的比较

区别:

  • 继承Thread:线程代码存放Thread子类run方法中。
  • 实现Runnable:线程代码存在接口的子类的run方法。
    实现Runnable 接口方式的好处:
  • 避免了单继承的局限性
  • 多个线程可以共享同一个接口实现类的对象,非常适合多个相同线程来处理同一份资源。

2.3线程的调度

线程的优先级:

  1. 线程的优先等级
  • MAX_PRIORITY :10 ----------最高优先级
  • MIN _PRIORITY :1 ------------最低优先级
  • NORM_PRIORITY :5 ----------默认优先级
  1. 涉及的方法
  • getPriority() : :返回线程优先值
  • setPriority(int newPriority) : :改变线程的优先级
  1. 说明
  • 线程创建时继承父线程的优先级
  • 低优先级只是获得调度的概率低,并非一定是在高优先级线程之后才被调用

【例】
1.使用继承Thread方式,多窗口卖票

/**
 * 例:创建三个窗口卖票,总票数为100张
 */
class Window extends Thread {

    private static int ticket = 100;//此处用static的原因:
                                    //如果不用static,那么new了三个对象,每个对象都有100张票,这显然不符合题意
                                    //当将ticket定义为static之后,不管new了多少个对象,这些对象都公用一个ticket,即共用100张票

    @Override
    public void run() {
        while (true) {
            if (ticket > 0) {
                System.out.println(getName() + ":卖票,票号为:" + ticket);
                ticket--;
            }else {
                break;
            }
        }
    }
}

public class WindowTest {
    public static void main(String[] args) {
        Window t1=new Window();
        Window t2=new Window();
        Window t3=new Window();

        t1.setName("窗口1");
        t2.setName("窗口2");
        t3.setName("窗口3");

        t1.start();
        t2.start();
        t3.start();

    }
}
//注:该代码存在线程的安全性问题,该问题将在Java学习09-多线程2中讲解

2.实现Runnable方式,多窗口卖票

/**
 * 例:创建三个窗口卖票,总票数为100张
 */

class Window1 implements Runnable {

    private int ticket = 100;//此处的ticket不用定义为static,因为只nwe了一个Window1,w1作为参数给Thread构造器
                            //t1,t2,t3公用一个ticket

    @Override
    public void run() {
        while (true) {
            if (ticket > 0) {
                System.out.println(Thread.currentThread().getName() + ":卖票,票号为:" + ticket);
                ticket--
            } else {
                break;
            }
        }
    }
}

public class WindowTest1 {
    public static void main(String[] args) {
        Window1 w1 = new Window1();

        Thread t1 = new Thread(w1);
        Thread t2 = new Thread(w1);
        Thread t3 = new Thread(w1);

        t1.setName("窗口1 ");
        t2.setName("窗口2 ");
        t3.setName("窗口3 ");

        t1.start();
        t2.start();
        t3.start();


    }
}
//注:该代码存在线程的安全性问题,该问题将在Java学习09-多线程2中讲解

猜你喜欢

转载自blog.csdn.net/qq_42145862/article/details/89077241