Java学习31-Java 多线程Thread 线程的创建

多线程的概念:

用户想要一边听歌,一边QQ聊天,一边游戏。要求能并发执行。

  • program程序: 有特殊功能的一组代码
  • process进程: 正在执行中的program,或者程序program的一次执行过程
  • thread线程:程序program内部的一条执行路经,一个process至少有一个thread,一个process同一时间若并行执行多个thread,就是支持多线程的。

一个process中的多个thread共享相同的内存单元,他们从同一个堆中分配对象,可以访问相同的变量和对象,但是多个thread操作共享的系统资源可能就会带来安全的隐患。注意:不同的进程之间是不共享内存的;进程之间的数据交换和通信成本很高;

如图所示,下面的红框左侧Method Area和Heap就是共享的,浅蓝色的Virtual Machine Stack, Native Method Stack, Program Counter Register是每个thread运行时自己独有的。

Thread调度

  • 分时调度: CPU给大家平均每个thread分多久时间,均分时间。
  • 抢占式调度: 让优先级高的以较大概率优先使用CPU,如果thread们优先级相同,那就随机选择一个,Java用的就这种。

并行parallel和并发concurrency

并行 parallel:两个或多个事件在同一时刻同时发生。同一时刻,有多条指令多个CPU同时执行,相当于“多个人同时干不同的活”。

parallel
并发 concurrency:两个或多个事件在同一时间发生。在一段时间内,有多条指令单个CPU快速轮换,交替执行,使得在宏观上看起来具有多个进程同时执行的效果。相当于“一个人不停的干不同的活”。
concurrency

如何创建线程

两种办法
多线程的创建方式一:自建类继承Thread类,用户重写run方法
多线程的创建方式二:用户创建实现Runnable接口的类,再在里面重写run方法

(java.lang.Thread类是线程类,所有的线程对象都必须是Thread类或其子类的实例。)

第一种: 线程执行体:Thread对象调用run()

具体步骤:

  1. 创建extends继承Thread类的子类。
  2. 重写Thread的run(),该run方法体囊括此线程需要完成的各种操作
  3. 创建该Thread类子类的对象(创建Thread子类的实例)
  4. 调用线程对象的start()方法,启动该线程,JVM会自动调用用户重写的那个run方法
  5. 如果想实现其他更多线程,需要创建新的线程对象。

先使用方式一:自建类继承Thread类,用户重写run方法
举例,输出1-100以内所有的偶数。

package Thread;

public class EvenNumberTest {
    public static void main(String[] args) {
    //3. 创建该Thread类子类的对象(创建Thread子类的实例)
    PrintNumber x = new PrintNumber();
    //4. 调用线程对象的start()方法,启动该线程
    x.start();
}
    //5. 如果想实现其他更多线程,需要创建新的线程对象,不能再次调用已经start的线程。
}



    //1. 创建继承Thread类的子类
    class PrintNumber extends Thread{

        //2. 重写Thread的run(),该run方法体囊括此线程需要完成的各种操作
        @Override
        public void run() {
            for (int i = 0; i <= 100; i++) {
                if (i%2==0){
                    System.out.print(i+"\t");
                }
            }
        }
    }

注意不能让已经start的线程再次执行start(),会报错IllgealThreadStateException,正确方法就是再建一个。
输出结果:

0	2	4	6	8	10	12	14	16	18	20	22	24	26	28	30	32	34	36	38	40	42	44	46	48	50	52	54	56	58	60	62	64	66	68	70	72	74	76	78	80	82	84	86	88	90	92	94	96	98	100	
Process finished with exit code 0

举例2,一个线程输出1-100以内所有的偶数。一个线程输出1-100以内所有的奇数。

package Thread;

public class Number2Thread {


    //3. 创建该Thread类子类的对象(创建Thread子类的实例)
    //4. 调用线程对象的start()方法,启动该线程,JVM会自动调用用户重写的那个run方法
    //5. 如果想实现其他更多线程,需要创建新的线程对象。
    public static void main(String[] args) {
        PrintEvenNum evenx = new PrintEvenNum();
        evenx.start();
        PrintOddNum oddx= new PrintOddNum();
        oddx.start();
    }
}

//1. 创建继承Thread类的子类
class PrintOddNum extends Thread{
    //2. 重写Thread的run(),该run方法体囊括此线程需要完成的各种操作


    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            if (i%2 !=0){
                System.out.print("我是odd number奇数"+i+"\t");
            }
        }
    }
}

class PrintEvenNum extends Thread{

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            if (i%2 ==0)    {
                System.out.println("我是偶数even number"+i+"\t");
            }
        }

    }
}

下面运行结果里可以明显看出,两个thread是交互运行输出的,并不是以往常见的,一个模块整体结束再运行下一个:


我是odd number奇数1	我是odd number奇数3	我是odd number奇数5	我是odd number奇数7	我是odd number奇数9	我是odd number奇数11	我是odd number奇数13	我是odd number奇数15	我是odd number奇数17	我是odd number奇数19	我是odd number奇数21	我是odd number奇数23	我是odd number奇数25	我是odd number奇数27	我是odd number奇数29	我是odd number奇数31	我是odd number奇数33	我是odd number奇数35	我是odd number奇数37	我是odd number奇数39	我是odd number奇数41	我是odd number奇数43	我是odd number奇数45	我是odd number奇数47	我是odd number奇数49	我是odd number奇数51	我是odd number奇数53	我是odd number奇数55	我是odd number奇数57	我是odd number奇数59	我是偶数even number0	
我是偶数even number2	
我是偶数even number4	
我是偶数even number6	
我是偶数even number8	
我是偶数even number10	
我是偶数even number12	
我是偶数even number14	
我是偶数even number16	
我是偶数even number18	
我是偶数even number20	
我是偶数even number22	
我是偶数even number24	
我是odd number奇数61	我是odd number奇数63	我是odd number奇数65	我是odd number奇数67	我是odd number奇数69	我是odd number奇数71	我是odd number奇数73	我是odd number奇数75	我是odd number奇数77	我是odd number奇数79	我是odd number奇数81	我是odd number奇数83	我是odd number奇数85	我是odd number奇数87	我是odd number奇数89	我是odd number奇数91	我是odd number奇数93	我是odd number奇数95	我是odd number奇数97	我是odd number奇数99	我是偶数even number26	
我是偶数even number28	
我是偶数even number30	
我是偶数even number32	
我是偶数even number34	
我是偶数even number36	
我是偶数even number38	
我是偶数even number40	
我是偶数even number42	
我是偶数even number44	
我是偶数even number46	
我是偶数even number48	
我是偶数even number50	
我是偶数even number52	
我是偶数even number54	
我是偶数even number56	
我是偶数even number58	
我是偶数even number60	
我是偶数even number62	
我是偶数even number64	
我是偶数even number66	
我是偶数even number68	
我是偶数even number70	
我是偶数even number72	
我是偶数even number74	
我是偶数even number76	
我是偶数even number78	
我是偶数even number80	
我是偶数even number82	
我是偶数even number84	
我是偶数even number86	
我是偶数even number88	
我是偶数even number90	
我是偶数even number92	
我是偶数even number94	
我是偶数even number96	
我是偶数even number98	

Process finished with exit code 0


也可以使用Thread.currentThread().getName()打印出当前thread的名字,输出时也会明显看出交替性:

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            if (i%2 !=0){
                //System.out.print("我是odd number奇数"+i+"\t");
                System.out.println(Thread.currentThread().getName()+"\t"+i);
            }
        }
    }

结果显示,两个thread-1/0互相交替出现


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

Process finished with exit code 0


下面对程序进行改写,试图创建Thread类的匿名子类的匿名对象,并调用其start方法
提示:可以使用new Thread(){XXX}.start();

package Thread;

public class Number2Thread {
public static void main(String[] args) {

       //创建Thread类的匿名子类的匿名对象,并调用其start方法
        // new Thread(){};
        // new Thread(){重写run方法};
        // new Thread(){重写run方法}.start();

       new Thread(){
            @Override
            public void run() {
                for (int i = 0; i < 100; i++) {
                    if (i%2 !=0){
                        //System.out.print("我是odd number奇数"+i+"\t");
                        System.out.println(Thread.currentThread().getName()+"\t"+i);
                    }
                }
            }

        }.start();

        new Thread(){
            public void run() {
            for (int i = 0; i < 100; i++) {
                if (i%2 ==0)    {
                    //System.out.println("我是偶数even number"+i+"\t");
                    System.out.println(Thread.currentThread().getName()+"\t"+i);
                }
            }
        }}.start();

    }
}


可以观察到,结果依旧是两个thread交替出现的

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

Process finished with exit code 0

现在使用创建方式二:用户创建实现Runnable接口的类,再在里面重写run方法

具体步骤如下:

  1. 创建一个实现Runnable接口的类
  2. 实现接口中的run() -->将此线程要执行的操作,声明再方法体中
  3. 创建当前实现类的对象
  4. 将此对象作为参数传递到Thread类的构造器中,创建Thread类的实例
  5. Thread类的实例调用.start()

这种思维模式可以简单解释成

- 先建一个完成Runnable接口的实现类,里面重写了run方法
- 用这个新建的实现类做一个objectA
- 将这个objectA作为参数传给Thread
- Thread调用start方法时,会使用参数objectA自己重写的run方法

IDEA快捷键ctrl+i快速生成run方法块 (自动弹出override相关method)
双击接口名称处的Runnable按键ctrl+i可以获得这个接口对应的method,选中override快速构建重写的结构。

举例:使用继承Runnable接口的方法,建立两个thread分别输出100以内能被3整除的所有数字。

package Thread;

public class QuickTestRunnable {
    public static void main(String[] args) {
        PrtRunDiv3Num p1= new PrtRunDiv3Num();
        //只需要建立一个PrtRunDiv3Num的实例p1就行
        Thread y0 = new Thread(p1);
        Thread y1 = new Thread(p1);
        y1.start();
        y0.start();

    }



}

class PrtRunDiv3Num implements Runnable{

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

    }
}


可以看到运行结果里,两个Thread交替输出着


Thread-1	0
Thread-1	3
Thread-1	6
Thread-1	9
Thread-1	12
Thread-1	15
Thread-1	18
Thread-1	21
Thread-0	0
Thread-0	3
Thread-0	6
Thread-0	9
Thread-0	12
Thread-0	15
Thread-0	18
Thread-0	21
Thread-0	24
Thread-0	27
Thread-0	30
Thread-0	33
Thread-0	36
Thread-0	39
Thread-0	42
Thread-0	45
Thread-0	48
Thread-0	51
Thread-0	54
Thread-0	57
Thread-0	60
Thread-0	63
Thread-0	66
Thread-1	24
Thread-1	27
Thread-1	30
Thread-1	33
Thread-1	36
Thread-1	39
Thread-0	69
Thread-0	72
Thread-1	42
Thread-1	45
Thread-1	48
Thread-1	51
Thread-1	54
Thread-1	57
Thread-1	60
Thread-1	63
Thread-1	66
Thread-1	69
Thread-1	72
Thread-1	75
Thread-1	78
Thread-1	81
Thread-1	84
Thread-1	87
Thread-1	90
Thread-1	93
Thread-1	96
Thread-1	99
Thread-0	75
Thread-0	78
Thread-0	81
Thread-0	84
Thread-0	87
Thread-0	90
Thread-0	93
Thread-0	96
Thread-0	99

Process finished with exit code 0


举例,使用继承Runnable接口的方法,建立两个thread分别输出1-100内所有的偶数和奇数。

package Thread;

public class EvenNumberRunnableTest {

    //1. 创建一个实现Runnable接口的类
    //2. 实现接口中的run() -->将此线程要执行的操作,声明再方法体中
    //3. 创建当前实现类的对象
    //4. 将此对象作为参数传递到Thread类的构造器中,创建Thread类的实例
    //5. Thread类的实例调用.start()


    public static void main(String[] args) {


    PrintEvenNumRunnable x=new PrintEvenNumRunnable();
    Thread t1 = new Thread(x);
    t1.start();

    PrintOddNumRunnable y = new PrintOddNumRunnable();
    Thread t2 =new Thread(y);
    t2.start();

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


class PrintOddNumRunnable implements Runnable{

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

运行结果可以看出两个Thread交替输出:

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

Process finished with exit code 0

用匿名实现类对上述程序进行改写,采用Runnable接口匿名实现类的匿名对象简化程序

提示,可使用格式 new Thread(new Runnable() {public void run() {XXX} }).start();调用start结构

package Thread;

public class EvenNumberRunnableTest {
    public static void main(String[] args) {

    new Thread(new Runnable(){

            public void run() {
            for (int i = 0; i <= 100; i++) {
                if(i%2==0){
                    System.out.println(Thread.currentThread().getName()+"\t"+i);
                }
            }
        }

    }).start();

    //PrintOddNumRunnable y = new PrintOddNumRunnable();
    new Thread(new Runnable(){
        public void run() {
            for (int i = 0; i <= 100; i++) {
                if(i%2==0){
                    System.out.println(Thread.currentThread().getName()+"bobo"+"\t"+i);
                }
            }
        }
    }).start();

    }
}

输出显示确实是交替输出的


Thread-0	0
Thread-0	2
Thread-0	4
Thread-0	6
Thread-0	8
Thread-0	10
Thread-0	12
Thread-0	14
Thread-0	16
Thread-0	18
Thread-0	20
Thread-0	22
Thread-0	24
Thread-0	26
Thread-0	28
Thread-0	30
Thread-0	32
Thread-0	34
Thread-0	36
Thread-0	38
Thread-1bobo	0
Thread-0	40
Thread-0	42
Thread-0	44
Thread-0	46
Thread-0	48
Thread-0	50
Thread-0	52
Thread-0	54
Thread-0	56
Thread-0	58
Thread-0	60
Thread-0	62
Thread-0	64
Thread-0	66
Thread-0	68
Thread-0	70
Thread-0	72
Thread-0	74
Thread-0	76
Thread-0	78
Thread-0	80
Thread-0	82
Thread-0	84
Thread-0	86
Thread-0	88
Thread-0	90
Thread-0	92
Thread-0	94
Thread-0	96
Thread-0	98
Thread-0	100
Thread-1bobo	2
Thread-1bobo	4
Thread-1bobo	6
Thread-1bobo	8
Thread-1bobo	10
Thread-1bobo	12
Thread-1bobo	14
Thread-1bobo	16
Thread-1bobo	18
Thread-1bobo	20
Thread-1bobo	22
Thread-1bobo	24
Thread-1bobo	26
Thread-1bobo	28
Thread-1bobo	30
Thread-1bobo	32
Thread-1bobo	34
Thread-1bobo	36
Thread-1bobo	38
Thread-1bobo	40
Thread-1bobo	42
Thread-1bobo	44
Thread-1bobo	46
Thread-1bobo	48
Thread-1bobo	50
Thread-1bobo	52
Thread-1bobo	54
Thread-1bobo	56
Thread-1bobo	58
Thread-1bobo	60
Thread-1bobo	62
Thread-1bobo	64
Thread-1bobo	66
Thread-1bobo	68
Thread-1bobo	70
Thread-1bobo	72
Thread-1bobo	74
Thread-1bobo	76
Thread-1bobo	78
Thread-1bobo	80
Thread-1bobo	82
Thread-1bobo	84
Thread-1bobo	86
Thread-1bobo	88
Thread-1bobo	90
Thread-1bobo	92
Thread-1bobo	94
Thread-1bobo	96
Thread-1bobo	98
Thread-1bobo	100

Process finished with exit code 0


两种线程创建方法,
共同点:都需要重写Run()方法,但也不尽相同。一个是继承Thread方法里面的 Run(); 一个是实现 Runnable接口的Run()
创建的线程对象,都是Thread类以及其子类的实例。
启动Thread调用的都是Thread里面的start

不同点:一个是继承于Thread类,一个是实现Runnable接口
==更建议使用Runnable接口的方式。==原因:1.避免类的单继承局限性 2.当遇到多个线程共享数据时候更好处理 3.实现代码和数据分离

联系:打开Thread类,就会发现最上面写的是
public class Thread implements Runnable {...
Thread类自己也是继承了Runnable接口的实现类。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值