多线程的认识3

1. 线程的状态

 

NEW: 安排了工作 , 还未开始行动
RUNNABLE: 可工作的 . 又可以分成正在工作中和即将开始工作 .
BLOCKED: 这几个都表示排队等着其他事情
WAITING: 这几个都表示排队等着其他事情
TIMED_WAITING: 这几个都表示排队等着其他事情
TERMINATED: 工作完成了 .
2 线程状态和状态转移的意义
还是我们之前的例子:
刚把李四、王五找来,还是给他们在安排任务,没让他们行动起来,就是 NEW 状态;
当李四、王五开始去窗口排队,等待服务,就进入到 RUNNABLE 状态。 该状态并不表示已经被银行工
作人员开始接待,排在队伍中也是属于该状态,即可被服务的状态,是否开始服务,则看调度器的调度 ;
当李四、王五因为一些事情需要去忙,例如需要填写信息、回家取证件、发呆一会等等时,进入
BLOCKED WATING TIMED_WAITING 状态,至于这些状态的细分,我们以后再详解;
如果李四、王五已经忙完,为 TERMINATED 状态。
所以,之前我们学过的 isAlive() 方法,可以认为是处于不是 NEW TERMINATED 的状态都是活着 的。

 

3 观察线程的状态和转移
观察 1: 关注 NEW RUNNABLE TERMINATED 状态的转换
    使用 isAlive 方法判定线程的存活状态 .

      关注 WAITING BLOCKED TIMED_WAITING 状态的转换

在这里运行代码我们是看不出来什么的,通过使用 jconsole 可以看到 thread1 的状态是 TIMED_WAITING ,thread2的状态是BLOCKED.

修改上面的代码, t1 中的 sleep 换成 wait

 使用 jconsole 可以看到 thread1 的状态是 WAITING

结论 :
BLOCKED 表示等待获取锁 , WAITING TIMED_WAITING 表示等待其他线程发来通知 .
TIMED_WAITING 线程在等待唤醒,但设置了时限 ; WAITING 线程在无限等待唤醒
3: yield() 大公无私,让出 CPU
可以看到 :
1. 不使用 yield 的时候 , 张三李四大概五五开
2. 使用 yield , 张三的数量远远少于李四
结论 :
yield 不改变线程的状态 , 但是会重新去排队
4. 多线程带来的的风险 - 线程安全 ( 重点 )
1 线程安全的概念
想给出一个线程安全的确切定义是复杂的,但我们可以这样认为:
如果多线程环境下代码运行的结果是符合我们预期的,即在单线程环境应该的结果,则说这个程序是线 程安全的。
2 解决线程不安全问题
可以利用synchronized的特性:
1) 互斥
synchronized 会起到互斥效果 , 某个线程执行到某个对象的 synchronized 中时 , 其他线程如果也执行到 同一个对象 synchronized 就会 阻塞等待 .
进入 synchronized 修饰的代码块 , 相当于 加锁
退出 synchronized 修饰的代码块,相当于 解锁
理解 " 阻塞等待 ".
针对每一把锁 , 操作系统内部都维护了一个等待队列 . 当这个锁被某个线程占有的时候 , 其他线程尝
试进行加锁 , 就加不上了 , 就会阻塞等待 , 一直等到之前的线程解锁之后 , 由操作系统唤醒一个新的
线程 , 再来获取到这个锁 .
注意 :
上一个线程解锁之后 , 下一个线程并不是立即就能获取到锁 . 而是要靠操作系统来 " 唤醒 ".
也就是操作系统线程调度的一部分工作 .
假设有 A B C 三个线程 , 线程 A 先获取到锁 , 然后 B 尝试获取锁 , 然后 C 再尝试获取锁 , 此时 B
C 都在阻塞队列中排队等待 . 但是当 A 释放锁之后 , 虽然 B C 先来的 , 但是 B 不一定就能
获取到锁 , 而是和 C 重新竞争 , 并不遵守先来后到的规则 .

 synchronized的底层是使用操作系统的mutex lock实现的.

2) 刷新内存
synchronized 的工作过程 :
1. 获得互斥锁
2. 从主内存拷贝变量的最新副本到工作的内存
3. 执行代码
4. 将更改后的共享变量的值刷新到主内存
5. 释放互斥锁
所以 synchronized 也能保证内存可见性 . 具体代码参见 volatile 的源码。
3) 可重入
synchronized 同步块对同一条线程来说是可重入的,不会出现自己把自己锁死的问题;
理解 " 把自己锁死 "
一个线程没有释放锁 , 然后又尝试再次加锁 .

 

理解 " 把自己锁死 "
一个线程没有释放锁 , 然后又尝试再次加锁

这样的锁称为 不可重入锁。
代码示例
在下面的代码中 ,
increase increase2 两个方法都加了 synchronized, 此处的 synchronized 都是针对 this 当前
对象加锁的 .
在调用 increase2 的时候 , 先加了一次锁 , 执行到 increase 的时候 , 又加了一次锁 . ( 上个锁还没释
, 相当于连续加两次锁 )
这个代码是完全没问题的 . 因为 synchronized 是可重入锁 .
在可重入锁的内部 , 包含了 " 线程持有者 " " 计数器 " 两个信息 .
如果某个线程加锁的时候 , 发现锁已经被人占用 , 但是恰好占用的正是自己 , 那么仍然可以继续获取
到锁 , 并让计数器自增 .
解锁的时候计数器递减为 0 的时候 , 才真正释放锁 . ( 才能被别的线程获取到)

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值