多线程

多线程

Java 虚拟机的启动是多线程还是多线程?

由Java命令启动JVM,JVM启动相当于启动了一个进程,接着该进程创建一个主线程去执行main方法。同时会启动的一个垃圾回收进程(避免出现内存溢出),所以至少启动两个线程。

创建线程的两种方法

  • 继承Thread类,重写run方法
public class ThreadDemo01 extends Thread{
    public static void main(String[] args) {
        ThreadDemo01 th1 = new ThreadDemo01();
        ThreadDemo01 th2 = new ThreadDemo01();
        th1.start();
        th2.start();
    }
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread()+"----"+i);
        }
    }
}
  • 实现Runnable接口,实现run()方法
public class ThreadNotifyTest {
    public static void main(String[] args) {
        Thread th = new Thread(new MyThread(),"th1");
        th.start();
    }
}

class MyThread implements Runnable{

    @Override
    public void run() {
    }
}

Run()方法和start()方法的区别

  • run()方法仅仅是封装该线程执行的代码,直接调用时趋同方法;
  • start()方法首先会启动该线程,然后再由JVM去调用该线程的run方法;

线程调度

假设我们的计算机只有一个CPU,那么CPU在一个时刻只能执行一个线程,线程只有得到CPU时间片,也就是使用权,才可以执行指令。线程有两种调度模型:

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

线程生命周期

image-20200616185507053

常用方法

public final void setPriority(int newPriority)  //设置线程优先级
  • 默认优先级是5,优先级范围[1,10],如果超出优先级范围,会抛java.lang.IllegalArgumentException异常;
  • 线程优先级高仅仅表示获取CPU时间片的几率高,优先级高的线程不一定比优先级低的线程优先执行。
public static native void sleep(long millis)  //线程睡眠
  • 在指定的毫秒数内让当前正在执行的线程休眠(暂停执行)。
  • 线程睡眠的过程中,如果是在synchronized线程同步内,是持有锁(监视器对象)的
public final void join() //线程加入
  • 只有当前线程执行完成后,再执行其他线程,其他线程阻塞
  • 可以想象成插队
public class ThreadJoinTest implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 50; i++) {
            System.out.println(Thread.currentThread()+"正在执行");
        }
    }
}
class Test{
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(new ThreadJoinTest());
        thread.start();
        for (int i = 0; i < 20; i++) { //主线程
            if (i==10){
                thread.join(); //当i=10时,线程加入
            }
            System.out.println("main");
        }
    }
}
public static native void yield(); //线程礼让
  • 暂停当前正在执行的线程,但不会造成线程阻塞
  • 线程由运行状态转变为就绪状态
  • 让CPU重新调度,礼让不一定成功
public final void wait(long timeout) //线程等待
  • 该方法是Object中的方法,不属于Thread类方法
  • wait()使当前线程阻塞,前提是 必须先获得锁,一般配合synchronized 关键字使用,一般在synchronized 同步代码块里使用 wait()
  • 当线程执行wait()时,会把当前的锁释放,然后让出CPU,进入阻塞状态,等待其他线程唤醒。
public final void notify() //唤醒线程
  • 唤醒正在等待对象监视器的单个线程。 如果任何线程正在等待这个对象,其中一个被选择被唤醒。 选择是任意的,并且由实施的判断发生。
public final void notifyAll() //唤醒所有线程
  • 唤醒正在等待对象监视器的所有线程
  • 唤醒的线程将无法继续,直到当前线程释放该对象上的锁。
  • 唤醒的线程只能由阻塞状态转换成就绪状态,重新与其他线程争夺CPU时间片

线程停止

  • 使用退出标志,使线程正常退出,也就是当run方法完成后线程终止。
public class ThreadDemo01 extends Thread {
    boolean stop =false;
    @Override
    public void run() {
        while (!stop) {
            System.out.println(getName() + " is running");
            try {
                sleep(1000);
            } catch (InterruptedException e) {
                System.out.println("week up from blcok...");
                stop = true; // 在异常处理代码中修改共享变量的状态
            }
        }
        System.out.println(getName() + " is exiting...");
    }
    public static void main(String[] args) {
        ThreadDemo01 demo01 = new ThreadDemo01();
        demo01.start();
    }
}

  • 使用stop方法强行终止,但是不推荐这个方法,已经过期作废。
  • 使用interrupt方法中断线程。

守护线程

  • 线程分为用户线程和守护线程
  • 虚拟机必须确保用户线程执行完毕(手动创建的线程都是用户线程)
  • 虚拟机不用等待守护线程执行完毕(GC线程,监控内存等)
public class DaemonTest {
    public static void main(String[] args) {
        Poeple poeple = new Poeple();
        God god = new God();

        new Thread(poeple).start();
        Thread thread = new Thread(god);
        thread.setDaemon(true);//设置为守护线程
        thread.start();
    }
}
//用户线程
class Poeple implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 36500; i++) {
            System.out.println("来到人间的第"+i+"天");
        }
        System.out.println("DoodBye,Workd");
    }
}
//守护线程
class God implements Runnable{
    @Override
    public void run() {
        while (true){
            System.out.println("默默守护着你!");
        }
    }
}

sleep()和wait() 的差异

相同点

  • sleep与wait都可以使线程等待,使该线程由运行状态变成阻塞状态

不同点:

  • sleep()方法,属于Thread类中的;而wait()方法,属于Object类中 的方法
  • sleep()方法导致了程序暂停执行指定的时间,让出cpu该其他线程,但是他的监控状态依然保持者,当 指定的时间到了又会自动恢复运行状态。在调用sleep()方法的过程中,线程不会释放对象锁
  • 当调用wait()方法的时候,线程会放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象调用 notify()方法后本线程才进入对象锁定池准备,获取对象锁进入运行状态。
  • wait方法只能在同步块或者同步方法中执行

notify()和notifyAll()的区别

  • notify可能会导致死锁,而notifyAll则不会 任何时候只有一个线程可以获得锁,也就是说只有一个线程可以运行synchronized 中的代码 使用notifyall,可以唤醒 所有处于wait状态的线程,使其重新进入锁的争夺队列中,而notify只能唤醒一个。
  • wait() 应配合while循环使用,不应使用if,务必在wait()调用前后都检查条件,如果不满足,必须调用 notify()唤醒另外的线程来处理,自己继续wait()直至条件满足再往下执行。
posted @ 2020-06-16 21:31  lzhya  阅读(158)  评论(0)    收藏  举报