一. 理解程序、进程、线程的概念
1. 程序可以理解为静态的代码
2. 进程可以理解为执行中的程序
3. 线程可以理解为进程的进一步细分, 程序的一条执行路径
二. 如何创建java程序的线程
方式一: 继承于Thread类
① class ImplThread extends Thread {
@Override
public void run() {
// 子线程执行的代码
super.run();
}
}
② new Thread(){
@Override
public void run() {
super.run();
}
}.start();
方式二: 实现Runnable接口的方式
① public static void main(String[] args) {
ImplRunnable runn = new ImplRunnable();
Thread thread = new Thread(runn);
Thread thread1 = new Thread(runn);
}
class ImplRunnable implements Runnable {
@Override
public void run() {
// 子线程执行的代码
}
}
② new Thread(new Runnable() {
@Override
public void run() {
}
}).start();
对比 继承方式和实现方式的区别
1. 联系: public class Thread implements Runnable
2. 实现的方式优于继承的方式
①. 避免了java单继承的局限性
②. 如果多个线程要操作同一份资源(或数据), 更适合使用实现的方式
三. Thread的常用方法:
1. start(): 启动相乘并执行相应的run()方法
2. run(): 子线程要执行的代码放入run()方法中
3. currentThread(): 静态的, 调取当前的线程
4. getName(): 获取此线程的名字
5. setName(): 设置此线程的名字
6. yield(): 调用此方法的线程释放当前CPU的执行权
7. join: 在A线程中调用B线程join()方法, 表示: 当执行到此方法, A线程停止执行, 直至B线程执行完毕, A线程再接着join()之后的代码执行
8. isAlive(): 判断当前线程是否还存活9
9. sleep(long l): 线程休眠(单位毫秒)
10. 线程通讯: wait() notify() notifyAll()
四. 线程的优先级
getPriority(): 返回线程优先值
setPriority(int newPriority): 改变线程的优先级
线程优先级越高, 不代表一定会最先执行. 而是抢占时间片的概率增大
五. 线程的生命周期(五种状态)
① 新建: 当一个Thread类或其子类的对象被声明并创建时, 新生的线程对象处于新建状态
② 就绪: 处于新建状态的线程被start()后, 将进入线程队列等待CPU时间片, 此时它已具备了运行的条件
③ 运行: 当就绪的线程被调度并获得处理器资源时, 便进入运行状态, run()方法定义了线程的操作和功能
④ 阻塞: 在某种特殊情况下, 被认为挂起或执行输入输出操作时, 让出CPU并临时终止自己的执行, 进入阻塞状态
⑤ 死亡: 线程完成了它的全部工作或线程被提前强制性的终止
线程的补充
java中线程分为两类: 一种是守护线程, 一种是用户线程
①. 它们几乎每个方面都是相同的, 唯一的区别是判断JVM何时离开.
②. 守护线程是用来服务用户线程的, 通过再start()方法前调用thread.setDaemon(true)可以把一个用户线程编程一个守护线程
③. java垃圾回收就是一个典型的守护线程
④. 若JVM中都是守护线程, 当前JVM将退出
六. 使用多线程的优点
背景: 只使用单个线程完成多个任务(调用多个方法), 肯定比用多个线程类完成用的时间更短, 为何仍需多线程呢?
1. 提高应用程序的响应. 对图形化界面更有意义, 可增强用户体验
2. 提高计算机系统CPU的利用率
3. 改善程序结构. 将即长又复杂的进程分为多个线程, 独立运行, 利于理解和修改
七. 线程安全问题
- 线程安全问题存在的原因?
由于一个线程在操作共享数据过程中, 未执行完毕的情况下, 另外的线程参与进来, 导致共享数据存在了安全问题
- 如何解决线程的安全问题
必须让一个线程操作共享数据完毕以后, 其他线程才有机会参加与共享数据的操作
- java如何实现线程的安全: 线程的同步机制
方式一: 同步代码块
synchronized(同步监视器){
// 需要被同步的代码块(即为操作共享数据的代码)
}
1. 共享数据: 多个线程共同操作的同一个数据(变量)
2. 同步监视器: 由一个类的对象来充当. 那个线程获取此监视器, 谁就执行{}里被同步的代码. 俗称:锁
要求: 所有的线程必须共用同一把锁
注: 在实现的方式中, 考虑同步的话, 可以使用this来充当锁. 但是在继承的方式中, 慎用this
方式二: 同步方法
将操作共享数据的方法声明为synchronized. 即此方法为同步方法, 能够 保证当其中一个线程执行此方法时, 其他线程在外等待直至此线程执行完此方法
同步线程的弊端: 由于同一个时间只能有一个线程访问共享数据, 效率变低了
八. 死锁
-
死锁: 不同的线程分别占用对方需要的同步资源不放弃, 都在等待对方放弃自己需要的同步资源, 就形成了线程的死锁
-
死锁的解决方法: ① 专门的算法、原则 ②尽量减少同步资源的定义
死锁代码 一.
static StringBuffer sBuffer1 = new StringBuffer();
static StringBuffer sBuffer2 = new StringBuffer();
public static void main(String[] args) {
new Thread() {
@Override
public void run() {
synchronized (sBuffer1) {
try {
Thread.currentThread().sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
sBuffer1.append("A");
synchronized (sBuffer2) {
sBuffer2.append("B");
System.out.println(sBuffer1.toString());
System.out.println(sBuffer2.toString());
}
}
}
}.start();
new Thread() {
@Override
public void run() {
synchronized (sBuffer2) {
try {
Thread.currentThread().sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
sBuffer1.append("C");
synchronized (sBuffer1) {
sBuffer2.append("D");
System.out.println(sBuffer1.toString());
System.out.println(sBuffer2.toString());
}
}
}
}.start();
}
二.
class A {
public synchronized void foo(B b) {// 锁: A的对象a
try {
Thread.currentThread().sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
b.last();
}
public synchronized void last() {// 锁: A的对象a
}
}
class B {
public synchronized void bar(A a) {// 锁: B的对象b
try {
Thread.currentThread().sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
a.last();
}
public synchronized void last() {// 锁: B的对象b
}
}
class DeckLock implements Runnable {
A a = new A();
B b = new B();
public void init() {
a.foo(b);
}
@Override
public void run() {
b.bar(a);
}
}
public static void main(String[] args) {
DeckLock deckLock = new Test_Thread().new DeckLock();
new Thread(deckLock).start();
deckLock.init();
}
九. 线程通讯
wait()与notify()、notifyAll()
1. wait(): 令当前线程挂起并放弃CPU、同步资源, 使别的线程可访问并修改共享资源, 而当前线程排队等候再次对资源的访问
2. notify(): 唤醒正在排队等待同步资源的线程中优先级最高者结束等待
3. notifyAll(): 唤醒正在排队等待资源的所有线程结束等待
java.lang.Object提供的这三个方法只有在synchronized方法或synchronized代码块中才能使用, 否则会报java.long.IllegalMonitorStateException异常