基本概念
进程:是指一个内存中运行的应用程序,每个进程都有自己独立的一块内存空间。
线程:线程是指进程中的一个执行流程,一个进程中可以运行多个线程,多个线程共享进程的内存。
幂等性:一个幂等操作的特点是其任意多次执行所产生的影响均与一次执行的影响相同。
竞态:多线程情况下程序计算的正确性依赖于相对时间顺序或线程的交错。导致竞态条件发生的代码区称作临界区。
并发:多个线程操作同一个资源,快速交替(交替执行)。
并行:多个线程操作同时执行(同时执行)。
线程的优缺点
优点:资源利用率高,响应更快。
缺点:线程间上下文切换需要额外的开销,增加资源的消耗。
java默认有几个线程?
main
方法运行的线程,被称为主线程。
GC
线程
java无法直接创建线程
java本身不支持创建线程,实际上都是通过本地方法调用了C++
private native void start0();
创建线程对象的几种方式
1:extends Thread类
public class MyThread extends Thread {}
MyThread thread = new MyThread();thread.start(); // 启动线程
2:implements Runnable接口
public class MyThread implements Runnable {}
Thread thread = new Thread(new Mythread());thread.start(); // 启动线程
3:implements Callable接口
4:Executor框架来创建线程池
线程的几种状态
新建(NEW):线程已经被创建但还没有调用start
方法所处的状态
可运行(RUNNABLE):调用start
方法后,正在Java虚拟机中执行,但是它可能正在等待来自操作系统(如处理器)的其他资源。RUNNABLE
内部又可分为两个状态,Ready就绪状态:当线程有资格运行,但调度程序还没有把它选定为运行线程时线程所处的状态。Running运行状态:线程调度程序从等待队列中选择一个线程作为当前线程时线程所处的状态
阻塞(BLOCKED):运行(running)的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池(lock pool)中,获得锁后会转为就绪状态。
等待(WAITING):运行(running)的线程执行wait()
、join()
、LockSupport.park()
方法,JVM会把该线程放入等待队列(waitting queue)中,调用notify()
、notifyAll()
、LockSupport.unpark()
方法会回到运行状态
超时等待(TIMED_WAITING):运行(running)的线程执行Thread.sleep(long ms)
、t.join(long millis)
方法或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入运行状态。
结束(TERMINATED):当线程的run()方法完成时就认为它结束了
public enum State {
// 新建
NEW,
// 可运行
RUNNABLE,
// 阻塞
BLOCKED,
// 等待
WAITING,
// 超时等待
TIMED_WAITING,
// 结束
TERMINATED;
}
BLOCKED
和WAITING
的区别?
BLOCKED
是指争用synchronized
的monitor
对象而引起的线程阻塞,通过操作系统挂起线程,这里涉及到了系统调用
WAITING
是指调用wait()、join()、LockSupport.park()
而引起的线程阻塞,只是JVM层面的阻塞
线程对象的主要方法
start() 启动线程
// 已经启动的线程再次启动会报IllegalThreadStateException
public synchronized void start()
setPriority() 设置线程优先级,但不能保证一定按照优先级来
public final void setPriority(int newPriority)
thread.setPriority(Thread.MAX_PRIORITY); // 10 最高
thread.setPriority(Thread.MIN_PRIORITY); // 0 最低
thread.setPriority(Thread.NORM_PRIORITY); // 5 默认
join() 调用线程等待该线程完成后,才继续运行。
例如在A线程中调用B.join,则A线程先阻塞等待B执行完成。如果在A中调用A.join,则A直接阻塞
public final void join() throws InterruptedException
join方法只会使当前线程进入等待池并等待该线程执行完毕后才会被唤醒。并不影响同一时刻处在运行状态的其他线程。
// 例一 先执行begin. A,B,end互不影响
System.out.println("main begin");
Thread thread = new Thread(() -> System.out.println(Thread.currentThread().getName()), "A");
Thread thread2 = new Thread(() -> System.out.println(Thread.currentThread().getName()), "B");
thread.start();
thread2.start();
System.out.println("main end");
// 例二 先执行begin. A,B互不影响 end一定是在A后输出
System.out.println("main begin");
Thread thread = new Thread(() -> System.out.println(Thread.currentThread().getName()), "A");
Thread thread2 = new Thread(() -> System.out.println(Thread.currentThread().getName()), "B");
thread.start();
thread2.start();
thread.join(); // thread-main是调用方 thread-A是被调用方
System.out.println("main end");
interrupt() 中断线程
中断一个线程只是为了引起该线程的注意,被中断线程可以决定如何应对中断
public void interrupt()
-
interrupt()的作用是中断本线程(不一定是当前线程,而是指调用该方法的线程)。
本线程中断自己是被允许的;其它线程调用本线程的interrupt()方法时,会通过checkAccess()检查权限。这有可能抛出SecurityException异常。 -
调用线程的
wait()、join()、sleep()
等方法会让它进入等待状态。若线程在等待状态时,调用了它的interrupt()方法,那么它的“中断状态”会被清除并且会收到一个InterruptedException异常。例如,线程通过
wait()
进入等待状态,此时通过interrupt()
中断该线程;调用interrupt()
会立即将线程的中断标记设为true,但是由于线程处于阻塞状态,所以该“中断标记”会立即被清除为false,同时会产生一个InterruptedException的异常。 -
如果不属于前面所说的情况,那么通过interrupt()中断线程时,它的中断标记会被设置为“true”,但是线程任然会继续运行。
-
中断一个“已终止的线程”不会产生任何操作。
-
通常,通过“中断”方式终止处于“等待状态”的线程。
-
如果InterruptedException产生在循环体外则可以终止循环,如果产生于循环体内还会继续循环,需要额外终止。
isInterrupted() 获取中断标识,线程的“中断状态”不受该方法的影响
public boolean isInterrupted()
作用于调用该方法的线程,检查中断标识,但是不会清除中断标识
Thread.interrupted() 返回中断标识,并且清除中断标识
public static boolean interrupted();
作用于当前线程,检查中断标识,并且清除中断标识
线程类的几个静态方法
Thread.sleep(long millis) 线程睡眠
当前线程暂停一段时间让给别的线程去运行,等到睡眠到规定的时间就自动恢复
public static native void sleep(long millis) throws InterruptedException;
Thread.yield() 线程礼让
暂停当前正在执行的线程对象放入等待队列,并从队列中拿一个线程执行(有可能还是拿到的该线程,所以只是让一下)。
public static native void yield();
Thread.interrupted() 返回中断标识,并且清除中断标识
public static boolean interrupted();
作用于当前线程,检查中断标识,并且清除中断标识