多线程入门
线程与进程
进程:
- 是指一个内存中运行的应用程序,每个进程都有一个独立的内存空间
线程:
- 是进程中的一个执行路径。共享一个内存空间,线程之间可以自由切换,并发执行,一个进程最少有一个线程
- 线程实际上是在进程基础之上的进一步划分,一个进程启动之后,里面的若干执行路径又可以划分成若干个线程
线程调度
分时调度:
- 所有线程轮流使用CPU的使用特权,平均分配每个线程占用CPU的时间。
抢占式调度:
- 优先让优先级高的线程使用CPU,如果线程的优先级相同,那么会随机选择一个(线程随机性)。
- CPU使用抢占式调度模式在多个线程间进行着告诉的切换。对于CPU的一个核心而言,某个时刻,只能执行一个线程,而CPU的在多个线程间切换速度相对我们的感觉要快,看上去就是在同一时刻运行。其实,多线程程序并不能提高程序的运行速度,但能够提高程序的运行效率,让CPU的使用率更高。
同步与异步
同步:排队执行,效率低但是安全。
异步:同时执行,效率高但是数据不安全。
并发与并行
并发:指两个或多个事件同一个时间段内发生。
并行:指两个或多个事件在同一个时刻发生(同时发生)。
线程的创建方式
继承Thread类
public class Demo00 {
public static void main(String[] args) {
//实例化线程对象
MeThread mt = new MeThread();
//启动线程
mt.start();
for(int i=0;i<10;i++){
//获取线程名称并输出
System.out.println(Thread.currentThread().getName()+"线程:"+i);
}
}
}
//继承Thread类
class MeThread extends Thread{
@Override
public void run() {
for(int i=0;i<10;i++){
System.out.println(Thread.currentThread().getName()+"线程:"+i);
}
}
}
Thread类常用方法
变量和类型 | 方法 | 描述 |
---|---|---|
static int | activeCount() | 返回当前线程thread group及其子组中活动线程数的估计值。 |
void | checkAccess() | 确定当前运行的线程是否具有修改此线程的权限。 |
protected Object | clone() | 抛出CloneNotSupportedException,因为无法有意义地克隆线程。 |
int | countStackFrames() | 不推荐使用,要删除:此API元素将在以后的版本中删除。 此调用的定义取决于suspend() ,已弃用。 |
static Thread | currentThread() | 返回对当前正在执行的线程对象的引用。 |
static void | dumpStack() | 将当前线程的堆栈跟踪打印到标准错误流。 |
static int | enumerate(Thread[] tarray) | 将当前线程的线程组及其子组中的每个活动线程复制到指定的数组中。 |
static Map<Thread,StackTraceElement[]> | getAllStackTraces() | 返回所有活动线程的堆栈跟踪映射。 |
ClassLoader | getContextClassLoader() | 返回此线程的上下文 ClassLoader 。 |
static Thread.UncaughtExceptionHandler | getDefaultUncaughtExceptionHandler() | 返回由于未捕获的异常而导致线程突然终止时调用的默认处理程序。 |
long | getId() | 返回此Thread的标识符。 |
String | getName() | 返回此线程的名称。 |
int | getPriority() | 返回此线程的优先级。 |
StackTraceElement[] | getStackTrace() | 返回表示此线程的堆栈转储的堆栈跟踪元素数组。 |
Thread.State | getState() | 返回此线程的状态。 |
ThreadGroup | getThreadGroup() | 返回此线程所属的线程组。 |
Thread.UncaughtExceptionHandler | getUncaughtExceptionHandler() | 返回此线程由于未捕获的异常而突然终止时调用的处理程序。 |
static boolean | holdsLock(Object obj) | 当且仅当当前线程在指定对象上保存监视器锁时,返回 true 。 |
void | interrupt() | 中断此线程。 |
static boolean | interrupted() | 测试当前线程是否已被中断。 |
boolean | isAlive() | 测试此线程是否存活。 |
boolean | isDaemon() | 测试此线程是否为守护程序线程。 |
boolean | isInterrupted() | 测试此线程是否已被中断。 |
void | join() | 等待这个线程死亡。 |
void | join(long millis) | 此线程最多等待 millis 毫秒。 |
void | join(long millis, int nanos) | 此线程最多等待 millis 毫秒加上 nanos 纳秒。 |
static void | onSpinWait() | 表示调用者暂时无法进展,直到其他活动发生一个或多个操作为止。 |
void | resume() | 已过时。 此方法仅适用于suspend() ,由于它易于死锁,因此已被弃用。 |
void | run() | 如果此线程是使用单独的Runnable 运行对象构造的,则调用该Runnable 对象的run 方法; 否则,此方法不执行任何操作并返回。 |
void | setContextClassLoader(ClassLoader cl) | 为此Thread设置上下文ClassLoader。 |
void | setDaemon(boolean on) | 将此线程标记为 daemon线程或用户线程。 |
static void | setDefaultUncaughtExceptionHandler(Thread.UncaughtExceptionHandler eh) | 设置当线程由于未捕获的异常而突然终止时调用的默认处理程序,并且没有为该线程定义其他处理程序。 |
void | setName(String name) | 将此线程的名称更改为等于参数 name 。 |
void | setPriority(int newPriority) | 更改此线程的优先级。 |
void | setUncaughtExceptionHandler(Thread.UncaughtExceptionHandler eh) | 设置当此线程由于未捕获的异常而突然终止时调用的处理程序。 |
static void | sleep(long millis) | 导致当前正在执行的线程休眠(暂时停止执行)指定的毫秒数,具体取决于系统计时器和调度程序的精度和准确性。 |
static void | sleep(long millis, int nanos) | 导致当前正在执行的线程休眠(暂时停止执行)指定的毫秒数加上指定的纳秒数,具体取决于系统定时器和调度程序的精度和准确性。 |
void | start() | 导致此线程开始执行; Java虚拟机调用对应的run()方法。 |
实现Runnable接口
public class Demo01 {
public static void main(String[] args) {
//创建实例化线程对象
MyThread mt = new MyThread();
Thread t = new Thread(mt);
//启动t线程
t.start();
//在main方法中在写一个同样的方法
for(int i=0;i<10;i++){
System.out.println(Thread.currentThread().getName()+"线程:"+i);
}
}
}
//实现Runnable接口,作为线程的实现类
class MyThread implements Runnable{
public MyThread(){
}
//重写run()方法,作为线程的操作主体
@Override
public void run() {
for(int i=0;i<10;i++){
System.out.println(Thread.currentThread().getName()+"线程:"+i);
}
}
}
实现Runnable接口 与 继承Thread类 相比有如下优势:
-
通过创建任务,然后给线程分配的方式来实现的多线程,更适合多个线程同时执行相同任务的情况。
-
可以避免单继承所带来的局限性。
-
任务与线程本身是分离的,提高了程序的健壮性。
Runnable 与 Callable
接口定义
//Callable接口
public interface Callable<V> {
V call() throws Exception;
}
//Runnable接口
public interface Runnable {
public abstract void run();
}
Callable使用步骤
1. 编写类实现Callable接口 , 实现call方法
class XXX implements Callable<T> {
@Override
public <T> call() throws Exception {
return T;
}
}
2. 创建FutureTask对象 , 并传入第一步编写的Callable类对象
FutureTask<Integer> future = new FutureTask<>(callable);
3. 通过Thread,启动线程
new Thread(future).start();
Runnable 与 Callable的相同点
- 都是接口
- 都可以编写多线程程序
- 都采用Thread.start()启动线程
Runnable 与 Callable的不同点
- Runnable没有返回值;Callable可以返回执行结果
- Callable接口的call()允许抛出异常;Runnable的run()不能抛出
Callable获取返回值
Callalble接口支持返回执行结果,需要调用FutureTask.get()得到,此方法会阻塞主进程的继续往下执 行,如果不调用不会阻塞。