1. 线程基本知识

请带着如下问题阅读本文。
1.什么是线程
2.线程和进程的区别
3.多线程的优缺点
4.什么是多线程的上下文切换
5.wait() sleep() 区别
6.线程实现方式哪4种?
7.如何保证多线程下I++正确
8.如何在线程间共享数据
9.怎么唤醒一个阻塞的线程
10.线程如果出现了运行时异常该怎么办?
11.JAVA里线程调度算法是什么?
12.线程类的构造方法和静态块是被哪个线程调用的?
13.线程的五个状态是什么?怎么转换?
14.wait()和sleep()的区别?
15.start()方法和run()方法的区别?
16.Runnable接口和Callable接口的区别?
17.Java中如何获取到线程dump文件?

线程

线程是进程内的执行单元。


image.png

进程是系统进行资源分配的基本单位,有独立的内存地址空间

线程是CPU独立运行和独立调度的基本单位,没有单独地址空间,有独立的栈,局部变量,寄存器, 程序计数器等。

创建进程的开销大,包括创建虚拟地址空间等需要大量系统资源

创建线程开销小,基本上只有一个内核对象和一个堆栈。

一个进程无法直接访问另一个进程的资源;同一进程内的多个线程共享进程的资源。

进程切换开销大,线程切换开销小;进程间通信开销大,线程间通信开销小。

线程属于进程,不能独立执行。每个进程至少要有一个线程,成为主线程

线程基本操作

image.png

NEW: 初始线程状态,还没START()
RUNABLE:在跑的,和等着被CPU调度到要跑的都是这个状态。
BLOCKED: 在等待一个监视器,或者锁。或者是被唤醒(notify()) 想重进同步方法或同步块的状态。
WAITING: wait(),thread.join(),locksupport.park()

TIMED_WAITING:
Thread.sleep
Object.wait with timeout
Thread.join with timeout
LockSupport.parkNanos
LockSupport.parkUntil

TERMINATED:线程执行结束了

IO阻塞,线程是RUNABLE 还是 BLOCKED?

行阻塞式 I/O 操作时,Java 的线程状态究竟是什么?是 BLOCKED?还是 WAITING?

可能你已经猜到,既然放到 RUNNABLE 这一主题下讨论,其实状态还是 RUNNABLE。我们也可以通过一些测试来验证这一点:

@Test
public void testInBlockedIOState() throws InterruptedException {
    Scanner in = new Scanner(System.in);
    // 创建一个名为“输入输出”的线程t
    Thread t = new Thread(new Runnable() {
        @Override
        public void run() {
            try {
                // 命令行中的阻塞读
                String input = in.nextLine();
                System.out.println(input);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
              IOUtils.closeQuietly(in);
            }
        }
    }, "输入输出"); // 线程的名字

    // 启动
    t.start();

    // 确保run已经得到执行
    Thread.sleep(100);

    // 状态为RUNNABLE
    assertThat(t.getState()).isEqualTo(Thread.State.RUNNABLE);
}

在最后的语句上加一断点,监控上也反映了这一点:


image.png

关于监控,可见上一篇中的介绍。

网络阻塞时同理,比如socket.accept,我们说这是一个“阻塞式(blocked)”式方法,但线程状态还是 RUNNABLE。

@Test
public void testBlockedSocketState() throws Exception {
    Thread serverThread = new Thread(new Runnable() {
        @Override
        public void run() {
            ServerSocket serverSocket = null;
            try {
                serverSocket = new ServerSocket(10086);
                while (true) {
                    // 阻塞的accept方法
                    Socket socket = serverSocket.accept();
                    // TODO
                }
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    serverSocket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }, "socket线程"); // 线程的名字
    serverThread.start();

    // 确保run已经得到执行
    Thread.sleep(500);

    // 状态为RUNNABLE
    assertThat(serverThread.getState()).isEqualTo(Thread.State.RUNNABLE);

}

监控显示:

image.png

start() 启动线程(NEW->RUNABLE)

start()方法来启动一个线程,真正实现了多线程运行。这时无需等待run方法体代码执行完毕,可以直接继续执行下面的代码;

run()方法是在本线程里的,只是线程里的一个函数,而不是多线程的。 如果直接调用run(),其实就相当于是调用了一个普通函数而已,直接待用run()方法必须等待run()方法执行完毕才能执行下面的代码,所以执行路径还是只有一条,根本就没有线程的特征,所以在多线程执行时要使用start()方法而不是run()方法。

stop() 终止线程(因为会释放所有监视器monitor,所以会造成数据不一致)

image.png

interrupt() 中断线程

suspend(), resume() 挂起和继续执行

因为SUSPEND不会释放锁,如果RESUME发生在SUSPEND前,则会死锁。


image.png

join() 等待线程结束


image.png

yeild() 谦让(把CPU使用权让出去)

sleep() 休眠

方法是线程类(Thread)的静态方法,让调用线程进入睡眠状态,让出执行机会给其他线程,等到休眠时间结束后,线程进入就绪状态和其他线程一起竞争cpu的执行时间。

因为sleep() 是static静态的方法,他不能改变对象的机锁,当一个synchronized块中调用了sleep() 方法,线程虽然进入休眠,但是对象的机锁没有被释放,其他线程依然无法访问这个对象

wait()
wait()是Object类的方法,当一个线程执行到wait方法时,它就进入到一个和该对象相关的等待池,同时释放对象的机锁,使得其他线程能够访问,可以通过notify,notifyAll方法来唤醒等待的线程

守护线程

只有守护线程时,JAVA虚拟机就退出了

在后台默默完成一些任务,如垃圾回收。

线程同步的基本操作

synchronized (可以给指定对象,给实例方法(指定对象为当前实例),静态方法(指定对象为当前类)加锁)

Object.wait() notify();


image.png

线程安全的概念

指一个类或一个函数,在多线程环境中被调用时;能够正确处理各个线程的局部变量,使程序功能正确完成。


image.png

多线程

优点:
1.可以使每个线程做自己的任务,代码上语义更明确
2.利用多核CPU的优势
3.可以把占据时间长如IO阻塞的任务放到后台处理

缺点:
1.线程安全问题(脏数据,死锁)
2.性能问题(活锁,饥饿,上下文切换开销)
3.线程本身需要更多的内存

当前任务执行一个时间片后会切换到下一个任务。但是,在切换前会保存上一个任务的状态,以便下次切换回这个任务时,可以再次加载这个任务的状态

从任务保存到再加载的过程就是一次上下文切换

线程实现

1.继承THREAD类,重写RUN方法
2.实现RUNABLE 接口,实例对象作为THREAD的构造函数的传参。
3.实现CALLABLE接口,通过FUTURETASK来创建


image.png

4.通过线程池创建。

image.png

Callable接口和Runnable接口相似,区别就是Callable需要实现call方法,而Runnable需要实现run方法;并且,call方法还可以返回任何对象,无论是什么对象,JVM都会当作Object来处理。但是如果使用了泛型,我们就不用每次都对Object进行转换了。
1.Callable可以返回一个类型V,而Runnable不可以

2.Callable能够抛出checked exception,而Runnable不可以。

3.Callable和Runnable都可以应用于executors。而Thread类只支持Runnable.

一个线程如果出现了运行时异常会怎么样?

如果这个异常没有被捕获的话,这个线程就停止执行了。另外重要的一点是:如果这个线程持有某个某个对象的监视器,那么这个对象监视器会被立即释放

怎么唤醒一个阻塞的线程?

如果线程是因为调用了wait()、sleep()或者join()方法而导致的阻塞,可以中断线程,并且通过抛出InterruptedException来唤醒它;如果线程遇到了IO阻塞,无能为力,因为IO是操作系统实现的,Java代码并没有办法直接接触到操作系统。

Java中用到的线程调度算法是什么?

抢占式。一个线程用完CPU之后,操作系统会根据线程优先级、线程饥饿情况等数据算出一个总的优先级并分配下一个时间片给某个线程执行

线程类的构造方法、静态块是被new这个线程类所在的线程所调用的,而run方法里面的代码才是被线程自身所调用的。

假设Thread2中new了Thread1,main函数中new了Thread2,那么:

(1)Thread2的构造方法、静态块是main线程调用的,Thread2的run()方法是Thread2自己调用的

(2)Thread1的构造方法、静态块是Thread2调用的,Thread1的run()方法是Thread1自己调用的

Java中如何获取到线程dump文件

(1)获取到线程的pid,可以通过使用jps命令,在Linux环境下还可以使用ps -ef | grep java

(2)打印线程堆栈,可以通过使用jstack pid命令,在Linux环境下还可以使用kill -3 pid

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 222,183评论 6 516
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,850评论 3 399
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 168,766评论 0 361
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,854评论 1 299
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,871评论 6 398
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,457评论 1 311
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,999评论 3 422
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,914评论 0 277
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,465评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,543评论 3 342
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,675评论 1 353
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,354评论 5 351
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 42,029评论 3 335
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,514评论 0 25
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,616评论 1 274
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 49,091评论 3 378
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,685评论 2 360

推荐阅读更多精彩内容

  • 本文主要讲了java中多线程的使用方法、线程同步、线程数据传递、线程状态及相应的一些线程函数用法、概述等。 首先讲...
    李欣阳阅读 2,458评论 1 15
  • Java多线程学习 [-] 一扩展javalangThread类 二实现javalangRunnable接口 三T...
    影驰阅读 2,965评论 1 18
  • (一)Java部分 1、列举出JAVA中6个比较常用的包【天威诚信面试题】 【参考答案】 java.lang;ja...
    独云阅读 7,116评论 0 62
  • 一、并发 进程:每个进程都拥有自己的一套变量 线程:线程之间共享数据 1.线程 Java中为多线程任务提供了很多的...
    SeanMa阅读 2,476评论 0 11
  • Linux中必备常用支持库的安装(CentOS-*) 在CentOS安装软件的时候,可能缺少一部分支持库,而报错。...
    SuperGu阅读 1,250评论 0 0