zoukankan      html  css  js  c++  java
  • Java入门3.1---多线程

    一.程序、进程、线程的概念

    1.基本概念

    1. 程序program为完成特定任务、用某种语言编写的一组指令的集合。即指一段静态的代码,静态对象。
    2. 进程process程序的一次执行过程,是程序在执行过程中分配和管理资源的基本单位,每一个进程都有一个自己的地址空间,至少有 5 种基本状态,它们是:初始态,执行态,等待状态,就绪状态,终止状态
      1. 如:运行中的QQ,运行中的MP3播放器
      2. 程序是静态的,进程的动态的。
    3. 线程thread:线程是CPU调度和分派的基本单位,它可与同属一个进程的其他的线程共享进程所拥有的全部资源。
      1. 线程是进程的一部分,一个线程只能属于一个进程,而一个进程可以有多个线程,但至少有一个线程。

    2.进程与多线程

    2.1 区别

    1. 进程是操作系统资源分配的基本单位,而线程是任务调度和执行的基本单位。
    2. 开销方面:每个进程都有独立的代码和数据空间(程序上下文),程序之间的切换会有较大的开销;线程可以看做轻量级的进程,同一类线程共享代码和数据空间,每个线程都有自己独立的运行栈和程序计数器(PC),线程之间切换的开销小。
    3. 所处环境:在操作系统中能同时运行多个进程(程序);而在同一个进程(程序)中有多个线程同时执行(通过CPU调度,在每个时间片中只有一个线程执行)
    4. 内存分配方面:系统在运行的时候会为每个进程分配不同的内存空间;而对线程而言,除了CPU外,系统不会为线程分配内存(线程所使用的资源来自其所属进程的资源),线程组之间只能共享资源。
    5. 包含关系:没有线程的进程可以看做是单线程的,如果一个进程内有多个线程,则执行过程不是一条线的,而是多条线(线程)共同完成的;线程是进程的一部分,所以线程也被称为轻权进程或者轻量级进程。

    2.2 线程的分类

    Java中的线程分为两类:一种是守护线程,一种是用户线程

    1. 它们在几乎每个方面都是相同的,唯一的区别是判断JVM何时离开。
    2. 守护线程是用来服务用户线程的,通过在start()方法前调用thread.setDaemon(true)可以把一个用户线程变成一个守护线程。
    3. Java垃圾回收就是一个典型的守护线程。
    4. 若JVM中都是守护线程,当前JVM将退出。

    (1)守护线程Daemon Thread(又称内核线程)

     (2)用户线程(ULT)

      有一种线程,它是在后台运行的,它的任务是为其他的线程提供服务,这种线程被称为后台线程(Daemon Thread),又称为守护线程或精灵线程。JVM的垃圾回收线程就是典型的后台线程。后台线程有个特征:如果所有的前台线程都死亡,后台线程会自动死亡。

      系统内核管理线程(KLT),内核保存线程的状态和上下文信息,线程阻塞不会引起进程阻塞。在多处理系统上,多线程在多处理器上并行运行。线程的创建、调度和管理由内核完成,效率比ULT要慢,比进程操作快。

      用户程序实现,不依赖操作系统核心,应用提供创建、同步、调度和管理线程的函数来控制用户线程。不需要用户态/核心态切换,速度快。内核对ULT无感知,线程阻塞则进程(包括它的所有线程)阻塞。

    举例:

    调用Thread对象的setDaemon(true)方法可将指定线程设置成后台线程。下面程序将执行线程设置成后台线程,可以看到当所有的前台线程死亡时,后台线程随之死亡。当整个虚拟机中只剩下后台线程时,程序就没有继续运行的必要了,所以虚拟机也就退出了。

     举例:WPS,QQ等应用
    package 多线程;
    
    public class DaemonThread extends Thread {
        // 定义后台线程的线程执行体与普通线程没有任何区别
        public void run() {
            for (int i = 0; i < 1000; i++) {
                System.out.println(getName() + "" + i);
            }
        }
    
        public static void main(String[] args) {
            DaemonThread t = new DaemonThread();
            // 将此线程设置成后台线程
            t.setDaemon(true);
            // 启动后台线程
            t.start();
            for (int i = 0; i < 10; i++) {
                System.out.println(Thread.currentThread().getName() + "" + i);
            }
            // -----程序执行到此处,前台线程(main线程)结束------
            // 后台线程也应该随之结束
        }
    }
    

    结果:

    main0
    Thread-00
    main1
    Thread-01
    main2
    Thread-02
    main3
    Thread-03
    main4
    Thread-04
    main5
    Thread-05
    main6
    Thread-06
    main7
    main8
    main9
    Thread-07
    Thread-08
    Thread-09
    Thread-010
    Thread-011
    Thread-012
    Thread-013
    Thread-014
    Thread-015
    Thread-016
    Thread-017
    Thread-018
    Thread-019

      上面程序中的t线程设置成后台线程,然后启动该线程,本来该线程应该执行到i等于999时才会结束,但运行程序时不难发现该后台线程无法运行到999,因为当主线程也就是程序中唯一的前台线程运行结束后,JVM会主动退出,因而后台线程也就被结束了。Thread类还提供了一个isDaemon0方法,用于判断指定线程是否为后台线程。

      从上面程序可以看出,主线程默认是前台线程, t线程默认也是前台线程。并不是所有的线程默认都是前台线程,有些线程默认就是后台线程——前台线程创建的子线程默认是前台线程,后台线程创建的子线程默认是后台线程。

      前台线程死亡后,JVM会通知后台线程死亡,但从它接收指令到做出响应,需要一定时间。而且要将某个线程设置为后台线程,必须在该线程启动之前设置,也就是说setDaemon(true)必须在start()方法之前调用,否则会引发IllegaIThreadStateException异常。

     

    2.3 为什么需要设计线程?

      计算机操作系统里面有两个重要概念:并发和隔离。

    1. 并发是为了尽量让硬件利用率高,线程是为了在系统层面做到并发。线程上下文切换效率比进程上下文切换会高很多,这样可以提高并发效率。
    2. 隔离也是并发之后要解决的重要问题,计算机的资源一般是共享的,隔离要能保障崩溃了这些资源能够被回收,不影响其他代码的使用。所以说一个操作系统只有线程没有进程也是可以的,只是这样的系统会经常崩溃而已,操作系统刚开始发展的时候和这种情形很像。

      所以,线程和并发有关系,进程和隔离有关系。线程基本是为了代码并发执行引入的概念,因为要分配cpu时间片,暂停后再恢复要能够继续和没暂停一样继续执行;进程相当于一堆线程加上线程执行过程中申请的资源,一旦挂了,这些资源都要能回收,不影响其他程序。

    2.4 何时需要多线程?

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

    二.Java中多线程的创建和使用

    1.继承Thread类与实现Runnable接口

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

    Thread类的特性:

    1. 每个线程都是通过某个特定Thread对象的run()方法来完成操作的,经常把run()方法的主体称为线程体。
    2. 通过该Thread对象的start()方法来调用这个线程。

    1.1 继承Thread类

    1.2 实现Runnable接口

    package 多线程;
    /**
     * 创建一个子线程,完成1-100之间自然数的输出,同样,让主线程执行同样的操作
     * 方法1:继承java.lang.Thread类
     */
    //第一步:创建继承Thread的子类
    class SubThread extends java.lang.Thread{
        //第二步:重写Thread类的run()方法,方法内实现此子线程要完成的功能
        public void run(){
            for(int i=1;i<=100;i++){
                System.out.println(Thread.currentThread().getName()+":"+i);
            }
        }
    }
    public class TestThread {
        public static void main(String[] args) {
            //第三步:创建子类的对象
            SubThread st1 = new SubThread();
            SubThread st2 = new SubThread();
            //第四步:调用线程的start(),启动此线程,调用相应的run()方法
            st1.start();
            st2.start();
            //一个线程只能执行一次start()
    //        st.run(); //不能通过Thread实现类对象的run()去启动一个线程
            for(int i=1;i<=100;i++){
                System.out.println(Thread.currentThread().getName()+":"+i);
            }
        }
    }
    package 多线程;
    //创建多线程的方式2:通过实现的方式
    //1.创建一个实现了Runnable接口的类
    class PrintNum1 implements Runnable{
        //2.实现接口的方法
        public void run(){
            for(int i=1;i<=100;i++){
                if(i%2 == 0){
                    System.out.println(Thread.currentThread().getName()+":"+i);
                }
            }
        }
    }
    public class TestThread3 {
        public static void main(String[] args) {
            //3.创建一个Runnable接口实现类的对象
            PrintNum1 p = new PrintNum1();
    //        p.start();
    //        p.run();
            //要想启动一个多线程,必须调用start方法()
            //4.将此对象作为形参传递给Thread类的构造器中,创建Thread类的对象,此对象即为一个线程
            Thread t1 = new Thread(p);
            //5.调用start()方法,启动线程并执行run()
            t1.start();//启动线程,执行Thread对象生成时构造器形参的对象的run()方法。
    
            //再创建一个线程
            Thread t2 = new Thread(p);
            t2.start();
        }
    } 
    C:Users
    00568290IdeaProjectsjava_learningoutproductionjava_learning 多线程.TestThread
    main:1
    Thread-0:1
    Thread-1:1
    Thread-0:2
    Thread-0:3
    Thread-0:4
    Thread-0:5
    Thread-0:6
    Thread-0:7
    Thread-0:8
    Thread-0:9
    Thread-0:10
    Thread-0:11
    Thread-0:12
    Thread-0:13
    Thread-0:14
    Thread-0:15
    Thread-0:16
    main:2
    main:3
    main:4
    main:5
    main:6
    Thread-0:17
    Thread-1:2
    Thread-0:18
    Thread-0:19
    Thread-0:20
    Thread-0:21
    Thread-0:22
    Thread-0:23
    Thread-0:24
    Thread-0:25
    Thread-0:26
    Thread-0:27
    Thread-0:28
    main:7
    Thread-0:29
    Thread-1:3
    Thread-0:30
    main:8
    Thread-0:31
    Thread-1:4
    Thread-0:32
    main:9
    Thread-0:33
    Thread-1:5
    Thread-0:34
    main:10
    Thread-0:35
    Thread-1:6
    Thread-0:36
    main:11
    Thread-0:37
    Thread-1:7
    Thread-0:38
    main:12
    Thread-0:39
    Thread-1:8
    Thread-0:40
    main:13
    Thread-0:41
    Thread-1:9
    Thread-0:42
    main:14
    Thread-0:43
    Thread-1:10
    Thread-0:44
    main:15
    Thread-0:45
    Thread-1:11
    Thread-0:46
    main:16
    Thread-0:47
    Thread-1:12
    Thread-0:48
    main:17
    Thread-0:49
    Thread-0:50
    Thread-1:13
    Thread-0:51
    main:18
    Thread-0:52
    Thread-1:14
    Thread-0:53
    Thread-0:54
    Thread-0:55
    Thread-0:56
    Thread-0:57
    Thread-0:58
    main:19
    Thread-0:59
    Thread-0:60
    main:20
    Thread-1:15
    main:21
    Thread-0:61
    main:22
    Thread-0:62
    Thread-0:63
    Thread-1:16
    Thread-0:64
    Thread-1:17
    Thread-0:65
    Thread-0:66
    Thread-0:67
    Thread-0:68
    Thread-0:69
    main:23
    Thread-1:18
    main:24
    Thread-0:70
    main:25
    Thread-1:19
    main:26
    main:27
    main:28
    main:29
    Thread-0:71
    main:30
    Thread-1:20
    main:31
    Thread-0:72
    main:32
    Thread-1:21
    main:33
    Thread-0:73
    main:34
    Thread-1:22
    main:35
    Thread-0:74
    main:36
    Thread-1:23
    main:37
    Thread-0:75
    main:38
    Thread-1:24
    main:39
    Thread-0:76
    main:40
    Thread-1:25
    main:41
    Thread-0:77
    main:42
    Thread-1:26
    main:43
    Thread-0:78
    main:44
    Thread-1:27
    main:45
    Thread-0:79
    main:46
    Thread-1:28
    main:47
    main:48
    main:49
    Thread-0:80
    main:50
    Thread-1:29
    main:51
    Thread-0:81
    main:52
    Thread-1:30
    main:53
    Thread-0:82
    main:54
    Thread-1:31
    Thread-1:32
    main:55
    Thread-1:33
    Thread-0:83
    Thread-1:34
    main:56
    Thread-1:35
    Thread-0:84
    Thread-1:36
    main:57
    main:58
    main:59
    main:60
    Thread-0:85
    Thread-0:86
    Thread-0:87
    Thread-0:88
    Thread-0:89
    Thread-0:90
    main:61
    Thread-1:37
    main:62
    main:63
    main:64
    Thread-0:91
    Thread-0:92
    main:65
    main:66
    main:67
    main:68
    Thread-1:38
    main:69
    Thread-0:93
    main:70
    Thread-1:39
    main:71
    Thread-0:94
    Thread-0:95
    main:72
    Thread-1:40
    main:73
    Thread-0:96
    main:74
    Thread-1:41
    main:75
    Thread-0:97
    main:76
    Thread-1:42
    main:77
    Thread-0:98
    main:78
    Thread-1:43
    main:79
    Thread-0:99
    main:80
    Thread-1:44
    main:81
    Thread-0:100
    main:82
    Thread-1:45
    main:83
    Thread-1:46
    main:84
    Thread-1:47
    Thread-1:48
    Thread-1:49
    Thread-1:50
    main:85
    Thread-1:51
    main:86
    Thread-1:52
    main:87
    Thread-1:53
    main:88
    Thread-1:54
    main:89
    Thread-1:55
    main:90
    Thread-1:56
    Thread-1:57
    Thread-1:58
    Thread-1:59
    Thread-1:60
    main:91
    Thread-1:61
    main:92
    Thread-1:62
    main:93
    Thread-1:63
    Thread-1:64
    main:94
    Thread-1:65
    main:95
    Thread-1:66
    main:96
    Thread-1:67
    main:97
    Thread-1:68
    main:98
    Thread-1:69
    main:99
    Thread-1:70
    main:100
    Thread-1:71
    Thread-1:72
    Thread-1:73
    Thread-1:74
    Thread-1:75
    Thread-1:76
    Thread-1:77
    Thread-1:78
    Thread-1:79
    Thread-1:80
    Thread-1:81
    Thread-1:82
    Thread-1:83
    Thread-1:84
    Thread-1:85
    Thread-1:86
    Thread-1:87
    Thread-1:88
    Thread-1:89
    Thread-1:90
    Thread-1:91
    Thread-1:92
    Thread-1:93
    Thread-1:94
    Thread-1:95
    Thread-1:96
    Thread-1:97
    Thread-1:98
    Thread-1:99
    Thread-1:100
    
    Process finished with exit code 0
    "C:Program FilesJavajdk-14.0.1injava.exe" "-javaagent:D:softwareIntelliJ IDEAIntelliJ IDEA 2020.1libidea_rt.jar=55831:D:softwareIntelliJ IDEAIntelliJ IDEA 2020.1in" -Dfile.encoding=UTF-8 -classpath C:Users
    00568290IdeaProjectsjava_learningoutproductionjava_learning 多线程.TestThread3
    Thread-0:2
    Thread-0:4
    Thread-1:2
    Thread-0:6
    Thread-1:4
    Thread-0:8
    Thread-1:6
    Thread-0:10
    Thread-1:8
    Thread-0:12
    Thread-1:10
    Thread-0:14
    Thread-1:12
    Thread-0:16
    Thread-1:14
    Thread-1:16
    Thread-0:18
    Thread-1:18
    Thread-0:20
    Thread-1:20
    Thread-0:22
    Thread-1:22
    Thread-0:24
    Thread-1:24
    Thread-0:26
    Thread-1:26
    Thread-0:28
    Thread-1:28
    Thread-0:30
    Thread-1:30
    Thread-0:32
    Thread-1:32
    Thread-0:34
    Thread-1:34
    Thread-0:36
    Thread-0:38
    Thread-1:36
    Thread-1:38
    Thread-1:40
    Thread-0:40
    Thread-1:42
    Thread-0:42
    Thread-1:44
    Thread-0:44
    Thread-1:46
    Thread-0:46
    Thread-1:48
    Thread-0:48
    Thread-1:50
    Thread-0:50
    Thread-1:52
    Thread-0:52
    Thread-1:54
    Thread-0:54
    Thread-1:56
    Thread-0:56
    Thread-1:58
    Thread-0:58
    Thread-1:60
    Thread-0:60
    Thread-1:62
    Thread-0:62
    Thread-1:64
    Thread-0:64
    Thread-1:66
    Thread-0:66
    Thread-1:68
    Thread-0:68
    Thread-1:70
    Thread-0:70
    Thread-1:72
    Thread-0:72
    Thread-1:74
    Thread-1:76
    Thread-0:74
    Thread-0:76
    Thread-1:78
    Thread-0:78
    Thread-1:80
    Thread-0:80
    Thread-1:82
    Thread-0:82
    Thread-1:84
    Thread-0:84
    Thread-1:86
    Thread-0:86
    Thread-1:88
    Thread-0:88
    Thread-1:90
    Thread-0:90
    Thread-1:92
    Thread-0:92
    Thread-1:94
    Thread-0:94
    Thread-1:96
    Thread-0:96
    Thread-1:98
    Thread-0:98
    Thread-1:100
    Thread-0:100
    
    Process finished with exit code 0
    注意:启动线程使用start()方法,而不是run()方法。永远不要调用线程对象的run()方法。调用start0方法来启动线程,系统会把该run()方法当成线程执行体来处理;但如果直按调用线程对象的run()方法,则run()方法立即就会被执行,而且在run()方法返回之前其他线程无法并发执行。也就是说,系统把线程对象当成一个普通对象,而run()方法也是一个普通方法,而不是线程执行体。需要指出的是,调用了线程的run()方法之后,该线程已经不再处于新建状态,不要再次调用线程对象的start()方法。只能对处于新建状态的线程调用start()方法,否则将引发IllegaIThreadStateExccption异常。

    继承的方式 VS 实现的方式:

    (1)联系:public class Thread inplements Runnable

    (2)哪个方式好?实现的方式优于继承的方式

    1. 实现的方式避免了java单继承的局限性;
    2. 如果多个线程要操作同一份资源(或数据),更适合使用实现的方式。同时,共享数据所在的类可以作为Runnable接口的实现类。

    使用多线程的好处:

    (1)提高应用程序的响应,对图形化界面更有意义,可增强用户体验。

    (2)提高计算机系统CPU的利用率

    (3)改善程序结构,将既长又复杂的进程分为多个线程,独立进行,利于理解和修改。

    2.Thread类的主要方法

    package 多线程;
    /**
     * 创建一个子线程,完成1-100之间自然数的输出,同样,让主线程执行同样的操作
     * 方法1:继承java.lang.Thread类
     * start():启动线程并执行相应的run()方法
     * run():子线程要执行的代码放入run()方法中
     * currentThread():静态的,调取当前的线程
     * getName():获取此线程的名字
     * setName():设置此线程的名字
     * yield():调用此方法的线程释放当前CPU的执行权
     * join():在A线程中调用B线程的join()方法,表示:当执行到此方法,A线程停止执行,直至B线程执行完毕,A线程再接着join()之后的代码执行。
     * isalive():判断当前线程是否还活着
     * sleep(long l):显示的让当前线程睡眠l毫秒
     * 线程通信:wait() notify() notifyall()
     */
    //第一步:创建继承Thread的子类
    class SubThread extends java.lang.Thread{
        //第二步:重写Thread类的run()方法,方法内实现此子线程要完成的功能
        public void run(){
            for(int i=1;i<=100;i++){
                try {
                    Thread.currentThread().sleep(1000);
                } catch (InterruptedException e){
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+":"+i);
            }
        }
    }
    public class TestThread {
        public static void main(String[] args) {
            //第三步:创建子类的对象
            SubThread st1 = new SubThread();
            st1.setName("子线程1");
            SubThread st2 = new SubThread();
            st2.setName("子线程2");
            //第四步:调用线程的start(),启动此线程,调用相应的run()方法
            st1.start();
            st2.start();
            //一个线程只能执行一次start()
    //        st.run(); //不能通过Thread实现类对象的run()去启动一个线程
            Thread.currentThread().setName("=======主线程");
            for(int i=1;i<=100;i++){
                System.out.println(Thread.currentThread().getName()+":"+i);
    //            if (i % 10 == 0){
    //                Thread.currentThread().yield();
    //            }
                if(i == 20){
                    try{
                        st1.join();
                    }catch (InterruptedException e){
                        e.printStackTrace();
                    }
                }
            }
            System.out.println(st1.isAlive());
        }
    }
    =======主线程:1
    =======主线程:2
    =======主线程:3
    =======主线程:4
    =======主线程:5
    =======主线程:6
    =======主线程:7
    =======主线程:8
    =======主线程:9
    =======主线程:10
    =======主线程:11
    =======主线程:12
    =======主线程:13
    =======主线程:14
    =======主线程:15
    =======主线程:16
    =======主线程:17
    =======主线程:18
    =======主线程:19
    =======主线程:20
    子线程1:1
    子线程2:1
    子线程2:2
    子线程1:2
    子线程1:3
    子线程2:3
    子线程2:4
    子线程1:4
    子线程1:5
    子线程2:5
    子线程2:6
    子线程1:6
    子线程1:7
    子线程2:7
    子线程1:8
    子线程2:8
    子线程2:9
    子线程1:9
    子线程1:10
    子线程2:10
    子线程1:11
    子线程2:11
    子线程1:12
    子线程2:12
    子线程1:13
    子线程2:13
    子线程1:14
    子线程2:14
    子线程1:15
    子线程2:15
    子线程1:16
    子线程2:16
    子线程1:17
    子线程2:17
    子线程1:18
    子线程2:18
    子线程1:19
    子线程2:19
    子线程1:20
    子线程2:20
    子线程1:21
    子线程2:21
    子线程1:22
    子线程2:22
    子线程1:23
    子线程2:23
    子线程1:24
    子线程2:24
    子线程1:25
    子线程2:25
    子线程1:26
    子线程2:26
    子线程1:27
    子线程2:27
    子线程1:28
    子线程2:28
    子线程1:29
    子线程2:29
    子线程1:30
    子线程2:30
    子线程1:31
    子线程2:31
    子线程1:32
    子线程2:32
    子线程1:33
    子线程2:33
    子线程1:34
    子线程2:34
    子线程1:35
    子线程2:35
    子线程1:36
    子线程2:36
    子线程1:37
    子线程2:37
    子线程1:38
    子线程2:38
    子线程1:39
    子线程2:39
    子线程1:40
    子线程2:40
    子线程1:41
    子线程2:41
    子线程1:42
    子线程2:42
    子线程1:43
    子线程2:43
    子线程1:44
    子线程2:44
    子线程1:45
    子线程2:45
    子线程1:46
    子线程2:46
    子线程1:47
    子线程2:47
    子线程1:48
    子线程2:48
    子线程1:49
    子线程2:49
    子线程1:50
    子线程2:50
    子线程1:51
    子线程2:51
    子线程1:52
    子线程2:52
    子线程1:53
    子线程2:53
    子线程1:54
    子线程2:54
    子线程1:55
    子线程2:55
    子线程1:56
    子线程2:56
    子线程1:57
    子线程2:57
    子线程1:58
    子线程2:58
    子线程1:59
    子线程2:59
    子线程1:60
    子线程2:60
    子线程1:61
    子线程2:61
    子线程1:62
    子线程2:62
    子线程1:63
    子线程2:63
    子线程1:64
    子线程2:64
    子线程1:65
    子线程2:65
    子线程1:66
    子线程2:66
    子线程1:67
    子线程2:67
    子线程1:68
    子线程2:68
    子线程1:69
    子线程2:69
    子线程1:70
    子线程2:70
    子线程1:71
    子线程2:71
    子线程1:72
    子线程2:72
    子线程1:73
    子线程2:73
    子线程1:74
    子线程2:74
    子线程1:75
    子线程2:75
    子线程1:76
    子线程2:76
    子线程1:77
    子线程2:77
    子线程1:78
    子线程2:78
    子线程1:79
    子线程2:79
    子线程1:80
    子线程2:80
    子线程1:81
    子线程2:81
    子线程1:82
    子线程2:82
    子线程1:83
    子线程2:83
    子线程1:84
    子线程2:84
    子线程1:85
    子线程2:85
    子线程1:86
    子线程2:86
    子线程1:87
    子线程2:87
    子线程1:88
    子线程2:88
    子线程1:89
    子线程2:89
    子线程1:90
    子线程2:90
    子线程1:91
    子线程2:91
    子线程1:92
    子线程2:92
    子线程1:93
    子线程2:93
    子线程1:94
    子线程2:94
    子线程1:95
    子线程2:95
    子线程1:96
    子线程2:96
    子线程1:97
    子线程2:97
    子线程1:98
    子线程2:98
    子线程1:99
    子线程2:99
    子线程1:100
    =======主线程:21
    =======主线程:22
    =======主线程:23
    =======主线程:24
    =======主线程:25
    =======主线程:26
    =======主线程:27
    =======主线程:28
    =======主线程:29
    =======主线程:30
    =======主线程:31
    =======主线程:32
    =======主线程:33
    =======主线程:34
    =======主线程:35
    =======主线程:36
    =======主线程:37
    =======主线程:38
    =======主线程:39
    =======主线程:40
    =======主线程:41
    =======主线程:42
    =======主线程:43
    =======主线程:44
    =======主线程:45
    =======主线程:46
    =======主线程:47
    =======主线程:48
    =======主线程:49
    =======主线程:50
    =======主线程:51
    =======主线程:52
    =======主线程:53
    =======主线程:54
    =======主线程:55
    =======主线程:56
    =======主线程:57
    =======主线程:58
    =======主线程:59
    =======主线程:60
    =======主线程:61
    =======主线程:62
    =======主线程:63
    =======主线程:64
    =======主线程:65
    =======主线程:66
    =======主线程:67
    =======主线程:68
    =======主线程:69
    =======主线程:70
    =======主线程:71
    =======主线程:72
    =======主线程:73
    =======主线程:74
    =======主线程:75
    =======主线程:76
    =======主线程:77
    =======主线程:78
    =======主线程:79
    =======主线程:80
    =======主线程:81
    =======主线程:82
    =======主线程:83
    =======主线程:84
    =======主线程:85
    =======主线程:86
    =======主线程:87
    =======主线程:88
    =======主线程:89
    =======主线程:90
    =======主线程:91
    =======主线程:92
    =======主线程:93
    =======主线程:94
    =======主线程:95
    =======主线程:96
    =======主线程:97
    =======主线程:98
    =======主线程:99
    =======主线程:100
    false
    子线程2:100


    2.1 start():启动线程并执行相应的run()方法

    2.2 run():子线程要执行的代码放入run()方法中

    2.3 currentThread():静态的,调取当前的线程

    2.4 getName():获取此线程的名字

    2.5 setName():设置此线程的名字

    2.6 yield():调用此方法的线程释放当前CPU的执行权。

      yield()方法是一个和sleep()方法有点相似的方法,它也是Threard类提供的一个静态方法,它也可以让当前正在执行的线程暂停,但它不会阻塞该线程,它只是将该线程转入就绪状态。yield()只是让当前线程暂停一下,让系统的线程调度器重新调度一次,完全可能的情况是:当某个线程调用了yield()方法暂停之后,线程调度器又将其调度出来重新执行。

      实际上,当某个线程调用了yield()方法暂停之后,只有优先级与当前线程相同,或者优先级比当前线程更高的处于就绪状态的线程才会获得执行的机会。下面程序使用yield()方法来让当前正在执行的线程暂停。

    package 多线程;
    
    public class YieldThread extends Thread {
        public YieldThread(String name) {
            super(name);
        }
    
        // 定义run方法作为线程执行体
        public void run() {
            for (int i = 0; i < 30; i++) {
                System.out.println(getName() + "" + i);
                // 当i等于20时,使用yield方法让当前线程让步
                if (i == 2) {
                    Thread.yield();
                }
            }
        }
    
        public static void main(String[] args) throws Exception {
            // 启动两条并发线程
            YieldThread yt1 = new YieldThread("高级");
            // 将ty1线程设置成最高优先级
             yt1.setPriority(Thread.MAX_PRIORITY);
            yt1.start();
            YieldThread yt2 = new YieldThread("低级");
            // 将yt2线程设置成最低优先级
             yt2.setPriority(Thread.MIN_PRIORITY);
            yt2.start();
        }
    }
    高级0
    高级1
    高级2
    低级0
    高级3
    低级1
    高级4
    低级2
    高级5
    低级3
    高级6
    低级4
    高级7
    低级5
    高级8
    低级6
    高级9
    低级7
    高级10
    低级8
    高级11
    低级9
    高级12
    低级10
    高级13
    低级11
    高级14
    低级12
    高级15
    低级13
    高级16
    低级14
    高级17
    低级15
    高级18
    低级16
    高级19
    低级17
    高级20
    低级18
    高级21
    低级19
    高级22
    低级20
    高级23
    低级21
    高级24
    低级22
    高级25
    低级23
    高级26
    低级24
    高级27
    低级25
    高级28
    低级26
    高级29
    低级27
    低级28
    低级29
     

    sleep()方法和yield()方法的区别如下:

    1. sleep()方法暂停当前线程后,会给其他线程执行机会,不会理会其他线程的优先级;但yield()方法只会给优先级相同,或优先级更高的线程执行机会
    2. sleep()方法会将线程转入阻塞状态,直到经过阻塞时间才会转入就绪状态:而yield()不会将线程转入阻塞状态,它只是强制当前线程进入就绪状态。因此完全有可能某个线程调用yield()方法暂停之后,立即再次获得处理器资源被执行
    3. sleep()方法声明抛出了InterruptcdException异常,所以调用sleep()方法时要么捕捉该异常,要么显式声明抛出该异常;而yield()方法则没有声明抛出任何异常
    4. sleep()方法比yield()方法有更好的可移植性,通常不建议使用yield()方法来控制并发线程的执行。

    2.7 join()

    当在某个程序执行流中调用其他线程的join()方法时,调用线程将被阻塞,直到被join()方法加入的join线程执行完为止。join()方法通常由使用线程的程序调用,以将大问题划分成许多小问题,每个小问题分配一个线程。当所有的小问题都得到处理后,再调用主线程来进一步操作。

    举例:

    package 多线程;
    
    public class JoinThread extends Thread {
        // 提供一个有参数的构造器,用于设置该线程的名字
        public JoinThread(String name) {
            super(name);
        }
    
        // 重写run方法,定义线程执行体
        public void run() {
            for (int i = 0; i < 100; i++) {
                System.out.println(getName() + "" + i);
            }
        }
    
        public static void main(String[] args) throws Exception {
            // 启动子线程
            new JoinThread("新线程").start();
            JoinThread jt2 = new JoinThread("线程nxf");
            jt2.start();
            for (int i = 0; i < 100; i++) {
                if (i == 20) {
                    JoinThread jt = new JoinThread("被Join的线程");
                    jt.start();
                    // main线程调用了jt线程的join()方法,main线程
                    // 必须等jt执行结束才会向下执行
                    jt.join();
                }
                System.out.println(Thread.currentThread().getName() + "" + i);
            }
        }
    }
      1 线程nxf0
      2 main0
      3 main1
      4 main2
      5 main3
      6 main4
      7 main5
      8 新线程0
      9 新线程1
     10 新线程2
     11 新线程3
     12 新线程4
     13 新线程5
     14 新线程6
     15 新线程7
     16 新线程8
     17 新线程9
     18 新线程10
     19 新线程11
     20 新线程12
     21 新线程13
     22 新线程14
     23 新线程15
     24 新线程16
     25 新线程17
     26 新线程18
     27 新线程19
     28 新线程20
     29 线程nxf1
     30 线程nxf2
     31 main6
     32 main7
     33 main8
     34 main9
     35 main10
     36 main11
     37 main12
     38 main13
     39 main14
     40 main15
     41 main16
     42 线程nxf3
     43 线程nxf4
     44 线程nxf5
     45 线程nxf6
     46 线程nxf7
     47 线程nxf8
     48 main17
     49 main18
     50 main19
     51 新线程21
     52 新线程22
     53 新线程23
     54 新线程24
     55 新线程25
     56 新线程26
     57 新线程27
     58 新线程28
     59 新线程29
     60 新线程30
     61 新线程31
     62 新线程32
     63 新线程33
     64 新线程34
     65 新线程35
     66 新线程36
     67 新线程37
     68 新线程38
     69 新线程39
     70 新线程40
     71 新线程41
     72 新线程42
     73 新线程43
     74 新线程44
     75 新线程45
     76 新线程46
     77 新线程47
     78 新线程48
     79 新线程49
     80 新线程50
     81 新线程51
     82 新线程52
     83 新线程53
     84 新线程54
     85 新线程55
     86 新线程56
     87 新线程57
     88 新线程58
     89 新线程59
     90 新线程60
     91 新线程61
     92 新线程62
     93 新线程63
     94 新线程64
     95 新线程65
     96 新线程66
     97 新线程67
     98 线程nxf9
     99 新线程68
    100 线程nxf10
    101 新线程69
    102 线程nxf11
    103 新线程70
    104 线程nxf12
    105 新线程71
    106 线程nxf13
    107 新线程72
    108 线程nxf14
    109 新线程73
    110 新线程74
    111 新线程75
    112 新线程76
    113 新线程77
    114 新线程78
    115 新线程79
    116 新线程80
    117 新线程81
    118 新线程82
    119 线程nxf15
    120 线程nxf16
    121 线程nxf17
    122 线程nxf18
    123 线程nxf19
    124 线程nxf20
    125 线程nxf21
    126 线程nxf22
    127 线程nxf23
    128 线程nxf24
    129 线程nxf25
    130 线程nxf26
    131 线程nxf27
    132 线程nxf28
    133 线程nxf29
    134 线程nxf30
    135 新线程83
    136 线程nxf31
    137 被Join的线程0
    138 线程nxf32
    139 新线程84
    140 线程nxf33
    141 被Join的线程1
    142 线程nxf34
    143 新线程85
    144 线程nxf35
    145 被Join的线程2
    146 线程nxf36
    147 新线程86
    148 线程nxf37
    149 被Join的线程3
    150 线程nxf38
    151 新线程87
    152 线程nxf39
    153 被Join的线程4
    154 线程nxf40
    155 新线程88
    156 线程nxf41
    157 被Join的线程5
    158 线程nxf42
    159 新线程89
    160 线程nxf43
    161 被Join的线程6
    162 线程nxf44
    163 新线程90
    164 线程nxf45
    165 新线程91
    166 新线程92
    167 新线程93
    168 新线程94
    169 新线程95
    170 新线程96
    171 新线程97
    172 新线程98
    173 新线程99
    174 被Join的线程7
    175 被Join的线程8
    176 被Join的线程9
    177 被Join的线程10
    178 线程nxf46
    179 线程nxf47
    180 线程nxf48
    181 线程nxf49
    182 线程nxf50
    183 被Join的线程11
    184 线程nxf51
    185 被Join的线程12
    186 线程nxf52
    187 被Join的线程13
    188 线程nxf53
    189 被Join的线程14
    190 线程nxf54
    191 被Join的线程15
    192 线程nxf55
    193 线程nxf56
    194 被Join的线程16
    195 线程nxf57
    196 被Join的线程17
    197 线程nxf58
    198 被Join的线程18
    199 线程nxf59
    200 被Join的线程19
    201 线程nxf60
    202 被Join的线程20
    203 被Join的线程21
    204 被Join的线程22
    205 被Join的线程23
    206 被Join的线程24
    207 被Join的线程25
    208 被Join的线程26
    209 被Join的线程27
    210 被Join的线程28
    211 被Join的线程29
    212 线程nxf61
    213 线程nxf62
    214 线程nxf63
    215 线程nxf64
    216 线程nxf65
    217 线程nxf66
    218 线程nxf67
    219 线程nxf68
    220 线程nxf69
    221 被Join的线程30
    222 线程nxf70
    223 被Join的线程31
    224 线程nxf71
    225 被Join的线程32
    226 线程nxf72
    227 被Join的线程33
    228 线程nxf73
    229 被Join的线程34
    230 线程nxf74
    231 被Join的线程35
    232 被Join的线程36
    233 被Join的线程37
    234 被Join的线程38
    235 被Join的线程39
    236 被Join的线程40
    237 被Join的线程41
    238 被Join的线程42
    239 被Join的线程43
    240 被Join的线程44
    241 被Join的线程45
    242 被Join的线程46
    243 线程nxf75
    244 线程nxf76
    245 线程nxf77
    246 被Join的线程47
    247 被Join的线程48
    248 被Join的线程49
    249 被Join的线程50
    250 被Join的线程51
    251 被Join的线程52
    252 被Join的线程53
    253 被Join的线程54
    254 被Join的线程55
    255 被Join的线程56
    256 被Join的线程57
    257 被Join的线程58
    258 被Join的线程59
    259 被Join的线程60
    260 被Join的线程61
    261 被Join的线程62
    262 被Join的线程63
    263 被Join的线程64
    264 被Join的线程65
    265 被Join的线程66
    266 被Join的线程67
    267 被Join的线程68
    268 被Join的线程69
    269 被Join的线程70
    270 被Join的线程71
    271 被Join的线程72
    272 被Join的线程73
    273 被Join的线程74
    274 被Join的线程75
    275 被Join的线程76
    276 被Join的线程77
    277 被Join的线程78
    278 被Join的线程79
    279 被Join的线程80
    280 被Join的线程81
    281 被Join的线程82
    282 被Join的线程83
    283 被Join的线程84
    284 被Join的线程85
    285 被Join的线程86
    286 被Join的线程87
    287 被Join的线程88
    288 被Join的线程89
    289 被Join的线程90
    290 被Join的线程91
    291 被Join的线程92
    292 被Join的线程93
    293 被Join的线程94
    294 被Join的线程95
    295 被Join的线程96
    296 被Join的线程97
    297 被Join的线程98
    298 被Join的线程99
    299 线程nxf78
    300 线程nxf79
    301 线程nxf80
    302 线程nxf81
    303 线程nxf82
    304 线程nxf83
    305 线程nxf84
    306 线程nxf85
    307 线程nxf86
    308 线程nxf87
    309 线程nxf88
    310 线程nxf89
    311 线程nxf90
    312 线程nxf91
    313 线程nxf92
    314 线程nxf93
    315 线程nxf94
    316 线程nxf95
    317 线程nxf96
    318 线程nxf97
    319 线程nxf98
    320 线程nxf99
    321 main20
    322 main21
    323 main22
    324 main23
    325 main24
    326 main25
    327 main26
    328 main27
    329 main28
    330 main29
    331 main30
    332 main31
    333 main32
    334 main33
    335 main34
    336 main35
    337 main36
    338 main37
    339 main38
    340 main39
    341 main40
    342 main41
    343 main42
    344 main43
    345 main44
    346 main45
    347 main46
    348 main47
    349 main48
    350 main49
    351 main50
    352 main51
    353 main52
    354 main53
    355 main54
    356 main55
    357 main56
    358 main57
    359 main58
    360 main59
    361 main60
    362 main61
    363 main62
    364 main63
    365 main64
    366 main65
    367 main66
    368 main67
    369 main68
    370 main69
    371 main70
    372 main71
    373 main72
    374 main73
    375 main74
    376 main75
    377 main76
    378 main77
    379 main78
    380 main79
    381 main80
    382 main81
    383 main82
    384 main83
    385 main84
    386 main85
    387 main86
    388 main87
    389 main88
    390 main89
    391 main90
    392 main91
    393 main92
    394 main93
    395 main94
    396 main95
    397 main96
    398 main97
    399 main98
    400 main99
    View Code

      

    上面程序中一共有4个线程,主方法开始时就启动了名为"新线程"的子线程,该子线程将会和main线程并发执行。当主线程的循环变量i等于20时启动了名为"被Join的线程"的线程,该线程不会和main线程并发执行。main线程必须等该线程执行结束后才可以向下执行。在名为"被Join的线程"的线程执行时,实际上只有2个子线程并发执行,而主线程处于等待状态。运行上面程序。从上面的运行结果可以看出,主线程执行到i=20时启动,并join了名为"被Join的线程"的线程,所以主线程将一直处于阻塞状态,直到名为"被Join的线程"的线程执行完成。

    2.8 isalive()

      判断当前线程是否还活着。

    1. 当线程处于就绪、运行、阻塞状态时,该方法将返回true;
    2. 当线程处于新建、死亡状态时,该方法将返回false。

    2.9 sleep(long l)

      如果需要让当前正在执行的线程暂停一段时,并进入阻塞状态,则可以通过调用Thread类的静态sleep()方法来实现。当前线程调用sleep()方法进入阻塞状态后,在其睡眠时间段内,该线程不会获得执行的机会,即使系统中没有其他可执行的线程,处于sleep()中的线程也不会执行,因此sleep()方法常用来暂停程序的执行。

    举例:

    package 多线程;
    import java.util.Date;
    
    class ThreadTest extends Thread {
        // 定义后台线程的线程执行体与普通线程没有任何区别
    
        public ThreadTest(String name) {
            super(name);
        }
        public void run() {
            for (int i = 0; i < 10; i++) {
                System.out.println(getName() + "" + i);
            }
        }
    }
    
    public class SleepThread {
        public static void main(String[] args) throws Exception {
            ThreadTest threadXf = new ThreadTest("nxf");
            threadXf.start();
            for (int i = 0; i < 10; i++) {
                System.out.println("当前时间: " + new Date());
                // 调用sleep方法让当前线程暂停1s。
                Thread.sleep(1000);
            }
        }
    }
    nxf0
    nxf1
    nxf2
    nxf3
    nxf4
    nxf5
    nxf6
    nxf7
    nxf8
    nxf9
    当前时间: Wed Jun 03 11:31:10 CST 2020
    当前时间: Wed Jun 03 11:31:11 CST 2020
    当前时间: Wed Jun 03 11:31:12 CST 2020
    当前时间: Wed Jun 03 11:31:13 CST 2020
    当前时间: Wed Jun 03 11:31:14 CST 2020
    当前时间: Wed Jun 03 11:31:15 CST 2020
    当前时间: Wed Jun 03 11:31:16 CST 2020
    当前时间: Wed Jun 03 11:31:17 CST 2020
    当前时间: Wed Jun 03 11:31:18 CST 2020
    当前时间: Wed Jun 03 11:31:19 CST 2020
    下面程序调用sleep()方法来暂停主线程的执行,因为该程序有2个主线程,当主线程进入睡眠后,系统执行另外一个线程,并且该进程在1ms的时间内执行完毕,主线程依次输出10条字符串,输出2条字符串之间的时间间隔为1秒。

    2.10 线程通信

    1. wait()
    2. notify()
    3. notifyall()

    3.线程的调度与设置优先级

    3.1 调度策略

    1. 时间片
    2. 抢占式:高优先级的线程抢占CPU

    3.2 Java的调度方法

    1. 同优先级线程组成先进先出队列(先到先服务),使用时间片策略。
    2. 对高优先级,使用优先调度的抢占式策略。 

    3.3 线程的优先级

    线程的优先级控制(设置优先级表示该线程抢到CPU的概率增大,不保证一定等到该线程执行结束才去执行其他线程。)

    1. MAX_PRIORITY(10);最大是10
    2. MIN_PRIORITY(1);最小是1
    3. NORM_PRIORITY(5); 默认是5

    涉及的方法:

    1. getPriority():返回线程优先级
    2. setPriority(int newPriority):改变线程的优先级
      1. SubThread st1 = new SubThread(); 
        st1.setName("子线程1"); 
        st1.setPriority(Thread.MAX_PRIORITY);
    3. 线程创建时继承父线程的优先级

    三.线程的生命周期

    JDK中用Thread.State枚举表示了线程的几种状态。

    要想实现多线程,必须在主线程中创建新的线程对象。Java语言使用Thread类及其子类的对象来表示线程,在它的一个完整的生命周期中通常要经历如下的五种状态:

    1. 新建:当一个Thread类及其子类的对象被声明并创建时,新生的线程对象处于新建状态;
    2. 就绪:处于新建状态的线程被start()后,将进入线程队列等待CPU时间片,此时它已具备了运行的条件;
    3. 运行:当就绪的线程被调度并获得处理器资源时,便进入运行状态,run()方法定义了线程的操作和功能。
    4. 阻塞:在某种特殊情况下,被人为挂起或执行输入输出操作时,让出CPU并临时中止自己的执行,进入阻塞状态。
    5. 死亡: 线程完成了它的全部工作或线程被提前强制性地中止。

    1.新建

      当程序使用new关键字创建了一个线程之后,该线程就处于新建状态,此时它和其他的Java对象一样,仅仅由Java虚拟机为其分配内存,并初始化其成员变量的值。此时的线程对象没有表现出任何线程的动态特征,程序也不会执行线程的线程执行体。

    2.就绪

      当线程对象调用了start()方法之后,该线程处于就绪状态。Java虚拟机会为其创建方法调用栈和程序计数器,处于这个状态中的线程并没有开始运行,只是表示该线程可以运行了。至于该线程何时开始运行,取决于JVM里线程调度器的调度。

    3.运行--->阻塞

      当发生如下情况时,线程将会进入阻塞状态:

    1. 线程调用sleep()方法主动放弃所占用的处理器资源
    2. 线程调用了一个阻塞式IO方法,在该方法返回之前,该线程被阻塞
    3. 线程试图获得一个同步监视器,但该同步监视器正被其他线程所持有。关于同步监视器的知识、后面将存更深入的介绍
    4. 线程在等待某个通知(notify)
    5. 程序调用了线程的suspend()方法将该线程挂起。但这个方法容易导致死锁,所以应该尽量避免使用该方法

      当前正在执行的线程被阻塞之后,其他线程就可以获得执行的机会。被阻塞的线程会在合适的时候重新进入就绪状态,注意是就绪状态而不是运行状态。也就是说,被阻塞线程的阻塞解除后,必须重新等待线程调度器再次调度它。

    4.阻塞--->就绪

      针对上面几种情况,当发生如下特定的情况时可以解除上面的阻塞,让该线程重新进入就绪状态

    1. 调用sleep()方法的线程经过了指定时间。
    2. 线程调用的阻塞式IO方法已经返回
    3. 线程成功地获得了试图取得的同步监视器
    4. 线程正在等待某个通知时,其他线程发出了个通知
    5. 处于挂起状态的线程被调甩了resume()恢复方法。

      线程从阻塞状态只能进入就绪状态,无法直接进入运行状态。而就绪和运行状态之间的转换通常不受程序控制,而是由系统线程调度所决定。当处于就绪状态的线程获得处理器资源时,该线程进入运行状态;当处于运行状态的线程失去处理器资源时,该线程进入就绪状态。但有一个方法例外,调用yield()方法可以让运行状态的线程转入就绪状态。

    5.死亡

    线程会以如下3种方式结束,结束后就处于死亡状态

    1. run()或call()方法执行完成,线程正常结束。
    2. 线程抛出一个未捕获的Exception或Error
    3. 直接调用该线程stop()方法来结束该线程——该方法容易导致死锁,通常不推荐使用。

    不要试图对一个已经死亡的线程调用start()方法使它重新启动,死亡就是死亡,该线程将不可再次作为线程执行。

    四.线程的同步

    1.线程安全问题存在的原因?

    由于一个线程在操作共享数据过程中,未执行完毕的情况下,另外的线程参与进来,导致共享数据存在了安全问题。

    2.如何来解决线程的安全问题? 

    必须让一个线程操作共享数据完毕之后,其他线程才有机会参与共享数据的操作。

    3.java如何实现线程的安全:线程的同步机制? 

    3.1 方式一:同步代码块

    synchronized(同步监视器){   

      //需要被同步的代码块(即操作共享数据的代码) 

    1.共享数据:多个线程需要共同操作的数据(变量) 。明确哪部分是操作共享数据的代码。

    2.同步监视器():任何一个类的对象充都可以充当锁。哪个线程获取此监视器,谁就执行大括号里被同步的代码。要想保证线程的安全,必须要求所有的线程共用同一把锁。

    3.使用实现Runnable接口的方式创建多线程时,同步代码块中的锁,可以使用this。如果使用继承Thread的方式,慎用this(保证是唯一的一个对象才可以使用this)。

    举例:

    package 多线程;
    //使用实现Runnable接口的方式,售票
    
    class Window2 implements Runnable {
        int ticket = 100;
     // Object obj = new Object();//任何一个类的对象都可以充当锁
    
        public void run() {
          Object obj = new Object();
            while(true) {
    //          synchronized (obj){//在本问题中,this表示w,所以obj可以用this替代
                synchronized (this){
                    if (ticket > 0){
                        try {
                            Thread.currentThread().sleep(10);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println(Thread.currentThread().getName() + "售票,票号为:" + ticket--);
                    }
                }
            }
        }
    }
    public class TestWindow2{
        public static void main(String[] args) {
            Window w = new Window(); // 三个线程共用一个对象
            Thread t1 = new Thread(w);
            Thread t2 = new Thread(w);
            Thread t3 = new Thread(w);
    
            t1.setName("窗口1");
            t2.setName("窗口2");
            t3.setName("窗口3");
    
            t1.start();
            t2.start();
            t3.start();
        }
    }
    class Window extends Thread{
        static int ticket = 100; //公用静态变量
        static Object obj = new Object();
        public void run(){
            while (true){
                synchronized(obj){ //因为有三个对象,这里不能用this
                    if (ticket > 0){
                        try{
                            Thread.currentThread().sleep(10);
                        }catch(InterruptedException e){
                            e.printStackTrace();
                        }
                        System.out.println(Thread.currentThread().getName()+"售票,票号为:"+ticket--);
                    }
                }
            }
        }
    
    public class TestWindow{
        public static void main(String[] args) {
            Window w1 = new Window(); //3个对象
            Window w2 = new Window();
            Window w3 = new Window();
    
            w1.setName("窗口1");
            w2.setName("窗口2");
            w3.setName("窗口3");
    
            w1.start();
            w2.start();
            w3.start();
        }
    }

    3.2 方式二:同步方法

    将操作共享数据的方法声明为synchronized,比如:public synchronized void show(){//操作共享数据的代码}。

    注:

    1. 对于非静态的方法而言,使用同步的话,默认的锁为:this。如果使用继承的方式实现多线程的话,慎用!
    2. 对于静态的方法,如果使用同步,默认的锁为:当前类本身。以单例的懒汉式为例。Class clazz = Singleton.class

    即此方法为同步方法,能够保证当其中一个线程执行此方法时,其他线程在外等待,直至此线程执行完此方法。

    class Window4 implements Runnable{
        int ticket = 100;
        public void run(){
            while(true){
                show();
            }
        }
        public synchronized void show() {
            if (ticket > 0){
                try {
                    Thread.currentThread().sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "售票,票号为:" + ticket--);
            }
            }
    }
    
    public class SynchronizedThread2{
        public static void main(String[] args) {
            Window4 w = new Window4(); // 三个线程共用一个对象
            Thread t1 = new Thread(w);
            Thread t2 = new Thread(w);
            Thread t3 = new Thread(w);
    
            t1.setName("窗口1");
            t2.setName("窗口2");
            t3.setName("窗口3");
    
            t1.start();
            t2.start();
            t3.start();
        }
    }
    采用继承的方式时,如果有多个对象,不能使用同步方法,因为会产生多把锁。

      

      1 窗口1售票,票号为:100
      2 窗口1售票,票号为:99
      3 窗口3售票,票号为:98
      4 窗口3售票,票号为:97
      5 窗口3售票,票号为:96
      6 窗口3售票,票号为:95
      7 窗口3售票,票号为:94
      8 窗口3售票,票号为:93
      9 窗口3售票,票号为:92
     10 窗口3售票,票号为:91
     11 窗口3售票,票号为:90
     12 窗口3售票,票号为:89
     13 窗口3售票,票号为:88
     14 窗口3售票,票号为:87
     15 窗口3售票,票号为:86
     16 窗口3售票,票号为:85
     17 窗口3售票,票号为:84
     18 窗口3售票,票号为:83
     19 窗口3售票,票号为:82
     20 窗口3售票,票号为:81
     21 窗口3售票,票号为:80
     22 窗口3售票,票号为:79
     23 窗口3售票,票号为:78
     24 窗口3售票,票号为:77
     25 窗口3售票,票号为:76
     26 窗口3售票,票号为:75
     27 窗口3售票,票号为:74
     28 窗口3售票,票号为:73
     29 窗口3售票,票号为:72
     30 窗口3售票,票号为:71
     31 窗口3售票,票号为:70
     32 窗口3售票,票号为:69
     33 窗口3售票,票号为:68
     34 窗口3售票,票号为:67
     35 窗口3售票,票号为:66
     36 窗口3售票,票号为:65
     37 窗口3售票,票号为:64
     38 窗口3售票,票号为:63
     39 窗口3售票,票号为:62
     40 窗口3售票,票号为:61
     41 窗口3售票,票号为:60
     42 窗口3售票,票号为:59
     43 窗口3售票,票号为:58
     44 窗口3售票,票号为:57
     45 窗口3售票,票号为:56
     46 窗口3售票,票号为:55
     47 窗口3售票,票号为:54
     48 窗口3售票,票号为:53
     49 窗口3售票,票号为:52
     50 窗口3售票,票号为:51
     51 窗口3售票,票号为:50
     52 窗口3售票,票号为:49
     53 窗口3售票,票号为:48
     54 窗口3售票,票号为:47
     55 窗口3售票,票号为:46
     56 窗口3售票,票号为:45
     57 窗口3售票,票号为:44
     58 窗口3售票,票号为:43
     59 窗口3售票,票号为:42
     60 窗口3售票,票号为:41
     61 窗口3售票,票号为:40
     62 窗口3售票,票号为:39
     63 窗口3售票,票号为:38
     64 窗口3售票,票号为:37
     65 窗口3售票,票号为:36
     66 窗口3售票,票号为:35
     67 窗口3售票,票号为:34
     68 窗口3售票,票号为:33
     69 窗口3售票,票号为:32
     70 窗口3售票,票号为:31
     71 窗口3售票,票号为:30
     72 窗口3售票,票号为:29
     73 窗口3售票,票号为:28
     74 窗口3售票,票号为:27
     75 窗口3售票,票号为:26
     76 窗口3售票,票号为:25
     77 窗口3售票,票号为:24
     78 窗口3售票,票号为:23
     79 窗口3售票,票号为:22
     80 窗口3售票,票号为:21
     81 窗口3售票,票号为:20
     82 窗口3售票,票号为:19
     83 窗口3售票,票号为:18
     84 窗口3售票,票号为:17
     85 窗口3售票,票号为:16
     86 窗口3售票,票号为:15
     87 窗口3售票,票号为:14
     88 窗口3售票,票号为:13
     89 窗口3售票,票号为:12
     90 窗口3售票,票号为:11
     91 窗口3售票,票号为:10
     92 窗口3售票,票号为:9
     93 窗口3售票,票号为:8
     94 窗口3售票,票号为:7
     95 窗口3售票,票号为:6
     96 窗口3售票,票号为:5
     97 窗口3售票,票号为:4
     98 窗口3售票,票号为:3
     99 窗口3售票,票号为:2
    100 窗口3售票,票号为:1
    View Code
     

    3.3 举例:实现多人在银行存钱的功能

    功能需求:银行有一个账户,若两个储户分别向同一个账户存3000元,每次存1000,存3次。每次存完打印账户余额。

    1. 是否涉及到多线程?是,有两个储户
    2. 是否有共享数据?有,同一个账户
    3. 得考虑线程的同步。(两种方法处理线程的安全)

    拓展问题:可否实现两个储户交替存钱的操作(需要使用线程通信)

    package 多线程;
    /**
     * 银行有一个账户,若两个储户分别向同一个账户存3000元,每次存1000,存3次。每次存完打印账户余额。
     1.是否涉及到多线程?是,有两个储户
     2.是否有共享数据?有,同一个账户
     3.得考虑线程的同步。(两种方法处理线程的安全)
    
     拓展问题:可否实现两个储户交替存钱的操作(需要使用线程通信)
     */
    class Account{
        double balance = 0; //余额
        public Account(){
        }
        //存钱
        public synchronized void deposit(double amt){ //存钱,共用一个account,是可以用同步方法的
            balance += amt;
            try{
                Thread.currentThread().sleep(10);
            }catch(InterruptedException e){
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+":"+balance);
        }
    }
    
    class Customer extends Thread{
        Account account;
        public Customer(Account account){
            this.account = account;
        }
        public void run(){
            for(int i=0;i<3;i++){
                account.deposit(1000);
            }
        }
    }
    
    public class BankThread{
        public static void main(String[] args) {
            Account acct = new Account();
            Customer c1 = new Customer(acct);
            Customer c2 = new Customer(acct);
            c1.setName("甲");
            c2.setName("乙");
            c1.start();
            c2.start();
        }
    }
    class Account{
        double balance = 0; //余额
        public Account(){
            System.out.println("账户余额"+balance);
        }
        public Account(int balance){
            this.balance = balance;
            System.out.println("账户余额"+this.balance);
        }
        //存钱
        public synchronized void deposit(double amt){ //存钱,共用一个account,是可以用同步方法的
            balance += amt;
            try{
                Thread.currentThread().sleep(10);
            }catch(InterruptedException e){
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+":"+balance);
        }
    }
    
    class Customer implements Runnable{
        private Account account = null;
    //    public Customer(){
    //    }
        public Customer(Account account){
            this.account = account;
        }
    //    public void setCustomer(Account account){
    //        this.account = account;
    //    }
    //    public Account getCustomer(){
    //        return account;
    //    }
        public void run(){
            for(int i=0;i<3;i++){
                account.deposit(1000);
            }
        }
    }
    
    public class BankThread{
        public static void main(String[] args) {
            Account acct = new Account();
            Customer customer = new Customer(acct);
            Thread c1 = new Thread(customer);
            Thread c2 = new Thread(customer);
            c1.setName("甲");
            c2.setName("乙");
            c1.start();
            c2.start();
        }
    }
    甲:1000.0
    甲:2000.0
    甲:3000.0
    乙:4000.0
    乙:5000.0
    乙:6000.0 
     

    3.4 死锁

    死锁:不同的线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源,就形成了线程的死锁。死锁是我们在使用同步时,需要避免的问题!(处理线程同步时容易出现。)

    package 多线程;
    //死锁的问题:处理线程同步时容易出现
    //不同的线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源,就形成了线程的死锁。
    public class DeadLock {
        static StringBuffer sb1 = new StringBuffer();
        static StringBuffer sb2 = new StringBuffer();
    
        public static void main(String[] args) {
            new Thread(){
                public void run(){
                    synchronized (sb1){
                        try {
                            Thread.currentThread().sleep(10);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        sb1.append("a");
                        synchronized (sb2){
                            sb2.append("b");
                            System.out.println(sb1);
                            System.out.println(sb2);
                        }
                    }
                }
            }.start();
            new Thread(){
                public void run(){
                    synchronized (sb2){
                        try {
                            Thread.currentThread().sleep(10);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        sb1.append("c");
                        synchronized (sb1){
                            sb2.append("d");
                            System.out.println(sb1);
                            System.out.println(sb2);
                        }
                    }
                }
            }.start();
        }
    } 

    五.线程的通信

    1.wait()、notify()、notifyAll()

    如下三个方法必须使用在同步代码块或同步方法中!

    1. wait():当在同步中,执行此方法,则此线程“等待”,直至其他线程执行notify()的方法,将其唤醒,唤醒后继续其wait()后的代码执行。令当前线程挂起并放弃CPU、同步资源、使别的线程可访问并修改共享资源,而当前线程排队等候再次对资源的访问。
    2. notify()/notifyAll():在同步中,执行到此方法,则唤醒某一个被wait的线程优先级最高者)/所有的被wait的线程

    Java.lang.Object提供的这三个方法只有在synchronized方法或synchronized代码块中才能使用,否则会报java.lang.illegalMonitorStateException异常。

    1. 释放锁:wait()
    2. 不释放锁:sleep()、yield()、suspend(过时,可能导致死锁)

    举例1:使用两个线程打印1-100,线程1和线程2交替打印。

    package 多线程;
    //使用两个线程打印1-100,线程1和线程2交替打印。
    //如下的三个关键字使用的话,都得在同步代码块或同步方法中。
    /**
     * 1.wait():一旦一个线程执行到wait(),就释放当前的锁。
     令当前线程挂起并放弃CPU、同步资源、使别的线程可访问并修改共享资源,而当前线程排队等候再次对资源的访问。
     * 2.notify():唤醒wait的一个或多个线程
     唤醒正在排队等待同步资源的线程中优先级最高者结束等待。
     * 3.notifyAll():唤醒正在排队等待资源的所有线程结束等待。
     * Java.lang.Object提供的这三个方法只有在synchronized方法或synchronized代码块中才能使用,
     * 否则会报java.lang.illegalMonitorStateException异常。
     */
    
    class PrintThread implements Runnable{
        int num = 1;
        public void run(){
            while(true) {
                synchronized (this) {
                    notify();//一拿到锁就唤醒
                    if (num <= 100) {
                        try {
                            Thread.currentThread().sleep(10);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println(Thread.currentThread().getName() + num);
                        num += 1;
                    } else {
                        break;
                    }
                    try {
                        wait();//执行完一次就释放锁
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
    
            }
        }
    }
    
    public class TestCommunication {
        public static void main(String[] args) {
            PrintThread pt = new PrintThread();
            Thread t1 = new Thread(pt);
            Thread t2 = new Thread(pt);
    //        Thread t3 = new Thread(pt);
            t1.setName("甲");
            t2.setName("乙");
    //        t3.setName("丙");
            t1.start();
            t2.start();
    //        t3.start();
        }
    }
    甲1
    乙2
    甲3
    乙4
    甲5
    乙6
    甲7
    乙8
    甲9
    乙10
    甲11
    乙12
    甲13
    乙14
    甲15
    乙16
    甲17
    乙18
    甲19
    乙20
    甲21
    乙22
    甲23
    乙24
    甲25
    乙26
    甲27
    乙28
    甲29
    乙30
    甲31
    乙32
    甲33
    乙34
    甲35
    乙36
    甲37
    乙38
    甲39
    乙40
    甲41
    乙42
    甲43
    乙44
    甲45
    乙46
    甲47
    乙48
    甲49
    乙50
    甲51
    乙52
    甲53
    乙54
    甲55
    乙56
    甲57
    乙58
    甲59
    乙60
    甲61
    乙62
    甲63
    乙64
    甲65
    乙66
    甲67
    乙68
    甲69
    乙70
    甲71
    乙72
    甲73
    乙74
    甲75
    乙76
    甲77
    乙78
    甲79
    乙80
    甲81
    乙82
    甲83
    乙84
    甲85
    乙86
    甲87
    乙88
    甲89
    乙90
    甲91
    乙92
    甲93
    乙94
    甲95
    乙96
    甲97
    乙98
    甲99
    乙100

    举例2:生产者消费者

    package 多线程;
    /**
     * 店员一次只能持有固定数量的产品(比如:20),如果生产者试图生产更多的产品,店员会叫生产者停一下。
     * 如果店中有空位放产品了,再通知生产者继续生产;如果店中没有产品了,店员会告诉消费者等一下。
     * 如果店中有空位了再通知消费者来取走产品。
     *
     * 分析:
     * 1.是否涉及到多线程的问题?是,生产者、消费者
     * 2.是否涉及共享数据?有,考虑线程的安全
     * 3.此共享数据是谁?即为产品的数量
     * 4.是否涉及到线程的通信?存在生产者与消费者的通信
     */
    class Clerk{ //店员
        int product;
        public synchronized void addProduct() throws InterruptedException { //生产者
            if (product >= 20){
                wait();
            }else {
                product += 1;
                System.out.println(Thread.currentThread().getName()+":生产力第"+product);
                notifyAll();
            }
        }
        public synchronized void eatProduct() throws InterruptedException { //消费者
            if(product <= 0){
                wait();
            }else{
                System.out.println(Thread.currentThread().getName()+":消费了第"+product);
                product -= 1;
                notifyAll();
            }
        }
    }
    
    class Consumer implements Runnable{ //消费者
        Clerk clerk;
        public Consumer(Clerk clerk){
            this.clerk = clerk;
        }
        public void run(){
            while (true){ //有产品
                try {
                    Thread.currentThread().sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                try {
                    clerk.eatProduct();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    
    class Producer implements Runnable { //生产者
        Clerk clerk;
        public Producer(Clerk clerk){
            this.clerk = clerk;
        }
        public void run() {
            System.out.println("生产者开始生产产品");
            while (true){
                try {
                    Thread.currentThread().sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                try {
                    clerk.addProduct();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    
    public class ConsumerProducer {
        public static void main(String[] args) {
            Clerk clerk = new Clerk();
            Consumer c1 = new Consumer(clerk);
            Thread t1 = new Thread(c1);//一个生产者的线程
    
            Producer p1 = new Producer(clerk);
            Thread t2 = new Thread(p1);//一个消费者的线程
    
            t1.start();
            t2.start();
        }
    }
    

      

    生产者开始生产产品
    Thread-1:生产力第1
    Thread-0:消费了第1
    Thread-1:生产力第1
    Thread-0:消费了第1
    Thread-1:生产力第1
    Thread-0:消费了第1
    Thread-1:生产力第1
    Thread-0:消费了第1
    Thread-1:生产力第1
    Thread-0:消费了第1
    Thread-1:生产力第1
    Thread-0:消费了第1
    Thread-1:生产力第1
    Thread-0:消费了第1
    Thread-1:生产力第1
    Thread-0:消费了第1
    Thread-1:生产力第1
    Thread-0:消费了第1
    Thread-1:生产力第1
    Thread-0:消费了第1
    Thread-1:生产力第1
    Thread-0:消费了第1
    Thread-1:生产力第1
    Thread-0:消费了第1
    Thread-1:生产力第1
    Thread-0:消费了第1
    Thread-1:生产力第1
    Thread-1:生产力第2
    Thread-0:消费了第2
    Thread-1:生产力第2
    Thread-0:消费了第2
    Thread-1:生产力第2
    Thread-0:消费了第2
    Thread-1:生产力第2
    Thread-0:消费了第2
    Thread-1:生产力第2
    Thread-0:消费了第2
    Thread-1:生产力第2
    Thread-0:消费了第2
    Thread-1:生产力第2
    Thread-0:消费了第2
    Thread-1:生产力第2
    Thread-0:消费了第2
    Thread-1:生产力第2
    Thread-0:消费了第2
    Thread-1:生产力第2
    Thread-0:消费了第2
    Thread-1:生产力第2
    Thread-0:消费了第2
    Thread-1:生产力第2
    Thread-0:消费了第2
    Thread-1:生产力第2
    Thread-0:消费了第2
    Thread-1:生产力第2
    Thread-0:消费了第2
    Thread-1:生产力第2
    Thread-0:消费了第2
    Thread-1:生产力第2
    Thread-0:消费了第2
    Thread-1:生产力第2
    Thread-0:消费了第2
    Thread-1:生产力第2
    Thread-0:消费了第2
    Thread-1:生产力第2
    Thread-0:消费了第2
    Thread-1:生产力第2
    Thread-0:消费了第2
    Thread-1:生产力第2
    Thread-0:消费了第2
    Thread-1:生产力第2
    Thread-0:消费了第2
    Thread-1:生产力第2
    Thread-0:消费了第2
    Thread-1:生产力第2
    Thread-0:消费了第2
    Thread-1:生产力第2
    Thread-0:消费了第2
    Thread-1:生产力第2
    Thread-0:消费了第2
    Thread-1:生产力第2
    Thread-0:消费了第2
    Thread-1:生产力第2
    Thread-0:消费了第2
    Thread-1:生产力第2
    Thread-0:消费了第2
    Thread-1:生产力第2
    Thread-0:消费了第2
    Thread-1:生产力第2
    Thread-0:消费了第2
    Thread-1:生产力第2
    Thread-0:消费了第2
    Thread-1:生产力第2
    Thread-0:消费了第2
    Thread-1:生产力第2
    Thread-0:消费了第2
    Thread-1:生产力第2
    Thread-0:消费了第2
    Thread-1:生产力第2
    Thread-0:消费了第2
    Thread-1:生产力第2
    Thread-0:消费了第2
    Thread-1:生产力第2
    Thread-0:消费了第2
    Thread-1:生产力第2
    Thread-0:消费了第2
    Thread-1:生产力第2
    Thread-0:消费了第2
    Thread-1:生产力第2
    Thread-0:消费了第2
    Thread-1:生产力第2
    Thread-0:消费了第2
    Thread-1:生产力第2
    Thread-0:消费了第2
    Thread-1:生产力第2
    Thread-0:消费了第2
    Thread-1:生产力第2
    Thread-0:消费了第2
    Thread-1:生产力第2
    Thread-0:消费了第2
    Thread-1:生产力第2
    Thread-0:消费了第2
    Thread-1:生产力第2
    Thread-0:消费了第2
    Thread-1:生产力第2
    Thread-0:消费了第2
    Thread-1:生产力第2
    Thread-0:消费了第2
    Thread-1:生产力第2
    Thread-0:消费了第2
    Thread-1:生产力第2
    Thread-0:消费了第2
    Thread-1:生产力第2
    Thread-0:消费了第2
    Thread-1:生产力第2
    Thread-0:消费了第2
    Thread-1:生产力第2
    Thread-0:消费了第2
    Thread-1:生产力第2
    Thread-0:消费了第2
    Thread-1:生产力第2
    Thread-0:消费了第2
    Thread-1:生产力第2
    Thread-0:消费了第2
    Thread-1:生产力第2
    Thread-0:消费了第2
    Thread-1:生产力第2
    Thread-0:消费了第2
    Thread-1:生产力第2
    Thread-0:消费了第2
    Thread-1:生产力第2
    Thread-0:消费了第2
    Thread-1:生产力第2
    Thread-0:消费了第2
    Thread-1:生产力第2
    Thread-0:消费了第2
    Thread-1:生产力第2
    Thread-0:消费了第2
    Thread-1:生产力第2
    Thread-0:消费了第2
    Thread-1:生产力第2
    Thread-0:消费了第2
    Thread-1:生产力第2
    Thread-0:消费了第2
    Thread-1:生产力第2
    Thread-0:消费了第2
    Thread-1:生产力第2
    Thread-0:消费了第2
    Thread-1:生产力第2
    Thread-0:消费了第2
    Thread-1:生产力第2
    Thread-0:消费了第2
    Thread-1:生产力第2
    Thread-0:消费了第2
    Thread-1:生产力第2
    Thread-0:消费了第2
    Thread-1:生产力第2
    Thread-0:消费了第2
    Thread-1:生产力第2
    Thread-0:消费了第2
    Thread-1:生产力第2
    Thread-0:消费了第2
    Thread-1:生产力第2
    Thread-0:消费了第2
    Thread-1:生产力第2
    Thread-0:消费了第2
    Thread-1:生产力第2
    Thread-0:消费了第2
    Thread-1:生产力第2
    Thread-0:消费了第2
    Thread-0:消费了第1
    Thread-1:生产力第1
    Thread-1:生产力第2
    Thread-0:消费了第2
    Thread-1:生产力第2
    Thread-0:消费了第2
    Thread-1:生产力第2
    Thread-0:消费了第2
    Thread-0:消费了第1
    Thread-1:生产力第1
    Thread-1:生产力第2
    Thread-0:消费了第2
    Thread-0:消费了第1
    Thread-1:生产力第1
    Thread-0:消费了第1
    Thread-1:生产力第1
    Thread-1:生产力第2
    Thread-0:消费了第2
    Thread-1:生产力第2
    Thread-0:消费了第2
    Thread-1:生产力第2
    Thread-0:消费了第2
    Thread-1:生产力第2
    Thread-0:消费了第2
    Thread-1:生产力第2
    Thread-0:消费了第2
    Thread-1:生产力第2
    Thread-0:消费了第2
    Thread-1:生产力第2
    Thread-0:消费了第2
    Thread-0:消费了第1
    Thread-1:生产力第1
    Thread-1:生产力第2
    Thread-0:消费了第2
    Thread-0:消费了第1
    Thread-1:生产力第1
    Thread-1:生产力第2
    Thread-0:消费了第2
    Thread-0:消费了第1
    Thread-1:生产力第1
    Thread-1:生产力第2
    Thread-0:消费了第2
    Thread-1:生产力第2
    Thread-0:消费了第2
    Thread-1:生产力第2
    Thread-0:消费了第2
    Thread-0:消费了第1
    Thread-1:生产力第1
    Thread-1:生产力第2
    Thread-0:消费了第2
    Thread-1:生产力第2
    Thread-0:消费了第2
    Thread-0:消费了第1
    Thread-1:生产力第1
    Thread-1:生产力第2
    Thread-0:消费了第2
    Thread-0:消费了第1
    Thread-1:生产力第1
    Thread-0:消费了第1
    Thread-1:生产力第1
    Thread-1:生产力第2
    Thread-0:消费了第2
    Thread-0:消费了第1
    Thread-1:生产力第1
    Thread-1:生产力第2
    Thread-0:消费了第2
    Thread-1:生产力第2
    Thread-0:消费了第2
    Thread-0:消费了第1
    Thread-1:生产力第1
    Thread-1:生产力第2
    Thread-0:消费了第2
    Thread-0:消费了第1
    Thread-1:生产力第1
    Thread-0:消费了第1
    Thread-1:生产力第1
    Thread-0:消费了第1
    Thread-1:生产力第1
    Thread-0:消费了第1
    Thread-1:生产力第1
    Thread-1:生产力第2
    Thread-0:消费了第2
    Thread-1:生产力第2
    Thread-0:消费了第2
    Thread-1:生产力第2
    Thread-0:消费了第2
    Thread-0:消费了第1
    Thread-1:生产力第1
    Thread-1:生产力第2
    Thread-0:消费了第2
    Thread-1:生产力第2
    Thread-0:消费了第2
    Thread-1:生产力第2
    Thread-0:消费了第2
    Thread-0:消费了第1
    Thread-1:生产力第1
    Thread-1:生产力第2
    Thread-0:消费了第2
    Thread-1:生产力第2
    Thread-0:消费了第2
    Thread-1:生产力第2
    Thread-0:消费了第2
    Thread-1:生产力第2
    Thread-0:消费了第2
    Thread-1:生产力第2
    ...
    

      

    更多内容见Java入门3.3---线程通信 - nxf_rabbit75 - 博客园

    参考文献:

    【1】尚硅谷Java视频_深入浅出、兼顾实战的Java基础视频(课堂实录)

    【2】每个程序员都会遇到的面试问题:谈谈进程和线程的区别 - 知乎

    【3】Java 多线程 并发编程_escaflone的专栏-CSDN博客_java

    【4】Java并发结构 | 并发编程网 – ifeve.com

  • 相关阅读:
    angular js 自定义指令
    web api 解决跨域的问题
    angular 监听ngrepeat结束时间
    redis关闭和启动
    intellij idea快捷键
    mysql连接字符串
    crontab命令格式
    maven中scope属性的
    maven pom文件元素说明
    引入maven以外的jar包
  • 原文地址:https://www.cnblogs.com/nxf-rabbit75/p/13031555.html
Copyright © 2011-2022 走看看