当前位置:网站首页 > Java基础 > 正文

java线程1基础入门



下面学习线程的基本知识,包括线程与进程区别、开辟新线程的方法、线程安全隐患、死锁、等待唤醒机制和生产者消费者模式等知识。

线程

线程:负责进程中程序的执行,是进程的一个执行单元,一个进程中允许有多个线程,为多线程,如果只有一条线程,称之为单线程。一个进程至少有一条线程。

多线程并没有真的提高了运行速度,在同一时刻只有一条线程在执行,由于切换速度很快,感觉好像很多线程在同时执行。

进程:正在内存中运行的应用程序就是进程。

多线程的优点:

(1)提高用户体验,参考网页打开时图片和文字加载过程

(2)提高了CPU的利用率,线程执行需要和计算机硬件进行交互,当线程和计算机硬件进行交互时,此时CPU是空置的,空置的CPU可以执行其他的线程,因此多线程可以提高CPU利用率。

抢占式调度:Java使用抢占式调度,线程是有优先级的,理论上先执行优先级高的线程,如果优先级相同,会随机执行一条线程。对于CPU的一个核来说,同时只有一条线程在执行,但是线程在CPU核上进行高速的切换,所以感觉是同时执行。

JVM启动时会给一条主线程,给main方法去执行代码,还会给一条线程给gc用于垃圾回收。

在Java中开辟新的线程

(1)写一个子类继承自线程顶级父类Thread,重写run方法,将需要被新线程执行的方法写到里面,然后使用子类对象的start方法开启新的线程。

 

测试结果

经典面试题:run方法和start方法的区别,run方法并不会开启新的线程,只是在原来线程中去执行run方法里的代码,start方法会开启新的线程,并执行run方法里的代码。

(2)写一个类实现Runnable接口,并重写里面run方法,创建类对象将其传入Thread构造方法中创建一个Thread线程对象,然后调用Thread对象的start方法启动线程,这个方法最为常用。

 

测试结果

(3)实现Callable接口,后续添加

线程安全

线程安全隐患:多线程执行的结果和单线程执行的结果可能不一样,叫做线程安全隐患,参考卖票案例,会出现重复票,还有票号为0的,因为线程优先级一样的情况下,执行次序是无序的,导致结果异常,为了解决这个问题加同步锁解决。

 

添加同步锁后执行不会有线程安全问题,可以实现正常卖票。

出现的线程安全的条件:有多个线程、多个线程共享资源、有写操作。

解决方案:

(1)同步代码块,一条线程中核心代码没有执行完,其他线程就不要执行。同步代码块可以保证同一时刻最多只有一条线程在执行代码块中的逻辑,其书写格式如下:

 

锁资源可以是任意对象,但是需要保证多个线程共用同一个锁资源才可以。但是同步代码块不能随便加,否则会造成执行效率降低,最差会变成单线程,因此最好将能造成安全隐患的代码加入同步代码块,其他正常执行。

(2)同步方法,保证方法中最多只有一条线程在执行,锁资源是this,格式如下

 

(3)静态同步方法,保证方法中最多只有一条线程在执行,锁资源是类名.class,格式如下

 

经典面试题,简述HashMap、Hashtable和ConcurrentHashMap的区别

HashMap:是异步线程不安全的,但是效率高

Hashtable:是同步线程安全的,但是效率低

ConcurrentHashMap:是异步线程安全的,底层采用分桶锁的机制,效率比Hashtable高很多

经典面试题

同步和异步:同步是在程序中最多只有一条线程,异步是在程序中允许有多条线程。

同步一定线程安全 √

线程安全一定同步 ×

异步一定线程安全 ×

异步一定有线程安全隐患 ×

线程不安全一定异步 √

死锁

由于锁互相嵌套导致的互相锁死的现象,称之为死锁,参考打印机和扫描仪案例。

 

测试结果发现,每个线程开启都分别执行了一次打印和扫描,但是后续不再执行,分析发现第一个线程执行完打印后,准备执行扫描但是发现扫描锁资源s被占了,因此无法继续执行扫描并释放打印锁资源p,而二个线程执行完扫描后,准备执行打印发现打印锁资源p被占了,因此无法继续执行打印并释放扫描锁资源s,从而导致两个线程一直等待对方释放锁资源,这就形成了死锁。

等待唤醒机制

使用一个对象加锁,那么必须使用相同的对象进行等待和唤醒,参考模拟两个学生轮流问问题和生产者-消费模型。

 

测试结果

生产者-消费者模型:开辟一条线程代表生产者,开辟一条线程代表消费者,生产者生产后消费者才能消费,消费者消费后java线程1基础入门生产者才能生产,生产和消费的数量使用随机数,生产者生产的产品数量+剩余的产品数量不能超过1000。

 

测试结果

notify和notifyAll

参考修改后的生产者-消费模式,上面的代码是有一个问题的,就是锁可能空闲,分析比较复杂,后面再补。

wait和sleep的区别

sleep:可以设定休眠时间,到点了自然醒,如果sleep了有锁也不释放锁,且不释放执行权,没锁就释放执行权,是Thread类的一个静态方法。

wait:可以设定休眠时间,也可不设定休眠时间,如果wait了则释放锁并释放执行权,是Object的一个方法。

线程的状态

新建(NEW):新建一个线程对象

就绪(Runnable):调用了线程的start方法就是就绪状态,能否进入运行状态还要看能否获得CPU时间片

运行(Running):就绪状态的线程获得系统调度,即获得CPU时间片后就进入运行状态

阻塞(Blocked):线程放弃了CPU的使用权,进入阻塞状态

消亡(Dead):线程run方法,或者main方法结束后,进入消亡状态

具体参考博客 https://www.cnblogs.com/hejing-swust/p/8038263.html ,里面有详细图文说明。

守护线程

守护其他线程的线程,在java中分为守护线程和被守护线程,默认为被守护线程。如果被守护线程结束了,守护线程也将结束。GC就是一个守护线程,main方法执行的主线程就是被守护线程。

参考士兵和将军案例,士兵就是守护线程,将军消亡后士兵线程将不再执行。

 

测试结果发现,将军消亡后(将军掉血是被守护线程),士兵线程(守护线程)不再继续执行,几乎马上就结束了,如果不设置士兵线程为守护线程,程序一定会执行到士兵掉血到没有才结束。

线程的优先级

线程默认有1-10个优先级,优先级越高抢到资源的概率越大,但是差别不是很大,参考如下案例。

 

测试结果

总结

以上就是线程入门的基础知识,以窗口卖票为例,可以看出开启的三个线程都需要执行run方法里面的循环卖票逻辑,他们共享一个票数的变量,哪个线程抢到了同步锁,就可以执行卖票,另外没抢到的只能等待,直到下次循环得到锁资源才可以继续卖票。

参考博客

(1)https://blog.csdn.net/_/article/details/ 线程阻塞

  • 上一篇: java基础成功案例
  • 下一篇: java基础学php
  • 版权声明


    相关文章:

  • java基础成功案例2025-04-22 13:26:00
  • 吃透java基础2025-04-22 13:26:00
  • java联系基础题2025-04-22 13:26:00
  • java基础小记2025-04-22 13:26:00
  • typescript需要java基础2025-04-22 13:26:00
  • java基础学php2025-04-22 13:26:00
  • java中基础代码2025-04-22 13:26:00
  • java变成基础2025-04-22 13:26:00
  • java语言基础答案2025-04-22 13:26:00
  • 基础如何学java2025-04-22 13:26:00