现在操作系统中进程的定义:具有独立功能的程序在一个数据集合上的一次运行,是系统进行资源分配和调度的独立单位。
线程:轻量级进程,是系统调度的最小单元,拥有各自的计数器、堆栈和局部变量等属性,并且能够访问共享的内存变量。
Java程序是天生的多线程程序,通过JMX(thredMXBean)查看一个普通的java程序包含哪些线程。使用多线程的优势:
1)更多的处理器核心
2)更快的响应速度
3)多线程编程模型让程序员专注于更好的问题解决
1、线程优先级
Java线程中通过一个整形成员变量控制优先级,优先级范围是1~10,默认为5,优先级高的线程分配时间片数量要多于优先级低的线程,一般偏重计算的线程给予较低的优先级,确保处理器不被独占,有些操作系统不理会java优先级的设定,如MAC OS X 10.10,Ubuntu 14.04.
2、线程的状态
- NEW:初始状态,线程被创建,未被调用start()方法
- RUNABLE:运行状态,就绪和运行的统称
- TIMED_WAITING:超时等待,指定时间内返回(如调用线程的sleep() 方法)(右上BLOCKED)
- WAITING:等待状态,表示线程进入等待队列(如调用对象的await()方法),需要其他进程做出通知后被唤醒进入阻塞队列(如调用对象的notify()方法)(左下BLOCKED)
- BLOCKED:阻塞状态,阻塞于锁(synchronized) (右下BLOCKED)
- TERMINATED:终止
以上三种阻塞状态分别对应以下三种队列:
等待队列:线程调用对象wait()方法
同步队列:线程获取对象锁失败或则被notify()唤醒
其他:线程调用Thread.sleep()方法让出时间片不释放锁
3、Object对象的wait()和notify()
wait():调用线程会挂起自己让出 CPU 时间片,并将自身加入锁定对象的 Wait Set 中,释放对象的监视器锁(monitor)让其他线程可以获得。
notify():方法则会释放监视器锁的同时,唤醒对象 Wait Set 中等待的线程,顺序是随机的不确定。
wait和notify必须配合synchronized使用,而且wait必须在notify前用,wait后就会进入notify所在的线程,notify后唤醒wait所在的线程,但是wait所在的线程仍然没有获取锁,需要等待notify所在的线程释放锁。
4、Thread类的sleep()、yield()方法
Thread.sleep(long) :该方法是属于Thread类的静态方法。其基本语义是使当前运行的线程暂停一段时间。实现细节是把当前线程放入就绪线程队列中,直到睡眠时间到期才可被调度为执行线程(在时间到期前无法被调度为执行线程)。
Thread.yield():该方法暂停当前正在执行的线程,并执行其他线程。但是,实际中无法保证yield()达到让步的目的,因为,让步的线程可能被线程调度程序再次选中。
5、方法比较
thread.join():将指定的线程加入到当前线程,可以将两个交替执行的线程合并为顺序执行的线程。比如在线程B中调用了线程A的Join()方法,直到线程A执行完毕后,才会继续执行线程B。
1)sleep()和yield()区别:调用线程的sleep()方法,可以使比其低的优先线程运行;调用线程的yeild()方法,只让给同优先级运行。
2)join()方法是通过调用线程的wait()方法来达到同步的目的。例如:A线程调用B线程的join(),相当于A线程调用B线程wait()方法,A线程进入阻塞状态,直到B线程执行完才继续执行。
3)sleep()和wait()的区别:①sleep()是Thread类中的方法,wait()是Object类中的方法;②sleep()和wait()的执行,线程都将不占用cpu资源,但是sleep()不会释放对象锁,sleep()结束后,线程进入runnable状态,wait()执行后线程进入waitting状态,只有等待其他线程的notify()/notifyall()才能被唤醒重新获得cpu执行时间,如果没有获得对象锁则进入到Blocked 状态。
4)notify()/wait()的区别:notify()作用唤醒请求队列中的一个线程,而notifyall()唤醒请求队列中所有的线程。notify被调用后,不会像wait()一样马上阻塞线程的运行,而是继续运行,直到调度完成或则让出object的锁。正如上面所提,被唤醒的线程才能继续竞争对象锁。
5、demo 样例来解释 Wait 和 Notify 的功能
import java.util.concurrent.TimeUnit;
public class WaitNotify {
public static void main(String[] args) {
final Object A = new Object();
final Object B = new Object();
Thread t1 = new Thread("t1-thread") {
@Override
public void run() {
synchronized (A) {
System.out.println(Thread.currentThread().getName() + "拿到 A 的监视器锁");
System.out.println(Thread.currentThread().getName() + "尝试获取 B 的监视器锁");
try {
System.out.println(Thread.currentThread().getName() + "休眠 2s,不释放 A 的监视器锁");
TimeUnit.SECONDS.sleep(2);
System.out.println(Thread.currentThread().getName() + "挂起自己,释放 A 的监视器锁");
A.wait();
System.out.println(Thread.currentThread().getName() + "被唤醒,等待获取 B 的监视器锁");
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (B) {
System.out.println(Thread.currentThread().getName() + "拿到 B 的监视器锁");
B.notify();
}
}
}
};
Thread t2 = new Thread("t2-thread") {
@Override
public void run() {
synchronized (B) {
System.out.println(Thread.currentThread().getName() + "拿到 B 的监视器锁");
System.out.println(Thread.currentThread().getName() + "尝试获取 A 的监视器锁");
synchronized (A) {
System.out.println(Thread.currentThread().getName() + "拿到 A 的监视器锁");
try {
System.out.println(Thread.currentThread().getName() + "休眠 2s,不释放 A 的监视器锁");
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "挂起自己,释放 A 的监视器锁,唤醒 t0");
A.notify();
}
try {
System.out.println(Thread.currentThread().getName() + "休眠 2s,不释放 B 的监视器锁");
TimeUnit.SECONDS.sleep(2);
System.out.println(Thread.currentThread().getName() + "挂起自己,释放 B 的监视器锁");
B.wait();
System.out.println(Thread.currentThread().getName() + "被唤醒");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
t1.start();
t2.start();
}
}
t1-thread拿到 A 的监视器锁
t2-thread拿到 B 的监视器锁
t1-thread尝试获取 B 的监视器锁
t2-thread尝试获取 A 的监视器锁
t1-thread休眠 2s,不释放 A 的监视器锁
t1-thread挂起自己,释放 A 的监视器锁
t2-thread拿到 A 的监视器锁
t2-thread休眠 2s,不释放 A 的监视器锁
t2-thread挂起自己,释放 A 的监视器锁,唤醒 t0
t2-thread休眠 2s,不释放 B 的监视器锁
t1-thread被唤醒,等待获取 B 的监视器锁
t2-thread挂起自己,释放 B 的监视器锁
t1-thread拿到 B 的监视器锁
t2-thread被唤醒
Process finished with exit code 0