线程的概念
一个进程可包含一个或多个线程
多进程的内部数据和状态是独立的
多线程是共享一块内存空间和一组系统资源有可能互相影响
线程切换比进程的负担小
程序启动就自动产生一个线程即主线程 main 方法就执行在主线程
*线程的生命周期
一个线程从创建到消亡的过程 : 1 创建状态 new 2 : 可运行状态 start 3:不可运行状态 sleep wait IO阻塞 4:消亡状态 5 : 阻塞状态
(下图不包含锁的情况)
run()
start() 开始线程
线程消亡 只能靠 run 方法自然结束 ,stop() 不安全,可在 run 加判断结束代码执行
终止线程运行的情况:
1 yield() 让出了 CPU 的占用权
2 sleep
3 IO
4 出现一个优先级更高的线程
5 支持时间片的系统中,线程的时间片用完
*线程的实现
1 继承 Thread 类 重写 run 方法 2 实现 Runnable 接口 然后实现 run 方法
代码写在 run 方法中 线程启动 start 方法
线程的优先级
通过 setPriority() 来更改线程的优先级
线程的优先级 是 1~ 10 之间的正整数, 默认为 5
*多线程的同步
多线程可能存在两个或多个线程尝试去访问一个资源造成同步问题,例如: 去银行取钱 账户余额 1000元 同时在柜台 和 取款机各提取 800 , 柜台 和 取款机我们看做两个取款线程,访问一分资源 1000。此时其中一个线程已经把 800 取出来了 ,数据还未同步另外一个线程访问账户余额的数据还是 1000 。这种情况显然是不合理的 ,所以我们需要有办法去解决多线程同步的问题。
synchronized
为了解决多线程访问共享资源造成的同步问题,可以用 synchronized 来解决。原理是只要使用了 synchronized 获取到 CPU 执行权的线程开始访问资源,并且对资源(方法或者代码块)所在的对象上锁(lock),当一个对象被上锁后。试图访问这个对象资源的线程就必须在外阻塞等待已经获取锁的线程的 run 方法执行完毕或者中途抛出异常等情况让出 CPU 的执行权以及对象解锁。
synchronized 的使用方式分为两种
1 synchronized 修饰方法
2 synchronized 代码块
synchronized(object){
}
synchronized 代码块为更细粒度的解决同步问题,如果在一个方法中。不可能产生同步问题的代码我们不需要上锁,能更有效的节约资源
需要注意的是: static 修饰的 synchronized 方法上锁的是所在对象的 class 对象 而不是所在对象的锁
线程间如何通信? wait notify notifyAll
两个或多个线程同时 start 后 run 方法中分别做 for 循环遍历 ,遍历中对一个共享 int 变量 0,一个线程做 int ++ 一个线程做 int -- ; 问如何保证每次输出的结果是 1010101.... 或者 01010101...
1 首先保证执行 ++ 或者 -- 的方法 或者 代码逻辑做了 synchronized 处理。解决同步问题
2 在某个线程获得 CPU 的执行权后,进入 synchronized ,如果遇到 int 变量不是预期的结果就调用对象的 wait 方法。让去 CPU 的执行权 让其他线程抢占,如果是预期的结果则执行 ++ 或者 -- 。本身的 ++ -- 逻辑做完以后当前线程工作完毕 调用对象的 notify 或者 notifyAll 通知其他线程去抢占 CPU 的执行权,以及类推在互相唤醒和等待的过程中实现 101010。 解决线程间通信问题
延伸: deadlock 死锁 哲学家就餐问题