- 学习记录贴:
1 线程调度
一个CPU在某一时刻只能执行一条指令,线程只有得到 CPU的时间片,也就是使用权才可以执行指令。
- 分时调度模型
-
- 所有线程轮流获得CPU的使用权;
-
- 平均分配每个线程占用CPU的时间片。
- 抢占式调度模型
-
- 优先让优先级高的线程使用CPU;
-
- 优先级相同则随机选择一个;
-
- 优先级高的线程获取的CPU时间片相对多一些;
-
- 这是java使用的线程调度模型
-
- 优先级范围:1-10,默认为5(10的优先级最高)
2 java Thread类常用的几个方法
public final void setPriority(int newPriority)
设置线程优先级public final int getPriority()
获取线程优先级public final void join()
等待该线程终止,才能执行下面的其他进程public static void sleep(long millis)
线程休眠,参数为休眠时间,单位为毫秒public static void yield()
暂停当前正在执行的线程对象,并执行其他线程(线程礼让)public final void stop()
让线程停止,过时(由于暴力杀掉进程的所有步骤,不安全),但是还可以使用public void interrupt()
中断线程。 把线程的状态(wait(),join(),sleep())终止,并抛出一个InterruptedException,线程并未终止,将会继续运行下面的步骤(安全)public final void setDaemon(boolean on)
将该线程标记为守护线程或用户线程
当正在运行的线程都是守护线程时,Java 虚拟机退出。
该方法必须在启动线程前调用(即 start() 之前)
3 线程的生命周期
- 新建:创建线程对象
- 就绪:有执行资格,没有执行权
- 运行:有执行资格,有执行权
-
- 阻塞:一些操作导致。没有执行资格和执行权。
另一些操作可以激活它,激活后处于就绪状态
- 阻塞:一些操作导致。没有执行资格和执行权。
- 死亡:线程对象变成垃圾,等待被回收
- 图解如下:
4 实现多线程的方式(2种)
4.1 继承Thread 类
- 步骤:
1)自定义MyThread 类继承 Thread 类;
2)在 MyThread 类中重写 run();
3)创建 MyThread 类的对象;
4)启动线程对象( start() 方法)。
MyThread t1 = new MyThread();
MyThread t2 = new MyThread();
MyThread t3 = new MyThread();
- 问题1:为什么要重写 run()?
run() 中封装的是被线程执行的代码 - 问题2: run() 和 start() 的区别?
run() 直接调用仅仅是普通方法
start() 先启动线程,再由JVM 调用run() 方法。
4.2 实现 Runnable 接口
- 步骤:
1)自定义MyRunnable 类实现 Runnable 接口;
2)在 MyRunnable 类中重写 run();
3)创建 MyRunnable 类的对象;
4)创建 Thread 类的对象,并把 3)步骤的对象作为构造参数传递。
MyRunnable mr = new MyRunnable();
Thread t1 = new Thread(mr, "liuBei");
Thread t2 = new Thread(mr, "guanYu");
Thread t3 = new Thread(mr, "zhangFei");
- 问题:有了方式1,为何还要有方式2?
-
- 1)可以避免由于 Java 单继承带来的局限性(Java 单继承多实现),
可以在实现 Runnable 的同时实现其它接口。
- 1)可以避免由于 Java 单继承带来的局限性(Java 单继承多实现),
-
- 2)当创建多个线程时,方式1需要创建等量的 MyThread 对象,而方式2只需要创建一个MyRunnable 对象,然后作为参数多次赋给不同的Thread 对象即可。
相对应地,方式1 MyThread 中的成员变量也会创建多份,方式2 MyRunnable 中的成员变量则只有一份。
适合于多个相同程序的代码去处理同一个资源的情况,较好体现了面向对象的设计思想(分离,少耦合)
- 2)当创建多个线程时,方式1需要创建等量的 MyThread 对象,而方式2只需要创建一个MyRunnable 对象,然后作为参数多次赋给不同的Thread 对象即可。
注:还有第三种:Callable 接口(但这种方式依赖于线程池存在,无法直接实现多线程)
5 死锁问题及其代码
- 死锁:是指两个或多个线程在执行过程中,因争夺资源而产生的一种相互等待的现象(同步嵌套)
- 代码示例:
public class MyLock {
//创建两把锁对象
public static final Object objA = new Object();
public static final Object objB = new Object();
}
/********************************************************************/
public class DieLock implements Runnable{
private boolean flag;
public DieLock(boolean flag) {
this.flag=flag;
}
@Override
public void run() {
if(flag) {
synchronized(MyLock.objA) {
System.out.println(Thread.currentThread().getName()+":if objA");
synchronized(MyLock.objB) {
System.out.println(Thread.currentThread().getName()+":if objB");
}
}
}else {
synchronized(MyLock.objB) {
System.out.println(Thread.currentThread().getName()+":if objB");
synchronized(MyLock.objA) {
System.out.println(Thread.currentThread().getName()+":if objA");
}
}
}
}
}
/********************************************************************/
public class DieLockDemo {
public static void main(String[] args) {
Thread t1 = new Thread(new DieLock(true),"Chinese");
Thread t2 = new Thread(new DieLock(false),"American");
t1.start();
t2.start();
//输出:
//Chinese:if objA
//American:if objB
//然后卡住运行不下去(产生死锁)
}
}
6 线程间通信问题
不同种类的线程针对同一个资源的操作(生产者-消费者模式)
- 等待唤醒
- Object类中提供了三个方法:
wait()
:等待(立即释放锁)
notify()
:唤醒单个线程
notifyAll()
:唤醒所有线程 - 问题:为什么定义在Object 中,而不是Thread 中?
因为这是些方法的调用必须通过锁对象,而锁对象可以是任意对象