简单进程与多线程

进程和线程概述

1.进程

​ 在现在操作系统中,运行中的任务通常称为进程(Process)。也就是说:当一个程序进入内存运行,即变成一个进程。进程是出于运行过程中的程序,并且具有一定独立功能。进程是操作系统进行资源分配和调度的一个独立单位。对于Windows操作系统而言,打开资源管理器就可以看到各种进程。

1.进程的特性

1.独立性

​ 进程是操作系统进行资源分配和调度的一个独立单位,每个进程都拥有自己私有的内存空间和系统资源。进程是系统进行资源分配和调度的一个独立单位。在没有经过进程本身允许的情况下,普通用户进程不可直接访问其他进程的地址空间。哪怕在同一台计算机上运行的不同程序,进程之间通信可坑也需要通过网路,独立于进程的按文件来交换数据。例如:QQ 不可以取获取百度网盘的数据。

2.动态性

程序是静态的指令的集合,而进程是在系统中运行的活动的指令集合。也就是说,进程是处于运行过程中的程序。而且,在进程中加入了时间和状态的概念,基础具有自己的生命周期和各种不同状态,这些概念在程序中都是不具备的。

3.并发性

多个线程可以在单处理器上并发执行,多个进程之间不会相互影响。对于单 CPU而言,它在某个时间点上只能执行一个程序,也就是只能执行一个进程,CPU不断的在这些的在这些金星和之间轮换执行。平常,我们为什么可以一边写程序,一边听音乐,一边上网而没有感觉到中断和轮换呢? 这是因为CPU的执行速度相对于我们的感知速度来说实在是太快了,所以我们感觉是同时运行一样。但是当我们启动足够多的程序时,就会名言感觉到运行速度下降的。

2.线程

​ 线程的出现使得进程进程可同时并发处理多个任务;所以,线程也被称为轻量级进程。类似于进程在操作系统地位一样,线程在进程中也是独立的,并发的执行流。当进程被初始化后,主线程(mian线程)就随之被创建。但是,我们可自行创建其他线程。

​ 在现代操作系统中,每个金星和中都可以提供多个线程并发运行。线程才是程序中真正的执行体,进程只是一个应用程序中所有资源的分配单位。

​ 进程中的每个线程可以完成特定的任务。在执行任务的过程中,线程可以拥有自己独立的栈堆,自己的程序计数器和自己的局部变量,但不再拥有系统资源,它与父进程的其他线程共享该进程锁拥有的全部资源。由于线程键的通信时在同一个线程中进行的,所以不需要额外的通信机制,这就使得通信更简便而且信息传递的速度也更快,效果更高。但是,存在安全问题。因为其中一个线程共享的系统资源的操作都会给其他线程带来影响;所以,多线程中的同步是非常重要的问题。

​ 线程的执行也是抢占式的。也就是说,当前运行的线程在任何时候都可能被挂起以便另一个线程可以运行。也就是说;CPU在不同的进程直接按轮换,进程又咋i不同的线程之间轮换;因此,线程是CPU执行和调度的最小单元。

3.RPC

​ 远程进程调用。不同进程之间的调用

小结:

  1. 进程和线程时两个不同层次上的概念,两者的粒度不同。进程时操作系统来管理的,线程则是进程来管理。一个进程可以有多个或者多个线程。
  2. 不同进程的内存都是完全独立的;一个进程内的不同进程则是共享进程的内存和系统资源,不同线程之间有可能发生 并生冲突。
  3. 线程的切换比进程切换的开销要小得多
  4. 一个进程中的所有非守护线程都退出后,进程也会终止
  5. 不同线程之间时并发运行的,它们之间的运行顺序是由操作系统调度的,是随机的![在这里插入图片描述](https://img-blog.csdnimg.cn/75d5bcf222dd45088b42cf112a79fcca.png#pic_center

线程的创建与启动

1.概述

​ Runnable 接口用于定义线程的执行体,其中仅仅声明了一个run() 方法。Thread 实现类Runnable 接口,但是它的run() 方法中没有实现任何东西,需要在Thread 子类实现线程执行体。

在这里插入图片描述

2.创建

1.定义子类继承 Thread 类 重写run() 方法

2.时间Runnable接口 (创建子类实例)

3.利用Callable接口

2.使用 Thread 创建线程

  • 继承
/*
    继承Thread类重写里面的run()方法
    把专业和耗时的操作交给子类做
 */
public class MyThread extends Thread {
    @Override
    public void run() {
//        super.run();
        System.out.println("run run run...");
    }
}

public class Demo01 {
    public static void main(String[] args) {
        System.out.println("---------------------");
        // 创建线程对象
        MyThread myThread = new MyThread();
        // 启动线程,执行run方法
        myThread.start();// 子类做的耗时操作
        System.out.println("XXxxxxxxxxxxxxxxxxxxx");// 自己做的其他事情,与耗时操作先后顺序不一定
    }
}
// 结果

---------------------
XXxxxxxxxxxxxxxxxxxxx
run run run...

小结:

  • 通常把一些耗时的操作火车是专业的操作交给子线程去做。自己还可以做其他事情。
  • 默认的线程也叫主线程,是 main

3.使用 Runnable 创建线程

  • 实现类(接口)
// 实现Runnable接口,创建Thread
public class RunnableImpl implements Runnable {
    @Override
    public void run() {
        System.out.println("run run run...");
    }
}

public class Demo02 {
    public static void main(String[] args) {
        // 获取当前线程的对象 的名字
        String name = Thread.currentThread().getName();
        System.out.println(name);
        // 创建Runnab接口实现类对象
        RunnableImpl runnable = new RunnableImpl();
        // 创建线程Thread对象,有参构造器(Runnable)
        Thread thread = new Thread(runnable);
        thread.start();
        System.out.println("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
    }
}
// 结果
main
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
run run run...

Process finished with exit code 0

4.使用 Callable 创建线程

public class CallableImpl implements Callable<Object> {
    @Override
    public Object call() throws Exception {
        return "call";
    }
}
public class Demo03 {
    public static void main(String[] args) throws Exception{
        //创建 Callable接口实现类,
        CallableImpl callable = new CallableImpl();
        // 创建FutureTask<Object> ,并传入Callabl实现类对象
        FutureTask<Object> futureTask = new FutureTask<>(callable);
        // 创建Thread对象,并传入FutureTask<Object>对象
        Thread thread = new Thread(futureTask);
        // 启动线程
        thread.start();
        // 获取线程的名字
        String name = thread.getName();
        System.out.println(name);
        // 获取第一个子线程看(FutreTask)的返回值
        Object o = futureTask.get();
        System.out.println(o);
    }
}
// 结果
Thread-0
call

Process finished with exit code 0

小结:

  1. Thread 类的run() 方法是一个空方法体,需要在子类中重写方法体,实现处理逻辑
  2. 如果直接调用线程对象的run() 方法,那么JVM 不会作为一个新线程来运行,这只是一个

3.线程的调度

​ Java虚拟机默认采用抢占式调度模型。线程若想被执行必须要得到CPU的使用权。Java虚拟机会按照特定的机制为程序中每个线程分配CPU的使用权,这种机制被称作线程的调度。

​ 常见的线程调度有两种模型:分时调度模型(划分时间片)和抢占式调度模型(优先级)。

4.线程的状态

1.线程状态图

在这里插入图片描述

2.NEW(新建状态)

​ new.

​ 创建线程对象后,该线程对象就处于新建状态。此时线程并不能运行,和其他 Java对象一样,仅仅由JVM为其分配了内存,并没有表现除任何线程的动态特征。

3.RUNNABLE(可运行状态)

​ runnable.

​ 新建状态下的线程对象调用start() 方法后进入可运行状态。但是runnable状态内部又细分为两种状态,且线程可以在这两个状态之间相互转换。

​ 就绪状态:线程对象调用 start() 方法之后等待JVM的调度。注意:此时线程未运行。

​ 运行状态:线程对象获得JVM调度,当失去CPU使用权或调用 yieid 方法时回到就绪。

4.BLOCKED(阻塞状态)

​ blocked.

​ 处于运行状态的线程可能因为有些原因失去 CPU的执行权,展示停止运行进入阻塞状态。一般会在以下两种情况进入阻塞状态:

  1. 当前线程A运行过程中,试图获取同步锁时,却被线程B获取。此时,JVM把线程A存到对象的锁池,线程A就会进入阻塞状态
  2. 当线程运行过程中发出I/O请求时该线程也会进入阻塞状态

​ JVM 不会给已阻塞线程分配CPU,除非线程重新进入就绪状态。而且,阻塞状态的线程只能先进入就绪状态,不能直接进入运行状态。

5.WAITING(等待状态)

​ waiting.

​ 当处于可运行状态的线程调用无参数限制的方法,例如:wait() ,join() 等方法,就会将当前运行中的线程转换为等待状态。

​ 处于等待状态的线程不能立即争夺CPU使用权,必须等待其他线程执行特定的操作后,才有机会再次争夺CPU使用权,将等待状态的线程转换为运行状态。例如:调用 wait() 方法而处于等待状态中的线程,必须等待其他线程调用 notify() 方法唤醒当前等待中的线程。在例如,调用 join() 方法而处于等待状态中的线程,必须等待其他加入的线程终止。

6.TIMED_WAITING(定时等待状态)

​ timed_waiting.

​ 将运行状态中的线程转换为定时等待状态中的线程与转换为等待状态中的线程操作非常类 似,只是运行线程调用了有时间参数限制的方法,如sleep(long millis)、wait( long timeout)、join(long millis)等方法

​ 处于定时等待状态中的线程也不能立即争夺CPU使用权,必须等待其他相关线程执行完特定的操作或者限时时间结束后,才有机会再次争夺CPU使用权,将定时等待状态的线程转换为运行状态。例如,调用了wait( long timeout)方法而处于等待状态中的线程,需要通过其他线程调用notify( )或者notifyAll( )方法唤醒当前等待中的线程。再例如,等待限时时间结束后线程也可以进行状态转换。

7.TERMINATRED(终止状态)

terminatred.

​ 当线程的run( )方法、call()方法正常执行完毕或者线程抛出一个未捕获的异常(Exception)、错误(Error)时线程就进人终止状态。一旦进人终止状态,线程已经消亡;也不能再转换到其他状态。此时,线程的生命周期结束。

线程常用方法

常用API概述

方法声明方法作用
ThreadGrou(String name)创建线程组
getName()返回线程的名字
setName(String name)设置线程对象名
sleep(Int time)线程睡眠指定的毫秒值,到时自动唤醒
getPriority()返回当前线程对象的优先级 默认线程的优先级是5
setPriority(int newPriority)设置线程的优先级
public void interrupt()中断线程
currentThread()返回当前执行的线程对象
isAlive()判断线程是否活动状态
suspend() resume() stop()不再使用方法

sleep(long millis)

​ 暂停当前的线程,进入定时阻塞(等待)状态

​ sleep方法用于暂停当前的线程,进入定时阻塞(等待)状态。在此情况下其它的线程可得到执行的机会。public static void sleep(long millis)方法中输入参数的单位是毫秒

public class MyRunnable2 implements Runnable{
    @Override
    public void run() {
        String name = Thread.currentThread().getName();
        for (int i = 0; i < 100; i++) {
            if (i == 60) {
                try {
                    Thread.sleep(1000*6);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println(name+" "+i);

        }
    }
}

public class Demo01 {
    public static void main(String[] args) {
        MyRunnable2 runnable = new MyRunnable2();
        Thread thread = new Thread(runnable);
        thread.start();
    }
}

join()

如果在某个线程中调用了另一个线程的join() 方法,那么当前线程将进入到等待状态直到join进来的线程执行完它才能继续。所以,join() 方法也被称为加塞方法。

thread.join()表示等待thread执行完成,然后调用线程才会继续执行
thread.join(int time)表示最多等待指定时间,然后调用线程才会继续执行

注意:

​ 该方法次序在其对应的 start 方法之后调用

public class Demo02 {
    public static void main(String[] args) throws InterruptedException {
        MyRunnable2 runnable = new MyRunnable2();
        Thread subThread = new Thread(runnable);
        subThread.start();
        for (int i = 0; i < 50; i++) {
            if (i == 31) {
                subThread.join();
            }
            System.out.println(Thread.currentThread().getName()+" "+i);
        }
    }
}

线程优先级

​ Java 提供一个 线程调度器来监控程序中启动后进入可运行状态的所有线程。多个线程运行时,若线程的优先级相同,由操作系统按时间片轮转方式和独占方式来分配线程的执行时间。

​ 线程调度器按照线程的优先级决定调度那些线程来执行,具有高优先级的线程会在较低优先级的线程之前得到执行。同时线程的调度是抢占式的,即如果当前线程在执行过程中,一个具有更高优先级的线程进入可执行状态,则该高优先级的线程会被立即调度执行。

低优先级Thread.MIN_PRIORITY数值为1 (2~4)
默认优先级Thread. NORM_PRIORITY数值为5
高优先级Thread.MAX_PRIORITY数值为10 (6~9)

​ 线程在被创建后,其缺省的优先级是缺省优先级 Thread>NORM_PRIORITY.

​ 具有相同优先级的多个线程,若它们都为高优先级Thread.MAX_PRIORITY,则每个线程都是独占式的,也就是说这些线程将被顺序执行;若该优先级不为高优先级,则这些线程将同时执行,也就是说这些线程的执行是无序的。

小结:

  1. 线程默认优先级是 5
  2. 线程优先级的范围:1-10
  3. 线程优先级高仅仅表示线程获取的 CPU 时间片的几率高,但是要在次数比较多,或者多次运行的时候才能看到比较好的效果。也就是说:不能保证优先级的线程一致完全占有执行权

Yield(让步,谦让)

​ yield( )方法是一个静态方法,它可以让当前正在执行的线程暂停。该方法和sleep(long mills)非常类似。但是,但它不会让线程进入定时等待状态,它只是将该线程转入就绪状态。也就是说:yield( )只是让当前线程暂停一下,期待系统的线程调度器重新调度一次;在这期间,让优先级与当前线程相同或更高的其它线程能够有机会被调度。但是,也不能百分之百保证其它线程能够被调度;有可能,当某个线程调用了yield方法暂停之后,线程调度器又将其调度出来重新执行。也就是说:该方法只能在一定程度上礼让;即在一定程度上让多个线程的执行更和谐,但是不能靠它保证每个线程轮次执行

守护线程

用户线程(User Thread):或前台线程,默认情况下新建的线程是用户线程

守护线程( Thread):后台线程

当普通线程消失,守护程序也随之消失了(不会立即消失)

方法声明方法作用
setDaemon(boolean on)设置线程为后台线程。必须调用 start() 方法之前设置才有效
isDaemon()测试线程是否为后台程序

结束线程

设置一个标记位,flag。当flag设为false停止

public class Demo03 {
    public static void main(String[] args) {
        //
        // 创建线程,传参Runnable,子线程
        MyRunnable3 runnable  = new MyRunnable3();
        Thread subThread = new Thread(runnable);
        // 设置守护线程isDaemon(子线程)
        subThread.setDaemon(true);
        // 启动子线程
        subThread.start();
        // 主线程程序
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName()+" "+i);
        }
    }
}

线程安全与互斥

同步代码块

​ synchronized(锁 同步监视器){

​ 需要被同步的代码,即多个线程共同操作的数据。

}

public class SellTicket implements Runnable{
    private int ticket = 100;
    private boolean flag = true;
    // 锁 创建锁对象
    private Object lock = new Object();
    @Override
    public void run() {
        while (flag) {
            // 同步代码块 监视器:lock
            // 改进:this:当前对象
            synchronized (this){
                if (ticket > 0) {
                    try {
                        Thread.sleep(200);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    String name = Thread.currentThread().getName();
                    System.out.println(name+"正在出售"+ticket+"票");
                    ticket--;
                }else {
                    flag = false;
                }
            }
        }

    }
    // 同步函数
}

public class Demo01 {
    public static void main(String[] args) {
        // 创建实现接口(Runnable)的实现类 (SellTicket)
        SellTicket runnable = new SellTicket();
        // 创建三个窗口 即三个线程
        Thread t1 = new Thread(runnable,"窗口1");
        Thread t2 = new Thread(runnable,"窗口2");
        Thread t3 = new Thread(runnable,"窗口3");
        // 启动线程
        t1.start();
        t2.start();
        t3.start();
    }
}
// 结果
窗口1正在出售100票
窗口1正在出售99票
窗口1正在出售98票
窗口1正在出售97票
窗口1正在出售96票
窗口1正在出售95票
窗口1正在出售94票
窗口1正在出售93票
窗口1正在出售92票
窗口1正在出售91票
窗口1正在出售90票
窗口1正在出售89票
窗口1正在出售88票
窗口1正在出售87票
窗口1正在出售86票
窗口1正在出售85票
窗口1正在出售84票
窗口1正在出售83票
窗口1正在出售82票
窗口1正在出售81票
窗口1正在出售80票
窗口1正在出售79票
窗口1正在出售78票
窗口1正在出售77票
窗口1正在出售76票
窗口1正在出售75票
窗口1正在出售74票
窗口1正在出售73票
窗口1正在出售72票
窗口1正在出售71票
窗口1正在出售70票
窗口1正在出售69票
窗口1正在出售68票
窗口1正在出售67票
窗口1正在出售66票
窗口1正在出售65票
窗口1正在出售64票
窗口1正在出售63票
窗口1正在出售62票
窗口1正在出售61票
窗口1正在出售60票
窗口1正在出售59票
窗口1正在出售58票
窗口1正在出售57票
窗口1正在出售56票
窗口1正在出售55票
窗口1正在出售54票
窗口1正在出售53票
窗口1正在出售52票
窗口1正在出售51票
窗口1正在出售50票
窗口1正在出售49票
窗口1正在出售48票
窗口1正在出售47票
窗口1正在出售46票
窗口1正在出售45票
窗口1正在出售44票
窗口1正在出售43票
窗口1正在出售42票
窗口1正在出售41票
窗口1正在出售40票
窗口1正在出售39票
窗口1正在出售38票
窗口1正在出售37票
窗口1正在出售36票
窗口1正在出售35票
窗口1正在出售34票
窗口1正在出售33票
窗口1正在出售32票
窗口1正在出售31票
窗口1正在出售30票
窗口1正在出售29票
窗口1正在出售28票
窗口1正在出售27票
窗口1正在出售26票
窗口1正在出售25票
窗口1正在出售24票
窗口1正在出售23票
窗口1正在出售22票
窗口1正在出售21票
窗口1正在出售20票
窗口1正在出售19票
窗口1正在出售18票
窗口1正在出售17票
窗口1正在出售16票
窗口1正在出售15票
窗口1正在出售14票
窗口1正在出售13票
窗口1正在出售12票
窗口1正在出售11票
窗口1正在出售10票
窗口1正在出售9票
窗口1正在出售8票
窗口1正在出售7票
窗口1正在出售6票
窗口1正在出售5票
窗口1正在出售4票
窗口1正在出售3票
窗口1正在出售2票
窗口1正在出售1票

Process finished with exit code 0

同步函数

public synchronized 函数返回值 函数名([参数列表]){

​ 需要被同步的代码…

}

public class SellTicket3 implements Runnable{
    private int ticket = 100;
    private boolean flag = true;
    @Override
    public void run() {
        while (flag) {
            setTicket();
        }
    }
    public synchronized void setTicket(){
        if (ticket > 0) {
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            String name = Thread.currentThread().getName();
            System.out.println(name+"正在出售"+ticket+"票");
            ticket--;
        }
        else {
            flag = false;
        }
    }
}

ReentrantLock

public class SellTicket2 implements Runnable{
    private int ticket = 100;
    private boolean flag = true;
    // 创建锁对象
    private ReentrantLock reentrantLock = new ReentrantLock();
    @Override
    public void run() {
        while (flag) {
            // 加锁
            reentrantLock.lock();
            if (ticket > 0) {
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                String name = Thread.currentThread().getName();
                System.out.println(name+"正在出售"+ticket+"票");
                ticket--;

            }
            // 释放锁
            reentrantLock.unlock();
            if (ticket==0)flag = false;
        }

    }

}

线程死锁

死锁产生的原因

​ 多线程中使用锁会造成效率低,如果出现了同步状态嵌套,就容易产生死锁问题。死锁是指两个或者 两个以上的线程再执行的过程中,因争夺资源产生的一种互相等待现象。

​ 如果两个线程之间有多个共享对象,如果两个线程费别持有对象所需要的共享对象的锁,在试图获取对方线程所持有的共享对象的锁时,就有死锁的危险

​ 画家 和 作家都同时需要 笔和纸。但是,作家先拿到笔,画家先拿到纸。

​ 这时,两者都不愿意率先放弃手中的资源并且希望拿到对方的资源。

public class Pen {
}
public class Paper {
}
public class Resource {
    private Pen pen;
    private Paper paper;

    public Resource() {
        super();
    }

    public Resource(Pen pen, Paper paper) {
        super();
        this.pen = pen;
        this.paper = paper;
    }

    public Pen getPen() {
        return pen;
    }

    public void setPen(Pen pen) {
        this.pen = pen;
    }

    public Paper getPaper() {
        return paper;
    }

    public void setPaper(Paper paper) {
        this.paper = paper;
    }
}
public class Writer extends Thread {
    private Resource resource;// 资源里面有 笔资源 纸资源

    public Writer() {
    }

    public Writer(Resource resource) {
        this.resource = resource;
    }

    @Override
    public void run() {
        Pen pen = resource.getPen();// 锁笔资源对象
        synchronized(pen){
            System.out.println("作家获得笔");

            Paper paper = resource.getPaper();// 锁纸资源对象
            synchronized (paper){
                System.out.println("作家获得纸");
            }
        }
    }
}
public class Painter extends Thread{
    private Resource resource;// 资源:笔资源 纸资源

    public Painter() {
    }

    public Painter(Resource resource) {
        this.resource = resource;
    }

    @Override
    public void run() {
        Paper paper = resource.getPaper();// 锁纸资源对象
        synchronized (paper){
            System.out.println("画家拿到了纸资源");
            Pen pen = resource.getPen();
            synchronized (pen){
                System.out.println("画家拿到笔资源");
            }
        }
    }
}

package com.etime09;

public class Test {
    public static void main(String[] args) {
        Pen pen = new Pen();
        Paper paper = new Paper();
        Resource resource = new Resource(pen,paper);
        Writer writer = new Writer(resource);
        Painter painter = new Painter(resource);
        writer.start();
        painter.start();

    }
}
// 结果
作家获得笔
画家拿到了纸资源

Process finished with exit code -1

线程通信

​ 通过加锁的方式保证共享数据访问的完整性,但是并没有规定线程执行的先后顺序。至于,各线程到底谁先执行由操作系统的调度所决定。在进行多线程程序设计时,还会遇到另一类问题:如何控制互相交互的线程之间的运行顺序,即多线程间的同步。

常用API

​ Java通过Object类的wait、nofify、notifyAll方法实现线程间的通信。由于锁可以是任意对象,所以这些方法都定义在Object类中。

wait( )

使当前线程放弃同步锁并进入到等待,直到其它线程进入此同步锁并调用notify()或notifyAll()方法唤醒该线程为止

notify( )

唤醒此同步锁上等待的第一个调用wait()方法的线程。

notifyAll( )

唤醒此同步锁上调用wait( )方法的所有线程

注意

wait( )、 notify( )、notifyAll( )这三个方法的调用者都应该是同步锁对象;否则JVM抛出IllegalMonitorStateException异常。

public class NumberRunnableImpl implements Runnable{
    private int number = 1;
    // 锁对象
    private Object lock = new Object();
    private boolean flag = true;
    @Override
    public void run() {
        while (flag) {
            synchronized(lock){
                // 唤醒阻塞的线程
                lock.notify();
                if (number <= 10) {
                    String name = Thread.currentThread().getName();
                    System.out.println(name+"正在打印"+number);
                    number++;
                    // 让当前线程等待并且释放锁
                    try {
                        lock.wait();
                        name = Thread.currentThread().getName();
                        System.out.println(name+"完成本次打印");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }else {
                    flag = false;
                }
            }
        }

    }
}

public class Demo01 {
    public static void main(String[] args) {
        NumberRunnableImpl runnable = new NumberRunnableImpl();
        Thread t1 = new Thread(runnable,"线程1");
        Thread t2 = new Thread(runnable,"线程2");
        t1.start();
        t2.start();
    }
}
// 结果
线程1正在打印1
线程2正在打印2
线程1完成本次打印
线程1正在打印3
线程2完成本次打印
线程2正在打印4
线程1完成本次打印
线程1正在打印5
线程2完成本次打印
线程2正在打印6
线程1完成本次打印
线程1正在打印7
线程2完成本次打印
线程2正在打印8
线程1完成本次打印
线程1正在打印9
线程2完成本次打印
线程2正在打印10
线程1完成本次打印
线程2完成本次打印

Process finished with exit code 0

sleep() 和 wait() 的区别

相同点:

两个方法都可以使得当前线程进入阻塞状态。

不同点:

1、 sleep()是Thread的方法;wait()是Object的方法;

2、 sleep()方法可以在任意场景下调用; wait()方法必须在同步代码块或同步方法中调用

3、 sleep()交出线程的执行控制权(即其他线程可抢夺CPU执行权),但是不释放锁;也就是说sleep()方法让出cpu给其他线程,但是它的监控状态依然保持者,当指定的时间到了又会自动恢复运行状态。wait()交出线程的执行控制权(即其他线程可抢夺CPU执行权),同时释放锁。

4、 sleep()和wait()方法都使用在同步代码块或同步方法中时:sleep()不会释放锁,wait()会释放锁。
unnableImpl runnable = new NumberRunnableImpl();
Thread t1 = new Thread(runnable,“线程1”);
Thread t2 = new Thread(runnable,“线程2”);
t1.start();
t2.start();
}
}
// 结果
线程1正在打印1
线程2正在打印2
线程1完成本次打印
线程1正在打印3
线程2完成本次打印
线程2正在打印4
线程1完成本次打印
线程1正在打印5
线程2完成本次打印
线程2正在打印6
线程1完成本次打印
线程1正在打印7
线程2完成本次打印
线程2正在打印8
线程1完成本次打印
线程1正在打印9
线程2完成本次打印
线程2正在打印10
线程1完成本次打印
线程2完成本次打印

Process finished with exit code 0


# sleep() 和 wait() 的区别

**相同点:**

两个方法都可以使得当前线程进入阻塞状态。

**不同点:**

1、 sleep()是Thread的方法;wait()是Object的方法; 

2、 sleep()方法可以在任意场景下调用; wait()方法必须在同步代码块或同步方法中调用

3、 sleep()交出线程的执行控制权(即其他线程可抢夺CPU执行权),但是不释放锁;也就是说sleep()方法让出cpu给其他线程,但是它的监控状态依然保持者,当指定的时间到了又会自动恢复运行状态。wait()交出线程的执行控制权(即其他线程可抢夺CPU执行权),同时释放锁。

4、 sleep()和wait()方法都使用在同步代码块或同步方法中时:sleep()不会释放锁,wait()会释放锁。
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

咸鱼不咸鱼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值