一、进程和线程
1.1、进程
一个正在执行中的程序就是一个进程,系统会为这个进程发配独立的内存资源。进程是程序的一次执行过程,它有自己独立的生命周期,它会在启动程序时产生,运行程序时存在,关闭程序时消亡。例如QQ就是一个进程
1.2、线程
线程是由进程创建的,是进程的一个实体,是具体干活的人,一个进程可能有多个线程。线程不独立分配内存,而是共享进程的内存资源,线程可以共享cpu的计算资源。
1.3、上下文切换
用户线程执行的过程我们称之为用户态
,内核调度的状态称之为内核态
,每一个线程运行时产生的数据我们称之为上下文
,线程的每次切换都需要进行用户态到内核态的来回切换,同时伴随着上下文的切换,是一个比较消耗资源的操作
1.4、一个错误认知
之前先学的python,学C/C++的时候也没接触过多线程技术,导致我认为多线程只是一直在用一个核心,其实并不是的,Python的多线程技术确实只能用一个核心,由于GIL解释器锁的原因,因为这个原因,python又添加了多核心编程的库,据说Guido van Rossum也想干掉GIL,但是因为Python的体系已经很大了,很难干掉这样一个重要的角色,曾经是Python的功臣现在却阻碍了Python的进步。Java的线程技术是真的创建了一个线程,这个线程是会被系统进行调用的。也就是说JAVA的多线程其实就是多核心了。
二、创建线程三种方法
2.1、继承Thread类重写run方法
public class MyThreadTest {
private static class MyThread extends Thread{
@Override
public void run() {
// 休眠两秒
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("200");
}
}
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new MyThread().start();
}
}
}
2.2、实现Runnable接口
public class MyThreadTest {
private static class MyThread implements Runnable{
@Override
public void run() {
// 休眠两秒
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("200");
}
}
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(new MyThread()).start();
}
}
}
2.3、使用Lammbda表达式
我们发现其实Runable里面只有一个方法,而且是一个函数式接口,所以我们可以使用箭头函数
public class MyThreadTest {
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(() -> {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("200");
}).start();
}
}
}
2.4、有返回值的线程
public class MyThreadTest {
private static class Task implements Callable<Integer> {
@Override
public Integer call() throws Exception {
Thread.sleep(100);
return 1;
}
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
FutureTask<Integer> futureTask = new FutureTask<>(new Task());
for (int i = 0; i < 40; i++) {
new Thread(futureTask).start();
// 得到返回值
int result = futureTask.get();
// 打印返回值
System.out.println(result);
}
}
}
三、守护线程
守护线程对于后台支持任务非常有用,例如垃圾收集,释放未使用对象的内存以及从缓存中删除不需要的条目。大多数JVM线程都是守护线程。在比如qq等等聊天软件,主程序是非守护线程,而所有的聊天窗口是守护线程,当在聊天的过程中,直接关闭聊天应用程序时,聊天窗口也会随之关。包括word中我们在书写文字的时候,还有线程帮我们进行拼写检查,这都是守护线程。
要将线程设置为守护线程,我们需要做的就是调用Thread.setDaemon()
NewThread daemonThread = new NewThread();
daemonThread.setDaemon(true);
daemonThread.start();
任何线程都继承创建它的线程的守护进程状态。由于主线程是用户线程,因此在main方法内创建的任何线程默认为用户线程。
public class Deamon {
public static void main(String[] args){
Thread t1 = new Thread(() -> {
int count = 10;
Thread t2 = new Thread(() -> {
while (true){
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("我是个守护线程!");
}
});
t2.setDaemon(true);
t2.start();
while (count >= 0){
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("我是用户线程!");
count--;
}
System.out.println("用户线程结束-------------------");
});
t1.start();
}
}
四、线程的生命周期
我们在Thread类中发现了一个内部枚举类,这个State就可以表示一个线程的生命周期:
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,
BLOCKED,
WAITING,
TIMED_WAITING,
TERMINATED;
}
状态 | 描述 |
---|---|
NEW | 这个状态主要是线程未被Thread.start()调用前的状态。 |
RUNNABLE | 线程正在JVM中被执行,等待来自操作系统(如处理器)的调度。 |
BLOCKED | 阻塞,因为某些原因不能立即执行需要挂起等待。 |
WAITING | 无限期等待,由于线程调用了Object.wait(0) ,Thread.join(0) 和LockSupport.park 其中的一个方法,线程处于等待状态,其中调用wait , join 方法时未设置超时时间。 |
TIMED_WAITING | 有限期等待, 线程等待一个指定的时间,比如线程调用了Object.wait(long) , Thread.join(long) ,LockSupport.parkNanos , LockSupport.parkUntil 方法之后,线程的状态就会变成TIMED_WAITING |
TERMINATED | 终止的线程状态,线程已经完成执行。 |
五、堵塞线程
此时我们介绍一个Thread的join方法
我们首先定义了一个ThreadUtils的工具类
package ThreadTest;
public class ThreadUtils {
public static void sleep(int i){
try {
Thread.sleep(i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
package ThreadTest;
public class ThreadTest1 {
public static void main(String[] args) {
Thread t1 = new Thread(()->{
for (int i = 0; i < 10; i++) {
ThreadUtils.sleep(10);
System.out.println("this is thread t1");
}
});
Thread t2 = new Thread(()->{
for (int i = 0; i < 10; i++) {
ThreadUtils.sleep(10);
System.out.println("this is thread t2");
}
});
t1.start();
t2.start();
try {
t1.join();
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("it`s over");
}
}