JUC并发编程-线程基础知识

并发相关Java包

  • 涉及到的内容
    • java.util.concurrent
    • java.util.concurrent.atomic
    • java.util.concurrent.locks

start线程解读

初始程序

1
2
3
4
5
public static void main(String[] args) {
Thread t1 = new Thread(() ->{
},"t1");
t1.start();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
//start
public synchronized void start() {
/**
* This method is not invoked for the main method thread or "system"
* group threads created/set up by the VM. Any new functionality added
* to this method in the future may have to also be added to the VM.
*
* A zero status value corresponds to state "NEW".
*/
if (threadStatus != 0)
throw new IllegalThreadStateException();

/* Notify the group that this thread is about to be started
* so that it can be added to the group's list of threads
* and the group's unstarted count can be decremented. */
group.add(this);

boolean started = false;
try {
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
/* do nothing. If start0 threw a Throwable then
it will be passed up the call stack */
}
}
}
1
private native void start0();//start0是一个native方法

native调用了本地方法,我们可以通过下载官网OpenJDK查看其源码

  • thread.c

    • java线程是通过start的方法启动执行的,主要内容在native方法start0中

      Openjdk的写JNI一般是一一对应的,Thread.java对应的就是Thread.c

      start0其实就是JVM_StartThread。此时查看源代码可以看到在jvm.h中找到了声明,jvm.cpp中有实现。

  • jvm.cpp

  • thread.cpp

    • 终于在这里调用了操作系统的线程启动os::start_thread(thread);

Java多线程相关概念

1把锁

2个并(并发和并行)

​ ①并发

​ 是在同一实体上的多个事件,是在同一台处理器上“同时”处理多个任务,同一时刻,其实是只有一个事件在发生。

​ ②并行

​ 是在不同实体上的多个事件,是在多台处理器上同时处理多个任务,同一时刻,大家都真的在做事情,你做你的,我做我的

并发VS并行

3个程(进程 线程 管程)

  • 通过上面start线程的案例,其实进程线程都来源于操作系统。

①进程

  • 系统中运行的一个应用程序就是一个进程,每一个进程都有它自己的内存空间和系统资源。
  • 进程是一个具有一定独立功能的程序关于某个数据集合的一次运行活动。它是操作系统动态执行的基本单元

②线程

  • 也被称为轻量级进程,在同一个进程内基本会有1一个或多个线程,是大多数操作系统进行调度的基本单元。

③管程

  • Monitor(监视器),也就是我们平时说的
  • Monitor其实是一种同步机制,他的义务是保证(同一时间)只有一个线程可以访问被保护的数据和代码。
  • JVM中同步是基于进入和退出监视器对象(Monitor,管程对象)来实现的,每个对象实例都会有一个Monitor对象,
  • Monitor对象会和Java对象一同创建并销毁,它底层是由C++语言来实现的。

进程VS线程

进程和线程的最大不同在于进程基本上是独立的,而线程不一定,线程共享方法区,线程私有栈本地方法栈程序计数器

用户线程和守护线程

Java线程分为用户线程和守护线程

  • 线程的daemon属性为
    • true表示是守护线程
    • false表示是用户线程。

用户线程

是系统的工作线程,它会完成这个程序需要完成的业务操作

守护线程

是一种特殊的线程,为其他线程服务的,在后台默默地完成一些系统性的服务,比如垃圾回收线程。

总结

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class DaemonDemo
{
public static void main(String[] args)
{
Thread t1 = new Thread(() -> {
System.out.println(Thread.currentThread().getName()+"\t 开始运行,"+(Thread.currentThread().isDaemon() ? "守护线程":"用户线程"));
while (true) {

}
}, "t1");
//线程的daemon属性为true表示是守护线程,false表示是用户线程
//---------------------------------------------
t1.setDaemon(true);
//-----------------------------------------------
t1.start();
//3秒钟后主线程再运行
try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }

System.out.println("----------main线程运行完毕");
}

}
  • 两种情况
    1. 未加t1.setDaemon(true);,默认是用户线程,他会继续运行,所以灯亮着
    2. 加了t1.setDaemon(true);是守护线程,当用户线程main方法结束后自动退出了
      在这里插入图片描述
      在这里插入图片描述
  • 守护线程作为一个服务线程,没有服务对象就没有必要继续运行了,如果用户线程全部结束了,意味着程序需要完成的业务操作已经结束了,系统可退出了。假如当系统只剩下守护线程的时候,java虚拟机会自动退出
  • setDaemon(true)方法必须在start()之前设置,否则报IIIegalThreadStateException异常

多线程的实现方式

继承Thread

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//注意:打印出来的结果会交替执行
public class ThreadDemo{
public static void main(String[] args) {
//4.创建Thread类的子类对象
MyThread myThread=new MyThread();
//5.调用start()方法开启线程
//[ 会自动调用run方法这是JVM做的事情,源码看不到 ]
myThread.start();
for (int i = 0; i < 100; i++) {
System.out.println("我是主线程"+i);
}
}
}
class MyThread extends Thread{
//2.重写run方法
public void run(){
//3.将要执行的代码写在run方法中
for(int i=0;i<100;i++){
System.out.println("我是线程"+i);
}
}
}

实现Runnable接口

在这里插入图片描述

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class RunnableDemo {
public static void main(String[] args) {
//4.创建Runnable的子类对象
MyRunnale mr=new MyRunnale();
//5.将子类对象当做参数传递给Thread的构造函数,并开启线程
//MyRunnale taget=mr; 多态
new Thread(mr).start();
for (int i = 0; i < 1000; i++) {
System.out.println("我是主线程"+i);
}
}
}

//1.定义一个类实现Runnable
class MyRunnale implements Runnable{
//2.重写run方法
@Override
public void run() {
//3.将要执行的代码写在run方法中
for (int i = 0; i < 1000; i++) {
System.out.println("我是线程"+i);
}
}
}

两种实现多线程方式的区别

(1).查看源码
a.继承Thread:由于子类重写了Thread类的run(),当调用start()时,直接找子类的run()方法
b.实现Runnable:构造函数中传入了Runnable的引用,成员变量记住了它,start()调用run()方法时内部判断成员变量Runnable的引用是否为空,不为空编译时看的是 Runnable的run(),运行时执行的是子类的run()方法
(2).继承Thread
a.好处是:可以直接使用Thread类中的方法,代码简单
b.弊端是:如果已经有了父类,就不能用这种方法
(3).实现Runnable接口
a.好处是:即使自己定义的线程类有了父类也没有关系,因为有了父类可以实现接口,而且接口可以多现实的
b.弊端是:不能直接使用Thread中的方法需要先获取到线程对象后,才能得到Thread的方法,代码复杂

Thread.activeCount( )

在IDEA中Thread.activeCount()=2,除了main方法的主线程外还有,还多了一个预期外的Monitor Ctrl-Break线程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class threadActiveCount {
public static void main(String[] args) {
//打印当前线程组的线程
Thread.currentThread().getThreadGroup().list();
System.out.println("=========");
//idea用的是反射,还有一个monitor监控线程。
System.out.println(Thread.activeCount());
/*
输出结果:
java.lang.ThreadGroup[name=main,maxpri=10]
Thread[main,5,main]
Thread[Monitor Ctrl-Break,5,main]
=========
2
* */
}
}

在这里插入图片描述

在ECLIPSE中得到的结果是1

在这里插入图片描述

TimeUnit类

TimeUnit是java.util.concurrent包下面的一个类,TimeUnit提供了可读性更好的线程暂停操作,通常用来替换Thread.sleep( ) 底层实现还是使用的Thread.sleep( )

字段 描述
SECONDS 停顿3秒
MINUTES 停顿3分钟
HOURS 停顿3小时
DAYS 停顿三天
1
2
3
4
5
6
7
8
//停顿3s
try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) {e.printStackTrace();}
//停顿3分钟
try { TimeUnit.MINUTES.sleep(3); } catch (InterruptedException e) {e.printStackTrace();}
//停顿3h
try { TimeUnit.HOURS.sleep(3); } catch (InterruptedException e) {e.printStackTrace();}
//停顿三天
try { TimeUnit.DAYS.sleep(3); } catch (InterruptedException e) {e.printStackTrace();}

JUC并发编程-线程基础知识
https://yztldxdz.top/2022/11/19/JUC并发编程-线程基础知识/
发布于
2022年11月19日
许可协议