线程分配到的时间片多少也就决定了线程使用处理器资源的多少,而线程优先级就是决定线程需要多或少分配一些处理器资源的线程属性
设置线程优先级时,针对频繁阻塞(休眠或者I/O操作)的线程需要设置较高优先级,偏重计算(需要较多CPU时间或者偏运算)的线程则设置较低的优先级
优先级不能作为程序正确性的依赖(操作系统可以不用理会Java线程对于优先级的设定)


阻塞状态是线程阻塞在进入synchronized关键字修饰的方法或代码块(获取锁)时的状态
阻塞在java.concurrent包中Lock接口的线程状态却是等待状态
主要被用作程序中后台调度以及支持性工作
JVM中不存在非Daemon线程的时候,Java虚拟机将会退出(即使Daemon线程有finally代码块也不会执行)
通过调用Thread.setDaemon(true)将线程设置为Daemon线程(需要在启动线程前设置)
contextClassLoader以及可继承的ThreadLocal,同时还会分配一个唯一的ID来标识这个child线程线程start()方法的含义是:当前线程(即parent线程)同步告知JVM只要线程规划器空闲,立即启动调用start()方法的线程
中断状态可以理解为线程的一个标识位属性,它表示一个运行中的线程是否被其他线程进行了中断操作
其他线程通过调用某线程的interrupt()方法对其进行中断操作
线程通过检查自身是否被中断来进行响应,线程通过方法isInterrupted()来进行判断是否
被中断(true表示被中断,清除标志位表示置为false),也可以调用静态方法Thread.interrupted()对当前线程的中断标识位复位
比如Thread.sleep(longmillis)等在声明中抛出InterruptedException的方法,Java虚拟机会先将该线程的中断标识位清除,然后抛出InterruptedException
使用中断操作:
public static void main(String[] args) throws Exception {
Runner one = new Runner();
Thread countThread = new Thread(one, "CountThread");
countThread.start();
// 睡眠1秒,main线程对CountThread进行中断,使CountThread能够感知中断而结束
TimeUnit.SECONDS.sleep(1);
countThread.interrupt(); // 执行该方法后,下面的isInterrupted()返回true
}
}
private static class Runner implements Runnable {
private long i;
@Override
public void run() {
while (!Thread.currentThread().isInterrupted()){
i++;
}
System.out.println("Count i = " + i);
}
}
使用标识位:
public static void main(String[] args) throws Exception {
Runner two = new Runner();
countThread = new Thread(two, "CountThread");
countThread.start();
// 睡眠1秒,main线程对Runner two进行取消,使CountThread能够感知on为false而结束
TimeUnit.SECONDS.sleep(1);
two.cancel(); // 执行该方法使得on=false,进而退出while循环
}
private static class Runner implements Runnable {
private long i;
private volatile boolean on = true;
@Override
public void run() {
while (on){
i++;
}
System.out.println("Count i = " + i);
}
public void cancel() {
on = false;
}
}
volatile:
synchronized:
修饰方法或者以同步块的形式来进行使用
主要确保多个线程在同一个时刻,只能有一个线程处于方法或者同步块中
保证了线程对变量访问的可见性和排他性
使用该关键字对方法/代码块进行加锁,本质是对一个对象的监视器进行获取
对于同步块的实现使用了monitorenter和monitorexit指令;同步方法依靠方法修饰符上的ACC_SYNCHRONIZED来完成

一个线程(生产者)修改了一个对象的值,而另一个线程(消费者)感知到了变化,然后进行相应的操作。对于消费者线程需要执行以下代码判断被生产者修改的变量是否符合预期:
while (value != desire) {
Thread.sleep(1000);
}
doSomething();
但是会出现以下问题:
- 难以确保及时性:如果睡眠时间设置过大,则不能即使发现条件的变化
- 难以降低开销:如果降低睡眠的时间,消费者能更加迅速地发现条件变化,但消耗更多的处理器资源
上述问题通过内置的等待和通知机制解决
等待/通知机制:指一个线程A调用了对象O的wait()方法进入等待状态,另一个线程B调用了对象O的notify()或者notifyAll()方法,线程A收到通知后从对象O的wait()方法返回,进而执行后续操作


等待方(消费者)和通知方(生产者)满足以下范式:
// 消费者
synchronized(对象) {
while(条件不满足) {
对象.wait();
}
对应的处理逻辑
}
// 生产者
synchronized(对象) {
改变条件
对象.notifyAll(); // 此时不会立即释放锁
}
管道输入/输出流和普通的文件输入/输出流或者网络输入/输出流不同之处在于它主要用于线程之间的数据传输,而传输的媒介为内存
如果一个线程A执行了
thread.join()语句,表示当前线程A等待thread线程终止之后才从thread.join()返回
当方法执行时间过长,不会“永久”阻塞调用者,而是会按照调用者的要求“按时”返回
// 对当前对象加锁
public synchronized Object get(long mills) throws InterruptedException {
long future = System.currentTimeMillis() + mills;
long remaining = mills;
// 当超时大于0并且result返回值不满足要求
// 在等待通知范式基础上加上了超时控制
while ((result == null) && remaining > 0) {
wait(remaining);
remaining = future - System.currentTimeMillis();
}
return result;
}
模拟从连接池中获取、使用和释放连接的过程,而客户端获取连接的过程被设定为等待超时的模式,也就是在1000毫秒内如果无法获取到可用连接,将会返回给客户端一个null
public class ConnectionPool {
private LinkedList<Connection> pool = new LinkedList<Connection>();
public ConnectionPool(int initialSize) {
if (initialSize > 0) {
for (int i = 0; i < initialSize; i++) {
pool.addLast(ConnectionDriver.createConnection());
}
}
}
public void releaseConnection(Connection connection) {
if (connection != null) {
synchronized (pool) {
// 连接释放后需要进行通知,这样其他消费者能够感知到连接池中已经归还了一个连接
pool.addLast(connection);
pool.notifyAll();
}
}
}
// fetchConnection方法:指定在多少毫秒内超时获取连接
// 在mills内无法获取到连接,将会返回null
public Connection fetchConnection(long mills) throws InterruptedException {
synchronized (pool) {
// 完全超时
if (mills <= 0) {
while (pool.isEmpty()) {
pool.wait();
}
return pool.removeFirst();
} else {
// 等待-超时模式
long future = System.currentTimeMillis() + mills;
long remaining = mills;
while (pool.isEmpty() && remaining > 0) {
pool.wait(remaining);
remaining = future - System.currentTimeMillis();
}
Connection result = null;
if (!pool.isEmpty()) {
result = pool.removeFirst();
}
return result;
}
}
}
}
面对大量的任务递交进服务器时,如果采用一个任务一个线程的方式,则会创建大量线程,这会使操作系统频繁的进行线程上下文切换,增加系统的负载,而线程的创建和消亡都是需要耗费系统资源的,浪费系统资源
线程池能预先创建了若干数量的线程,并且不能由用户直接对线程的创建进行控制,在这个前提下重复使用固定或较为固定数目的线程来完成任务的执行
线程池的本质: