1, 线程相关概念
1), 线程与进程的区别
线程是操作系统调度的最小单元, 也叫轻量级进程, 而进程是操作系统中的应用程序, 在进程中可以创建多个线程.
2), 上下文切换
我们知道现代处理器都是多核的, 几核处理器只能同时处理几个线程, 多线程执行程序看起来是同时进行, 实际上是 CPU 在多个线程之间快速切换执行, 这中间就涉及到上下问切换, 所谓的上下文切换就是指一个线程 T 被分配的时间片用完了之后, 线程的信息被保存起来, CPU 执行另外的线程, 再到 CPU 读取线程 T 的信息并继续执行 T 的过程.
2, 线程实现方式
1), 继承 Thread 类
由于类的单继承性, 使用这种方式实现就无法再去继承其他的线程, 有局限, 耦合度高.
- public class MyThread extends Thread{
- @Override
- public void run() { super.run();
- System.out.println("我继承了 Thread 类...");
- }
- }
2), 实现 Runnable 接口
大多数都用这种方式实现, 很灵活, 重写 run() 方法即可, run() 方法没有返回值.
3), 实现 Callable 接口
若是想获取到线程的执行结果, 那就用这种方式, 它和实现 Runnable 接口的区别是要重写 call() 方法, call() 方式是有返回值的, 返回的 Object 是任务的执行结果, 可以用 Future 接口的实现类 FutureTask 来接收, 并调用 get() 方法获取到执行结果. 另外 call() 方法可抛出异常, 而 run() 方法是不能抛出异常的
- public class Test {
- public static void main(String[] args) throws Exception {
- Thread thread1 = new MyThread();
- thread1.start();
- Thread thread2 = new Thread(new MyRunnable());
- thread2.start();
- Callable callable = new MyCallable();
- FutureTask<String> future = new FutureTask<>(callable);
- ExecutorService executorService = Executors.newSingleThreadExecutor();
- executorService.submit(future);
- System.out.println(future.get());
- executorService.shutdown();
- }
- static class MyRunnable implements Runnable{
- @Override
- public void run() {
- System.out.println("我实现了 Runnable 接口...");
- }
- }
- static class MyCallable implements Callable{
- @Override
- public String call() throws Exception {
- return "我实现了 Callable 接口...";
- }
- }
- }
执行结果:
我继承了 Thread 类...
我实现了 Runnable 接口...
我实现了 Callable 接口...
3, 线程状态
根据 jdk 中 Thread 类的 State 内部类, 线程有 6 种状态, 下次面试官问你线程有几种状态, 你可以很有底气的回答: 6 种, 如下左图, 右图是线程状态之间的转换.
这里注意: 线程在等待进入 synchronzed 方法或者 synchronized 块时的线程状态时 BLOCKED, 而在等待进入 lock 锁时的状态是 WAITING 或者 TIME_WAITING, 因为 lock 是用 LockSupport 实现的 (源码还没研究).
测试如下:
- public class MyService {
- public static synchronized void serviceMethod1(){
- try{
- System.out.println(Thread.currentThread().getName+"进入了业务方法");
- Thread.sleep(millis: 1000);
- } catch (Execption e){
- e.printStackTrace();
- }
- }
- public static void serviceMethod2(){
- ReentrantLock reentrantLock = new ReentrantLock();
- reentrantLock.lock();
- System.out.println(Thread.currentThread().getName()+"进入了业务方法");
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- reentrantLock.unlock();
- }
- }
- public class MyThread1 extends Thread{
- @Override
- public void run() {
- MyService.serviceMethod1();
- }
- public class MyThread2 extends Thread{
- @Override
- public void run() {
- MyService.serviceMethod1();
- }
- }
- public class MyThread3 extends Thread{
- @Override
- public void run() {
- MyService.serviceMethod2();
- }
- }
- public class MyThread4 extends Thread{
- @Override
- public void run() {
- MyService.serviceMethod2();
- }
- }
- public class ThreadStatusTest {
- public static void main(String[] args){
- test1();
- test2();
- }
- public static void test1(){
- MyThread1 t1 = new MyThread1();
- t1.setName("a");
- t1.start();
- MyThread2 t2 = new MyThread2();
- t2.setName("b");
- t2.start();
- System.out.println("t2 的状态:"+t2.getState());
- }
- public static void test2(){
- MyThread3 t3 = new MyThread3();
- t3.setName("a");
- t3.start();
- MyThread4 t4 = new MyThread4();
- t4.setName("b");
- t4.start();
- System.out.println("t4 的状态:"+t4.getState());
- }
- }
test1() 运行结果:
a 进入了业务方法
t2 的状态: RUNNABLE
b 进入了业务方法
test2() 运行结果:
a 进入了业务方法
t4 的状态: RUNNABLE
b 进入了业务方法
4, 常用方法
1),start() 启动线程的一种方式, 线程调用 start() 方法后由 NEW 状态变为 RUNNABLE 状态中的 READY 状态, 等待 CPU 分配时间片, 它是 Thread 类的方法;
2), run() 所需实现的逻辑写在 run 方法里, 线程获得时间片后由 READY 状态变为 RUNNING, 并自动调用 run() 方法, 当然 run() 方法也可以直接调用, 那它就是普通方法, 和线程无关;
3),sleep(...) 线程由 RUNNABLE 状态变为 TIMED_WAITING 状态, 调用此方法会抛出 InterruptedException 异常, 线程自己拥有系统资源, 并等待时间到了, 自己醒来, 这是和 wait() 方法的主要区别;
4),wait() notify() notifyAll() 这三个都是 Object 类的方法, 它们是配合使用的, 调用 wait() 方法的线程状态由 RUNNABLE 中的 RUNNING 变为 WAITING 状态, 并且此对象是不占有系统资源的, 当调用 notify() 或 notifyAll() 方法后线程又进入 RUNNABLE 中的 READY 状态, 等待获取 CPU 时间片;
5),join(...) 等待线程对象销毁, 线程状态由 RUNNABLE 变为 WAITING 或者 TIMED_WAITING;
6),interrupt() 中断线程, 线程状态变为 TERMINATED;
7),yield() , 线程状态由 RUNNING 变为 READY, 即由运行中变为就绪状态, 向处理器表示自己愿意放弃当前 CPU 资源 (让出自己的执行时间), 但放弃时间不确定, 有可能刚刚放弃, 马上又获得 CPU 时间片, 所有此方法并不能保证其它线程一定执行, 调用此方法的线程一定不执行, 而是看 CPU 是否分配了时间片, 并且它只会让优先级不低于当前线程的线程执行, 优先级比它低的是没有机会执行的.
5, 线程的优先级
线程优先级为 1-10(由低到高), 默认优先级是 5, 优先级高的线程分配的时间片的数量要低于优先级低的, 我们可以调用 Thread.setPriority(int) 方法来为线程设置优先级, 在设置线程优先级时应注意, 争对频繁阻塞的, 如休眠, IO, 数据库等任务的线程应设置较高的优先级, 对于偏重计算的, 如需要较多的 CPU 时间或者偏运算的线程则应设置较低的优先级, 确保处理器不会被独占.
6, 线程间的通信
1),volatile synchronized
这两个关键字可以实现线程间的通讯, 我们知道每个线程都有自己的工作内存, 并且它们还有共享内存, 线程对一个变量修改时会先从共享内存中读取这个变量到自己私有的工作内存中, 若是一个普通变量则修改后刷新到主内存中的时机时随机的, 若是 volatile 变量 (可见性和有序性), 这时另一个线程来读这个变量, 则它会被立即刷新到主内存中去, 让后面读取的线程能看到变化, 这就实现了两个线程之间的通信.
synchronized 实现线程之间的同步, B 线程必须等到 A 线程释放锁才能获得相应的资源, 这是线程之间的一种通信方式.
2), 等待通知机制 wait() notify() notifyAll()
一个过程从一个线程开始, 在另一个线程结束, 前者是生产者, 后者是消费者, 生成者完成生产, 通知消费者去消费, 完成二者之间的通信.
等待通知的相关方法有
wait(): 调用该方法的线程进入 WAITING 状态, 只有被其他线程通知或者被中断才会返回, 调用该方法后, 会释放对象的锁;
wait(long): 超时等待一段时间, 这里的参数时间是毫秒, 也就是等待长达 n 毫秒, 如果没有通知就超时返回;
wait(long,int): 超时时间为 long 毫秒 + int 纳秒;
notify(): 通知一个在对象等待的线程, 使其从 wait() 方法返回, 而返回的前提时该线程获取了对象的锁;
notifyAll(): 通知所有等待在该对象上的线程.
以上方法都来自 java.lang.Object 类中, 所以只要是对象就可以调用它们.
wait(),notify() 和 notifyAll() 调用时需要注意:
a, 使用 wait(),notify() 和 notifyAll() 时需要先对调用对象加锁;
b, 调用 wait() 方法后, 线程状态由 RUNNING 变为 WAITING, 并将当前线程放置到对象的等待队列;
c,notify() 或者 notifyAll() 方法调用后, 等待线程依旧不会从 wait() 返回, 需要调用 notify() 或者 notifyAll() 的线程释放锁之后, 等待线程才有机会从 wait() 返回;
d,notify() 方法将等待队列中的一个等待线程从等待队列中移到同步队列中, 而 notifyAll() 方法将等待队列中所有的线程全部移到同步队列, 被移动的线程状
态由 WAITING 变为 BLOCKED;
e, 从 wait() 方法返回的前提是获得了调用对象的锁.
A 线程调用 wait() 方法会释放持有的对象监视器, 进入等待状态, 等 B 线程执行完了后, 调用 notify() 或者 notifyAll() 方法唤醒 A 线程
3), 管道输入, 输出流
管道流专门用于线程之间的通信, 和普通字符字节流的区别是它们操作的是内存而不是硬盘.
主要有四种实现: 字节流: PipedOutputStream,PipedInputStream
字符流: PipedWriter,PipedReader
4),join(...)
当前存在线程 A,B, 若 A 执行了 join() 方法, 意思就是: 当前线程 A 等待 B 线程执行完成之后, 才继续执行, 即完成了 A,B 间的通信.
5),ThreadLocal
ThreadLocal, 即线程变量, 是一个以 ThreadLocal 对象为键, 任意对象为值的存储结构. 可以通过 set(T) 方法来设置一个值, 在当前线程下通过 get() 方法获取原先获取的值.
下面摘抄Java 并发编程的艺术中的一段代码:
- public class Profiler {
- private static final ThreadLocal<Long> TIME_THREADLOCAL = new ThreadLocal<Long>(){
- protected Long initialValue(){
- return System.currentTimeMillis();
- }
- };
- public static final void begin(){
- TIME_THREADLOCAL.set(System.currentTimeMillis());
- }
- public static final long end(){
- return System.currentTimeMillis() - TIME_THREADLOCAL.get();
- }
- public static void main(String[] args) throws InterruptedException {
- Profiler.begin();
- TimeUnit.SECONDS.sleep(1);
- System.out.println("Cost:"+Profiler.end() +"mills");
- }
- }
运行结果:
Cost: 1005 mills
来源: https://www.cnblogs.com/-Marksman/p/9322589.html