java线程的基础知识
目录
前言
随着计算机技术的不断发展,高并发的技术要求越来越多严格,但这都离不开基础服务核心的支持,那就是线程,本文就根据自己的见解,来说一说java的线程。
一、基础概念
1.1 程序、进程、线程
1.1.1程序
为了完成某个任务,用计算机的编程语言编写的可运行代码集合。是一个静态的对象。例如我们常用的微信,就是一个通讯的程序。
1.1.2进程
程序运行的一个过程。当运行了微信程序,就相当于开启了一个微信运行的进程,当进程被强迫关闭的时候,该程序也会被强制关闭。
1.1.3线程
程序内部执行的一条执行路径。例如微信程序中,接收某个朋友的一条信息的过程就是一个线程。多线程就是在同一个单位时间内,可以同时进行多个线程操作,例如在微信在同一时间内可以接收多个朋友发送的信息。
1.2 单核CPU与多核CPU
单核CPU,在单位时间内,只能执行一个线程任务。多线程任务在单核cpu的电脑中执行,实际上也是会执行完一个线程再执行另外一个线程。
多核CPU,在单位时间内,可以执行多个线程任务,只有多核CPU,才可以发挥多线程的效率。
1.3 并行和并发
并行,多个cpu并行执行多个任务。例如,多个人同时在做不同的事情。
并发,单个cpu同时执行多个任务。例如,商品的秒杀。多个人在做同一个事情。
1.4 多线程的优缺点
缺点,单cpu情况下,执行多线程的时间会比执行一个个单线程的时间慢。
优点,可以在同一个时间内,同时进行多个操作。可以有效避免主线程阻塞的问题。增强用户的体验度。
二、线程的分类
线程可以分为守护线程和用户线程。两者各方面都是相同的, 守护线程依赖于用户线程,若jvm中都只剩下守护线程的时候,那么jvm也将退出了。
2.1 用户线程
用户线程就是我们平时使用的用来处理逻辑的线程。例如,平时运行program程序中的main方法里的主线程。
2.2 守护线程
最常见的就是jvm的垃圾回收线程。
三、线程的创建方式
一共有两种方式来创建java线程,在java8 api文档中说到。其实我觉得最终的实质也就是一种方式去创建线程,都是需要new Thread ,然后去调用它的start方法。只是它的run方法,实现的方式不同而已。
3.1 声明一个子类继承Thread ,重写run方法
class PrimeThread extends Thread {
long minPrime;
PrimeThread(long minPrime) {
this.minPrime = minPrime;
}
public void run() {
// compute primes larger than minPrime
. . .
}
}
实例化子类,并调用它的start方法
PrimeThread p = new PrimeThread(143);
p.start();
3.2 声明一个子类实现Runable接口,实现run方法
class PrimeRun implements Runnable {
long minPrime;
PrimeRun(long minPrime) {
this.minPrime = minPrime;
}
public void run() {
// compute primes larger than minPrime
. . .
}
}
实例化子类,并传到实例化线程类中。
PrimeRun p = new PrimeRun(143);
new Thread(p).start();
3.3 实现Callable接口,重写call方法
官方文档说到,A task that returns a result。是一个有返回的任务。The Callable
interface is similar to Runnable
。它和Runnable
接口很相似。需要结合Interface Future<V>接口来使用,通过调用feature.get方法来获得任务的返回值。
interface ArchiveSearcher { String search(String target); }
class App {
ExecutorService executor = ...
ArchiveSearcher searcher = ...
void showSearch(final String target)
throws InterruptedException {
Future<String> future
= executor.submit(new Callable<String>() {
public String call() {
return searcher.search(target);
}});
displayOtherThings(); // do other things while searching
try {
displayText(future.get()); // use future
} catch (ExecutionException ex) { cleanup(); return; }
}
}
3.4 注意事项
线程start后,不能重复多次进行start 方法调用。
四 创建方式的比较
4.1优先使用哪种方式
实际应用中,建议选择实现Runable接口的方式。主要原因有两个。
其一,继承接口的方式,可以打破了类单一继承的局限性。
其二,多线程之间的数据共享,可以通过共享单一的Runable实现类,而达到共享资源的目的。非常适合多个相同线程来处理同一份资源的情况。
4.2 两种方式的联系
Thread类中也是继承了Runnable的接口,方式二,也是实现了Runnable的接口。不管哪一种创建方式,都是需要实现run方法。
五、线程的生命周期
一个完整的线程会经历五种线程状态,创建》就绪 》运行》阻塞》死亡。
创建
当一个Thread子类被声明创建时。
PrimeThread p = new PrimeThread(143);
就绪
当线程调用start方法后,进入线程队列等待,并还没有得到cpu分配资源。
p.start();
运行
当线程获得cpu分配的资源后,执行run方法。
public void run() {
// compute primes larger than minPrime
}
阻塞
当因为某些特定的原因,cpu让出了执行的资源,线程被挂起,就形成了阻塞的状态。例如线程1 join 线程2,则线程1被挂起,就形成了阻塞状态。sleep(long time) ,等待同步锁,wait,
死亡
当线程完成了所有的操作,或者被强迫调用终止的方法,或是因为出现了异常而导致结束。
总结
简单的说明了一下java线程的基础知识,也不知道对不对,共勉。下一篇文章,继续探讨线程的安全问题。