一、线程
1、线程优先级
//优先级
private int priority;
public final static int MIN_PRIORITY = 1;
public final static int NORM_PRIORITY = 5;
public final static int MAX_PRIORITY = 10;
- 线程执行有优先级,优先级越高先执行机会越大(并不是一定先执行)。
- 优先级用int的priority参数表示。
- 线程优先级最高为10,最低为1。默认为5
2、线程状态
public enum State {
NEW,
RUNNABLE,
BLOCKED,
WAITING,
TIMED_WAITING,
TERMINATED;
}
public State getState() {
return State.values()[nativeGetStatus(started)];
}
Thread对象共有6种状态:
- NEW(新建)
- RUNNABLE(运行)
- BLOCKED(阻塞)
- WAITING(等待)
- TIMED_WAITING(有时间的等待)
- TERMINATED(终止);
- RUNNABLE:对应”就绪”和”运行”两种状态,也就是说处于就绪和运行状态的线程在java.lang.Thread中都表现为”RUNNABLE”
- BLOCKED:对应”阻塞”状态,此线程需要获得某个锁才能继续执行,而这个锁目前被其他线程持有,所以进入了被动的等待状态,直到抢到了那个锁,才会再次进入”就绪”状态
- WAITING:对应”阻塞”状态,代表此线程正处于无限期的主动等待中,直到有人唤醒它,它才会再次进入就绪状态
- TIMED_WAITING:对应”阻塞”状态,代表此线程正处于有限期的主动等待中,要么有人唤醒它,要么等待够了一定时间之后,才会再次进入就绪状态。
3、线程构造方法
构造方法最终调用了init方法
private void init(ThreadGroup g, Runnable target, String name,
long stackSize) {
init(g, target, name, stackSize, null);
}
四个参数含义:
- ThreadGroup g
指定当前线程的线程组,未指定时线程组为创建该线程所属的线程组。线程组可以用来管理一组线程,通过activeCount() 来查看活动线程的数量。其他没有什么大的用处。 - Runnable target
指定运行其中的Runnable,一般都需要指定,不指定的线程没有意义,或者可以通过创建Thread的子类并重新run方法。 - String name
线程的名称,不指定自动生成。 - long stackSize
预期堆栈大小,不指定默认为0,0代表忽略这个属性。与平台相关,不建议使用该属性。
4、公共方法
Thread Thread.currentThread() :获得当前线程的引用。获得当前线程后对其进行操作。
Thread.UncaughtExceptionHandler getDefaultUncaughtExceptionHandler() :返回线程由于未捕获到异常而突然终止时调用的默认处理程序。
int Thread.activeCount():当前线程所在线程组中活动线程的数目。
void dumpStack() :将当前线程的堆栈跟踪打印至标准错误流。
int enumerate(Thread[] tarray) :将当前线程的线程组及其子组中的每一个活动线程复制到指定的数组中。
Map getAllStackTraces() :返回所有活动线程的堆栈跟踪的一个映射。
boolean holdsLock(Object obj) :当且仅当当前线程在指定的对象上保持监视器锁时,才返回 true。
boolean interrupted() :测试当前线程是否已经中断。
void setDefaultUncaughtExceptionHandler(Thread.UncaughtExceptionHandler eh) :设置当线程由于未捕获到异常而突然终止,并且没有为该线程定义其他处理程序时所调用的默认处理程序。
void sleep(long millis) :休眠指定时间
void sleep(long millis, int nanos) :休眠指定时间
void yield() :暂停当前正在执行的线程对象,并执行其他线程。意义不太大
void checkAccess() :判定当前运行的线程是否有权修改该线程。
ClassLoader getContextClassLoader() :返回该线程的上下文 ClassLoader。
long getId() :返回该线程的标识符。
String getName() :返回该线程的名称。
int getPriority() :返回线程的优先级。
StackTraceElement[] getStackTrace() :返回一个表示该线程堆栈转储的堆栈跟踪元素数组。
Thread.State getState() :返回该线程的状态。
ThreadGroup getThreadGroup() :返回该线程所属的线程组。
Thread.UncaughtExceptionHandler getUncaughtExceptionHandler() :返回该线程由于未捕获到异常而突然终止时调用的处理程序。
void interrupt() :中断线程。
boolean isAlive() :测试线程是否处于活动状态。
boolean isDaemon() :测试该线程是否为守护线程。
boolean isInterrupted():测试线程是否已经中断。
void join() :等待该线程终止。
void join(long millis) :等待该线程终止的时间最长为 millis 毫秒。
void join(long millis, int nanos) :等待该线程终止的时间最长为 millis 毫秒 + nanos 纳秒。
void run() :线程启动后执行的方法。
void setContextClassLoader(ClassLoader cl) :设置该线程的上下文 ClassLoader。
void setDaemon(boolean on) :将该线程标记为守护线程或用户线程。
void start():使该线程开始执行;Java 虚拟机调用该线程的 run 方法。
String toString():返回该线程的字符串表示形式,包括线程名称、优先级和线程组。
注意几个方法的区别:
1、针对线程中断标记的方法
- interrupt():中断本线程(将中断状态标记为true)
- isInterrupted():检测本线程是否已经中断 。如果已经中断,则返回true,否则false。中断状态不受该方法的影响。 如果中断调用时线程已经不处于活动状态,则返回false。
- interrupted():检测当前线程是否已经中断 。如果当前线程存在中断,返回true,并且修改标记为false。再调用isIterruoted()会返回false。如果当前线程没有中断标记,返回false,不会修改中断标记。
二、创建线程三种方式
1、通过继承Thread类创建线程
public class MyThread extends Thread{
//run方法就是线程执行体
@Override
public void run() {
super.run();
}
}
new MyThread().start();
2、通过实现接口Runnable创建线程
public class MyRunnable implements Runnable{
@Override
public void run() {
}
}
MyRunnable run = new MyRunnable();
new Thread(run).start();
3、通过实现接口Callable和Future创建线程
public class MyCallable implements Callable {
@Override
public Integer call() throws Exception {
return 10;
}
}
//1、创建MyCallable
MyCallable myCallable = new MyCallable();
//2、创建FutureTask并传入Callable实例;
FutureTask futureTask = new FutureTask(myCallable);
//3、创建Thread并将FutureTask实例传入
new Thread(futureTask).start();
1、FutureTask其实是实现了Runnable,本质上还是个Runnable;
2、FutureTask实现了run()方法,其中调用了Callable的call方法;
4、CompletableFuture
Java异步编程离不开Future接口,但是Future接口提供的方法使用起来不够灵活。用户如果需要异步执行多个任务,并且这些任务具有先后依赖关系。基于传统的方式我们需要大量使用锁,CountDownLatch和CyclicBarrier和阻塞队列等,编程十分复杂。
CompletableFuture是Future的增强版,提供了一系列的同步或异步任务执行操作。除此之外还能够对异步任务多个阶段的前后依赖关系进行控制。使用起来十分方便。
long start = System.currentTimeMillis();
CompletableFuture completableFuture1 = CompletableFuture.runAsync(() -> {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("runAsync1");
});
CompletableFuture completableFuture2 = CompletableFuture.runAsync(() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("runAsync2");
});
// 等待两个CompletableFuture都完成的时候,future才会完成
CompletableFuture future = CompletableFuture.allOf(completableFuture1, completableFuture2);
// 阻塞到CompletableFuture1完成
future.get();
// 耗时大于3000毫秒
System.out.println(System.currentTimeMillis() - start);
三、创建线程三种方式的对比
1、采用实现Runnable、Callable接口的方式创建多线程
优势:
线程类只是实现了 Runnable接口或Callable接口,还可以继承其他类。
在这种方式下,多个线程可以共享同一个target对象,所以非常适合多个相同线程来处理同一份资源的情況,从而可以将CPU、代码和数据分开,形成清晰的模型,较好地体现了面向对象的思想。
劣势:
编程稍微复杂,如果要访问当前线程,则必须使用Thread.currentThread()方法。
2、使用继承Thread类的方式创建多线程
优势:
编写简单,如果需要访问当前线程,则无需使用 Thread.currentThread()方法,直接使用this即可获得当前线程。
劣势:
线程类已经继承了Thread类,所以不能再继承其他父类。