1. 线程
- 线程必须有有一个父进程
- 线程可以拥有自己的堆栈,自己的程序计数器和自己的局部变量,但不拥有系统资源,与父进程的其他线程共享该进程的所有资源
- 所有线程对象都必须是Thread类或其子类的实例
- 使用继承Thread类的方法来创建线程类时,多个线程之间无法共享线程类的实例变量
public class FirstThread extends Thread {
private int i;
@Override
public void run() {
for (;i<100;i++) {
System.out.println(getName()+" "+i);
}
}
}
- 使用Runnable接口的方式创建的多个线程可以共享线程类的实例
public class SecondThread implements Runnable {
private int i;
@Override
public void run() {
for (;i<100;i++) {
System.out.println(Thread.currentThread().getName()+" "+i);
}
}
}
- 使用Callable和Future创建线程时线程的执行体就是该Callable对象的call()方法
FutureTask<Integer> task = new FutureTask<Integer>((Callable<Integer>)()->{
int i = 0;
for (;i<100;i++) {
System.out.println(Thread.currentThread().getName()+"的i的值:"+i);
}
return i;
});
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName()+" 的i的值:"+i);
if (i == 20) {
new Thread(task,"有返回值的线程").start();
}
}
System.out.println("子线程的返回值:"+task.get());
2. 线程的生命周期
- 新建
- 就绪
- 使用start()方法后处于就绪状态(只能对新建状态的线程调用,否则会抛出异常)
- 阻塞
- 线程调用sleep()方法主动放弃所占用的处理器资源,进入阻塞状态
- 调用了阻塞式IO方法,方法返回前线程阻塞
- 线程试图获得一个同步监视器,但该同步监视器被其他线程所持有
- 线程在等待某个通知(notify)
- 程序使用了suspend方法将该线程挂起
- 死亡
- run()或call()方法执行完成,线程正常结束
- 线程抛出一个未捕获的Exception或Error
- 使用了该线程的stop()方法结束该线程
3. 控制线程
- join线程
- 当某个程序执行流中调用其他线程的join()方法时,调用线程将被阻塞,直到join()方法加入的join线程执行完为止
- 后台线程(Daemon Thread)
- 任务是为其他线程提供服务,在后台运行
- 将线程设置为后台进程时setDaemon(true)必须在start()方法之前调用
- 线程睡眠
- 当前线程调用sleep()方法进入阻塞状态后,在其睡眠时间内不会获得执行的机会
- 线程让步
- 调用yield()方法,不好阻塞该线程,只是让该线程处于就绪状态
- 只是让该线程暂停一下,让系统的线程调度器重新调度一次(系统调度该线程时会再次执行)
- 当调用了yield()方法暂停之后,只有优先级与当前线程相同,或者优先级比当前线程更高的处于就绪状态的线程才会获得执行的机会
- 改变线程优先级
- 调用setPriority()方法实现
- 优先级最大为10,最小为1
4. 线程同步
- 任何时刻只能有一个线程可以获得对同步监视器的锁定,当同步代码块执行完成后该线程会释放对该同步监视器的锁定
- 强制要求加锁和释放锁要出现在一个块结构中,当取得了多个锁时,必须以相反的顺序释放,且必须在与所有锁被获取时相同的范围内释放锁
- 一个线程可以对被加锁的ReentrantLock锁再次加锁,被锁保护的代码可以调用另一个被相同锁保护的方法
5. 线程通信
- wait()导致当前线程等待,知道其他线程调用该同步监视器的notify()或notifyAll()方法来唤醒该线程
- notify()唤醒在此同步监视器上等待的单个线程,选择是任意性的
- notifyAll()唤醒在此同步监视器上等待的所有线程
- 使用Condition可以让那些已经得到Lock对象却无法继续执行的线程释放Lock对象
- Condition对象也可以唤醒其他处于等待的线程
- Condition对象被绑定在一个Lock实例上
- BlockingQueue中当生产者向其中放入元素时,如果该队列已满,在该线程被阻塞,当消费者取出元素时,如果该队列已空,则该线程被阻塞
6. 线程组
- 对线程组的控制相当于同时控制这批线程
- 用户所创建的所有线程都属于指定线程组,如果没有显示的指定,则该线程属于默认线程组
- 通常,子线程和创建它的父线程处于同一个线程组内
- 线程允许中途不能改变它所属的线程组
- 后台线程组中,当后台线程组的最后一个线程执行结束或最后一个线程被销毁后,后台线程将自动销毁
- ThreadLocal为每一个使用该变量的线程都会提供一个变量值的副本,使每一个线程都可以独立的改变自己的副本,而不会和其他线程的副本冲突
7. 线程池
- 调用线程池的shutdown()方法将会启动线程池的关闭序列,不在接受新任务,但会将所有已提交的任务执行完成,当所有线程完成后池中的所有线程都会死亡
- 调用shutdownNow()关闭线程池时,会停止所有正在执行的活动任务,暂停处理正在等待的任务,并返回等待执行的任务列表
public static void test() {
ExecutorService pool = Executors.newFixedThreadPool(6);
Runnable target = ()-> {
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName()+"的i的值为:"+i);
}
};
pool.submit(target);
pool.submit(target);
pool.shutdown();
}