JavaSE学习笔记-13

线程(基础)

程序就是我们编写的代码,然而

进程是我们把代码运行起来所形成的动态的一个过程

每当我们执行一个程序的时候,我们就会开启一个进程。。。

当我们关闭,即是结束程序的时候,进程就会消失,就会停止。。。

 一个进程可以拥有多个线程任务

并行:相对于两个CPU而言,多个CPU实现并行

并发:一个CPU实现多个任务执行,造成一种似乎多个任务同时执行的错觉,多个任务这样执行的情景被称之为并发 

八个处理器表示的意思就是八核,具有八个CPU进行处理。

测量当前电脑有多少核?

 线程应用案例1-继承Thread类

 

 细节

一开始执行程序形成一个进程,之后先开始执行main线程,执行start方法之后开始调用Thread-0线程 。但是执行Thread-0线程的时候,main线程依然是继续并发执行的。。。

 

 记住一句话:

对于一个程序执行,产生进程,产生main线程(主线程)以及多个子线程(Thread-0线程) ,

并不是说当main线程结束之后进程就挂掉了,而是等所有线程的任务都结束完成了之后进程才会结束挂掉。。。

并且记住:

不仅是main线程才可以产生子线程,子线程(如Thread-0线程)也可以再一次产生一个线程去执行。

只有当所有的线程执行完之后,进程才会结束。。。。

 为什么不直接调用run方法,而是通过start方法创建出一个子线程去调用呢?

run方法就是一个普通的方法,如果这样直接调用,等于说还是执行的main这个线程的方法,并没有再启动一个线程实现并发。。。所以我们调用start方法实现多线程并发执行的高效效果

直接调用run方法的后果就是:

当run方法执行完之后才会执行main线程下面的任务方法。。。。

 

native方法

native标识的方法start0是本地方法,只可以由JVM机进行调用,我们不可以调用。。。

 start方法底层分析

源码:

真正实现多线程效果的是start0方法而不是run方法 

我们通过main线程,之后通过一个类(这个类继承了Thread线程类)的对象引用进行调用start方法

实现多线程并发执行

追及源码可知,start方法底层就是start0方法,所以实现多线程效果的是start0而不是run方法

 线程应用2-实现Runnable接口

 

我们模拟一下这种实现Runnable接口来启动多线程并发的底层实现源码:

来解释一下为什么这样传?

这里的ThreadProxy类就相对于后面的Thread类

Thread类编译器底层源码截取:

实现的原理和我们上面模拟的一模一样

 

 构造器:

练习

 

 

 总结:

我们把相应的业务代码写到对应的子线程(由main方法各种方式通过start方法启动出来的线程)

 让各个线程并发分别执行完。

 

无论哪个线程先执行完,都不会影响其他的线程,直到所有线程执行完之后,整个进程才会退出 

 T3就是业务实现逻辑类。它是被thread01类和thread02类共享的

最后实现调用start方法,底层调用start0方法,start0方法调用Thread类中的run方法,这个重写的run方法的实现内容就是运行时绑定实现业务逻辑类T3的run方法,业务逻辑的代码就写在T3的run方法中【上面分析过底层源码】

 使用synchronized解决售票问题【后面会细讲synchronized】

线程终止

 

 总结:

如果我们希望t1这个线程可以控制t2这个线程什么时候退出,那么我们需要在t1中可以进行设置t2的变量就可以了呀

 线程常用方法

当类Th继承了Thread线程类之后,我们创建一个Th类的对象引用,我们就可以通过这个对象引用来调用方法作用于这个线程

 细节:

 1.如图:t2让出CPU让t1线程自己进行执行

但是也有可能礼让不成功,当CPU能够顾及t1,t2过来的时候,我们就不会进行礼让了。。。

 2.插队

在CPU执行的过程中启动两个线程 t1 t2,当线程t1执行到某个代码的时候,这行代码是执行t2.join()方法

【join方法表示插队执行的意思】t2.join表示让t2线程的任务插队优先进行执行

那么CPU开始执行t2的线程任务,当t2的线程任务执行完之后,CPU才会接着执行t1的线程任务

 上图就是描述,在main线程中调用 t1.join方法,表示的意思是把t1线程的任务执行完之后,再返回去执行main线程的任务

 线程常用方法练习

 结果: 

 

有一个细节:

在上面进行插队时,我们应该先调用Thread类中的start方法,先启动子线程,启动完之后再进行调用join方法【在后面将线程的状态会讲解】

 守护线程

 

一般来说,主线程m启动了一个子线程为t1。假设t1是一个无限循环的代码,当主线程m执行完之后,子线程t1依旧会无限循环的执行下去。。。

但是当我们把t1设置成主线程m的守护线程的时候,那么当主线程m执行结束后,守护线程t1也会结束。。。。。 

 演示代码:

线程的状态

一般官方文档是有六种线程状态 :

NEW RUNNABLE BLOCKED WAITING TIMED_WAITING TERMINATED

但是有的地方说是七种线程状态

RUNNABLE代表可运行状态,但是它是否真的运行是取决于内核的调度器决定的。因此RUNNABLE又可以被细化为两种状态,Ready就绪状态和Running 运行状态。Running 运行状态是真正占用CPU内存的,真正运行的状态。

yeild方法

当线程start之后进行RUNNABLE状态,我们调用yeild方法,把其搞到Ready状态进行就绪状态,但是内核中线程是被调度器选中执行的,是具有一定的难控制性。它是取决于内核中的资源来决定是否真的进行礼让,如果资源多,那么同时执行。资源过少时才可能进行真的礼让处理。。。

线程被挂起

当线程被挂起时,线程从运行状态Running转化到就绪状态Ready

 

注意顺序执行:

首先第一步是start启动线程之后到Runnable状态

第二步:

如果进入同步代码块的锁,那么就进入Blocked状态(锁阻塞状态)当获得锁时,才可以返回Runnable状态

如果【剩余两种情况如图所示查看即可】

 

 Synchronized

 

线程同步: 

不管有多少个线程同时进来,在同一个时刻最多只能有一个线程进来实现操作。。。当一个操作完了之后才可以进来第二个执行。。

 

举个例子:无论有多少个线程进行调用m方法,在同一个时刻,只可以有一个线程进行调用m方法 

 

 4.使用synchronized解决售票问题

 线程同步原理

分析:

 当如图 t1 t2 t3三个线程进去到某一个由synchronized修饰的方法的时候,会有一把锁。然后这三个线程去抢这一把锁,假设t1抢到了这把锁之后,t2 t3只能在门口进行等待,当t1执行完之后把锁还了回去,那么此时再一次线程t1 t2 t3再一次进行抢锁的操作,三者抢到锁的可能性还是一样的。反复进行,但是同一时刻这个方法的执行只可以有一个线程进来。。。。

 【这把锁是在对象obj上加的,是在对象的位上。所以这个锁是修饰对象的】

 互斥锁

1.如果synchronized是加在非静态方法上: 

那么锁可以是this,也可以是其他对象(要求是同一个对象)

如图:

 我们也可以在代码块上写synchronized(this):表示这是一个同步代码块。。。

2.如果synchronized是加在静态方法上: 

那么锁其实是当前类本身

互斥锁细节:

 同步代码块或者同步方法都会使得程序效率降低,所以我们使用同步代码块比较好。因为同步代码块控制的代码范围更加具体,使得效率可以进一步提升。。。。

并且多个线程作用的锁对象是同一个:

但是下图不是代表同一个锁对象,所以不可以写this来代表同一个锁对象: 

锁对象不同时:如图:new Object()每一次new代表不同的锁对象

 不同的锁对象,就好像是三个门,一个马桶。。。。

 线程的死锁

——————————————————————

经典死锁演示:

 分析:

如果flag为true时,先等待进入第一个同步代码块,线程A会获得o1这把锁,并且进入第一个同步代码块等待进入第二个同步代码块,然后尝试获得o2这把锁,如果o2这把锁能够获得,那么就进入第二个同步代码块并且执行代码。执行完之后,释放锁o2;之后释放锁o1;如果不能获得,那么就Blocked。

如果flag为false,同理。进入同步代码块的锁是o2,然后尝试获得o1这把锁,如果可以获得。。。。。同上分析即可

当两个线程同时获得锁o1和锁o2的时候,

那么A线程就不可能再获得o2这把锁,那么就只能Blocked

同理B线程也就不可能再获得o1这把锁,那么也就Blocked

形成死锁。。。。。

——————————————————————————————

 如下图所示:

执行流程:

 线程进来的时候,先等待进入同步代码块的锁,获得锁对象,进入同步代码块执行。如果获得不了锁对象,Blocked。

 释放锁

过时的方法:

 练习:

 细节1:输出100以内的整数 

 细节2:输入一个整形字符

细节3:

注意要把A类对象引用传给B类,

不然没办法实现从一个线程控制另外一个线程

 代码如下:

 

分析【重点】:

t1和t2这两个线程过来之后,等待着进入同步代码块。两个线程进行争夺锁,锁对象只有一个,当t1获取这把锁之后,t1线程进入同步代码块,并且执行代码。t2Blocked在外面。当执行完之后,t1释放锁对象,t1和t2重新到这个地方进行争夺锁,抢到锁的进入执行方法或代码块语句。没有抢到锁的在外面被Blocked

 

 

 注意:

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值