进程是资源分配的最小单位,线程是CPU调度的最小单位
每运行一个java程序,会产生一个进程,进程包含至少一个线程,每个进程对应一个JVM实例,多个线程共享JVM的堆,每个线程拥有自己的栈
Java采用单线程编程模型,程序会自动创建主线程。
当一个java程序启动的时候,创建的jvm实例中除了main线程即主线程外,还会创建其他的线程,比如GC线程
如何处理线程的返回值
三种实现方式:
- 主线程等待法
- 使用Thread类的join()阻塞当前线程以等待子线程处理完毕
- 通过Callable接口实现 : 通过FutureTask Or 线程池获取
第一种弊端:需要自己实现逻辑,而且不精准(指线程结束后不能立即获得返回值)
第二种弊端:做到比第一种更精准的控制,但是力度不够细
通过FutureTask获取
MyCallable.class
public class MyCallable implements Callable<String> {
@Override
public String call() throws Exception {
Thread.sleep(5000);
return "some message";
}
}
main()
public static void main(String[] args) throws ExecutionException, InterruptedException {
FutureTask<String> futureTask = new FutureTask<>(new MyCallable());
new Thread(futureTask).start();
System.out.println(futureTask.get());
}
当MyCallable没执行完时,futureTask的get()会阻塞直到MyCallable执行完毕
通过线程池获取
public static void main(String[] args) {
ExecutorService executorService = Executors.newCachedThreadPool();
Future<String> future = executorService.submit(new MyCallable());
try {
System.out.println(future.get());
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
} finally {
executorService.shutdown();
}
}
使用线程池好处:
可以提交多个Callable的实现类去让线程池并发的处理结果,方便对这些Callable的类统一管理(这只是视频所说,具体还需要学习线程池方面再说)
线程的状态
Thread的内部类
public enum State {
/**
* Thread state for a thread which has not yet started.
*/
NEW,
/**
* Thread state for a runnable thread. A thread in the runnable
* state is executing in the Java virtual machine but it may
* be waiting for other resources from the operating system
* such as processor.
*/
RUNNABLE,
/**
* Thread state for a thread blocked waiting for a monitor lock.
* A thread in the blocked state is waiting for a monitor lock
* to enter a synchronized block/method or
* reenter a synchronized block/method after calling
* {@link Object#wait() Object.wait}.
*/
BLOCKED,
/**
* Thread state for a waiting thread.
* A thread is in the waiting state due to calling one of the
* following methods:
* <ul>
* <li>{@link Object#wait() Object.wait} with no timeout</li>
* <li>{@link #join() Thread.join} with no timeout</li>
* <li>{@link LockSupport#park() LockSupport.park}</li>
* </ul>
*
* <p>A thread in the waiting state is waiting for another thread to
* perform a particular action.
*
* For example, a thread that has called <tt>Object.wait()</tt>
* on an object is waiting for another thread to call
* <tt>Object.notify()</tt> or <tt>Object.notifyAll()</tt> on
* that object. A thread that has called <tt>Thread.join()</tt>
* is waiting for a specified thread to terminate.
*/
WAITING,
/**
* Thread state for a waiting thread with a specified waiting time.
* A thread is in the timed waiting state due to calling one of
* the following methods with a specified positive waiting time:
* <ul>
* <li>{@link #sleep Thread.sleep}</li>
* <li>{@link Object#wait(long) Object.wait} with timeout</li>
* <li>{@link #join(long) Thread.join} with timeout</li>
* <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li>
* <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li>
* </ul>
*/
TIMED_WAITING,
/**
* Thread state for a terminated thread.
* The thread has completed execution.
*/
TERMINATED;
}
- 新建(NEW) : 创建后尚未启动的线程的状态
- 运行(Runnable) : 包含Running和Ready
- 无限期等待(Waiting) : 不会被分配CPU执行时间,需要显式被唤醒
- 限期等待(Time Waiting) : 在一定时间后会由系统自动唤醒
- 阻塞(Blocked) : 等待获取排他锁
- 结束(Terminated) : 已终止线程的状态,线程已经结束执行 // run()方法执行完毕后或者说main()执行完毕
sleep和wait的区别
最主要的区别:
- Thread.sleep只会让出CPU,不会导致锁行为的改变
- Object.wait不仅让出CPU,还会释放已经占有的同步资源锁
notify和notifyAll的区别
- notifyAll 会让所有处于等待池的线程全部进入锁池去竞争获取锁的机会
- notify 只会随机选取一个处于等待池中的线程进入锁池去竞争获取锁的机会
当我们锁住的是this
实例时,实际上可以用synchronized
修饰这个方法。下面两种写法是等价的:
public void add(int n) {
synchronized(this) { // 锁住this
count += n;
} // 解锁
}
public synchronized void add(int n) { // 锁住this
count += n;
} // 解锁
yield
当调用Thread.yield()函数时,会给线程调度器一个当前线程愿意让出CPU使用的暗示,但是线程调度器可能会忽略这个暗示,yield对锁的行为不会有影响
中断线程
目前使用的方法:
调用interrupt(),通知线程应该中断了,和yield类似,都是给个暗示
- 如果线程处于阻塞状态,那么线程将立即退出阻塞状态,并抛出一个InterruptedException异常
- 如果线程处于正常活动状态,那么会将该线程的中断标志设置为true,将设置中断标志的线程将继续正常运行,不受影响。
因此调用interrupt()并不能真正的中断线程,需要被调用的线程配合中断