Java多线程
多线程技术概述
线程与进程
进程
- 是指一个内存中运行的应用程序,每个进程都有一个独立的内存空间
线程
线程调度
分时调度
- 所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间。
抢占式调度
-
优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一个(线程随机性), Java使用的为抢占式调度。 -
CPU使用抢占式调度模式在多个线程间进行着高速的切换。对于CPU的一个核新而言,某个时刻, 只能执行一个线程,而 CPU的在多个线程间切换速度相对我们的感觉要快,看上去就是 在同一时 刻运行。 其实,多线程程序并不能提高程序的运行速度,但能够提高程序运行效率,让CPU的 使 用率更高。
同步与异步
同步:排队执行 , 效率低但是安全。
异步:同时执行 , 效率高但是数据不安全。
并发与并行
并发:指两个或多个事件在同一个时间段内发生。
并行:指两个或多个事件在同一时刻发生(同时发生)。
线程池 Executors
如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程 就会大大降低 系统的效率,因为频繁创建线程和销毁线程需要时间. 线程池就是一个容纳多个线程的容 器,池中的线程可以反复使用,省去了频繁创建线程对象的操作,节省了大量的时间和资源。
线程池的好处
Java中的四种线程池 . ExecutorService
-
缓存线程池
ExecutorService service = Executors.newCachedThreadPool();
service.execute(new Runnable() {
@Override
public void run() {
System.out.println("线程的名称:" + Thread.currentThread().getName());
}
});
service.execute(new Runnable() {
@Override
public void run() {
System.out.println("线程的名称:" + Thread.currentThread().getName());
}
});
service.execute(new Runnable() {
@Override
public void run() {
System.out.println("线程的名称:" + Thread.currentThread().getName());
}
});
}
-
定长线程池
ExecutorService service = Executors.newFixedThreadPool(2);
service.execute(new Runnable() {
@Override
public void run() {
System.out.println("线程的名称:" + Thread.currentThread().getName());
}
});
service.execute(new Runnable() {
@Override
public void run() {
System.out.println("线程的名称:" + Thread.currentThread().getName());
}
});
? -
单线程线程池
ExecutorService service = Executors.newSingleThreadExecutor();
service.execute(new Runnable() {
@Override
public void run() {
System.out.println("线程的名称:" + Thread.currentThread().getName());
}
});
service.execute(new Runnable() {
@Override
public void run() {
System.out.println("线程的名称:" + Thread.currentThread().getName());
}
});
? -
周期性任务定长线程池 public static void main(String[] args) {
ScheduledExecutorService service = Executors.newScheduledThreadPool(2);
service.schedule(new Runnable() {
@Override
public void run() {
System.out.println("俩人相视一笑~ 嘿嘿嘿"); } },5,TimeUnit.SECONDS);
}
service.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
System.out.println("俩人相视一笑~ 嘿嘿嘿");
}
}, 5, 2, TimeUnit.SECONDS);
}
?
第三种实现线程的方法:Callable
Runnable 与 Callable的相同点
- 都是接口
- 都可以编写多线程程序
- 都采用Thread.start()启动线程
Runnable 与 Callable的不同点
- Runnable没有返回值;Callable可以返回执行结果
- Callable接口的call()允许抛出异常;Runnable的run()不能抛出
Callable获取返回值
Java并发编程艺术
(1)线程创建的方式
表面看来,创建线程的方式有三四种,但是走进源码我们发现每种创建线程的方式的核心都是在 new Thread() 的进一步封装,如果我们把这些都叫作一种新的方式,那么创建线程的方式便会千变万化、层出不穷,比如 JDK 更新了,它可能会多出几个类,会把new Thread() 重新封装,表面上看又会是一种新的实现线程的方式,透过现象看本质,打开封装后,会发现它们最终都是基于 Runnable 接口或继承 Thread 类实现的。
1)实现 Runnable 接口
public class RunnableThread implements Runnable {
@Override
public void run() {
System.out.println('用实现 Runnable 接口实现线程');
}
}
2)继承 Thread 类
public class ExtendsThread extends Thread {
@Override
public void run() {
System.out.println('用 Thread 类实现线程');
}
}
3)实现 Callable 接口(有返回值)
class CallableTask implements Callable<Integer> {
@Override
public Integer call() throws Exception {
return new Random().nextInt();
}
}
ExecutorService service = Executors.newFixedThreadPool(10);
Future<Integer> future = service.submit(new CallableTask());
(2)线程状态转换
New - Runnable - Blocked - Waiting - Timed Waiting - Terminated
New 表示线程被创建但尚未启动的状态:当我们用 new Thread() 新建一个线程时,如
果线程没有开始运行 start() 方法,所以也没有开始执行 run() 方法里面的代码,那么此时
它的状态就是 New。而一旦线程调用了 start(),它的状态就会从 New 变成 Runnable。
Java 中的 Runnable 状态对应操作系统线程状态中的两种状态,分别是 Running 和
Ready,也就是说,Java 中处于 Runnable 状态的线程有可能正在执行,也有可能没有正在
执行,正在等待被分配 CPU 资源。
在 Java 中阻塞状态通常不仅仅是 Blocked,实际上它包括三种状态,分别是 Blocked(被阻塞)、Waiting(等待)、Timed Waiting(计时等待),这三种状态统称为阻塞状态。
① Blocked 被阻塞
从 Runnable 状态进入 Blocked 状态只有一种可能,就是进入 synchronized 保护
的代码时没有抢到 monitor 锁,无论是进入 synchronized 代码块,还是 synchronized
方法,都是一样。
② Waiting 等待
线程进入 Waiting 状态有三种可能性(没有设置 Timeout 参数的 Object.wait() 方
法、没有设置 Timeout 参数的 Thread.join() 方法、LockSupport.park() 方法)。Blocked 与
Waiting 的区别是 Blocked 在等待其他线程释放 monitor 锁,而 Waiting 则是在等待
某个条件,比如 join 的线程执行完毕,或者是 notify()/notifyAll()。
③ Timed Waiting 计时等待
线程进入 Timed Waiting 状态有四种可能性(设置了时间参数的 Thread.sleep(long
millis) 方法、设置了时间参数的 Object.wait(long timeout) 方法、设置了时间参数的
Thread.join(long millis) 方法、设置了时间参数的 LockSupport.parkNanos(long nanos) 方
法和 LockSupport.parkUntil(long deadline) 方法)。Waiting 和 Timed Waiting 区别仅在于
有没有时间限制,Timed Waiting 会等待超时,由系统自动唤醒,或者在超时前被唤醒
信号唤醒。
Terminated 终止状态,要想进入这个状态有两种可能:run() 方法执行完毕,线程
正常退出。出现一个没有捕获的异常,终止了 run() 方法,最终导致意外终止。
|