多线程

多线程(multiple thread)是计算机实现多任务并行处理的一种方式。

在单线程情况下,计算机中存在一个控制权,并按照顺序依次执行指令。单线程好像是一个只有一个队长指挥的小队,整个小队同一个时间只能执行一个任务。

单线程

在多线程情境下,计算机中有多个控制权。多个控制权可以同时进行,每个控制权依次执行一系列的指令。多线程好像是一个小队中的成员同时执行不同的任务。

多线程

传统意义上,多线程是由操作系统提供的功能。对于单核的CPU,硬件中只存在一个线程。在操作系统的控制下,CPU会在不同的任务间(线程间)切换,从而造成多任务齐头并进的效果。这是单CPU分时复用机制下的多线程。现在,随着新的硬件技术的发展,硬件本身开始提供多线程支持,比如多核和超线程技术。然而,硬件的多线程还是要接受操作系统的统一管理。在操作系统之上的多线程程序依然通用。

多个线程可以并存于同一个进程空间。在JVM的一个进程空间中,一个栈(stack)代表了方法调用的次序。对于多线程来说,进程空间中需要有多个栈,以记录不同线程的调用次序。多个栈互不影响,但所有的线程将共享堆(heap)中的对象。

创建线程

Java中“一切皆对象”,线程也被封装成一个对象。我们可以通过继承Thread类来创建线程。线程类中的的run()方法包含了该线程应该执行的指令。我们在衍生类中覆盖该方法,以便向线程说明要做的任务:

public classTest

{

public static voidmain(String[] args)

{

NewThread thread1 = newNewThread();

NewThread thread2 = newNewThread();

thread1.start(); //start thread1

thread2.start(); //start thread2

}

}

/*** create new thread by inheriting Thread

*/

class NewThread extendsThread {

private static int threadID = 0; //shared by all

/*** constructor

*/

publicNewThread() {

super("ID:" + (++threadID));

}

/*** convert object to string

*/

publicString toString() {

return super.getName();

}

/*** what does the thread do?

*/

public voidrun() {

System.out.println(this);

}

}

(++是Java中的累加运算符,即让变量加1。这里++出现在threadID之前,说明先将threadID加1,再对周边的表达式求值

toString是Object根类的方法,我们通过覆盖该方法,来将对象转换成字符串。当我们打印该对象时,Java将自动调用该方法。)

可以看到,Thread基类的构建方法(super())可以接收一个字符串作为参数。该字符串是该线程的名字,并使用getName()返回。

定义类之后,我们在main()方法中创建线程对象。每个线程对象为一个线程。创建线程对象后,线程还没有开始执行。

我们调用线程对象的start()方法来启动线程。start()方法可以在构造方法中调用。这样,我们一旦使用new创建线程对象,就立即执行。

Thread类还提供了下面常用方法:

join(Thread tr)   等待线程tr完成

setDaemon()       设置当前线程为后台daemon (进程结束不受daemon线程的影响)

Runnable

实现多线程的另一个方式是实施Runnable接口,并提供run()方法。实施接口的好处是容易实现多重继承(multiple inheritance)。然而,由于内部类语法,继承Thread创建线程可以实现类似的功能。我们在下面给出一个简单的例子,而不深入:

public classTest

{

public static voidmain(String[] args)

{

Thread thread1 = new Thread(new NewThread(), "first");

Thread thread2 = new Thread(new NewThread(), "second");

thread1.start(); //start thread1

thread2.start(); //start thread2

}

}

/*** create new thread by implementing Runnable

*/

class NewThread implementsRunnable {

/*** convert object to string

*/

publicString toString() {

returnThread.currentThread().getName();

}

/*** what does the thread do?

*/

public voidrun() {

System.out.println(this);

}

}

synchronized

多任务编程的难点在于多任务共享资源。对于同一个进程空间中的多个线程来说,它们都共享堆中的对象。某个线程对对象的操作,将影响到其它的线程。

在多线程编程中,要尽力避免竞争条件(racing condition),即运行结果依赖于不同线程执行的先后。线程是并发执行的,无法确定线程的先后,所以我们的程序中不应该出现竞争条件。

然而,当多任务共享资源时,就很容易造成竞争条件。我们需要将共享资源,并造成竞争条件的多个线程线性化执行,即同一时间只允许一个线程执行。

下面是一个售票程序。3个售票亭

(Booth)共同售卖100张票(Reservoir)。每个售票亭要先判断是否有余票,然后再卖出一张票。如果只剩下一张票,在一个售票亭的判断和售

出两个动作之间,另一个售票亭卖出该票,那么第一个售票亭(由于已经执行过判断)依然会齿形卖出,造成票的超卖。为了解决该问题,判断和售出两个动作之间

不能有“空隙”。也就是说,在一个线程完成了这两个动作之后,才能有另一个线程执行。

在Java中,我们将共享的资源置于一个对象中,比如下面r(Reservoir)对象。它包含了总共的票数;将可能造成竞争条件的,针对共享资源的操作,放在synchronized(同步)方法中,比如下面的sellTicket()。synchronized是方法的修饰符。在Java中,同一对象的synchronized方法只能同时被一个线程调用。其他线程必须等待该线程调用结束,(余下的线程之一)才能运行。这样,我们就排除了竞争条件的可能。

在main()方法中,我们将共享的资源(r对象)传递给多个线程:

public classTest

{

public static voidmain(String[] args)

{

Reservoir r = new Reservoir(100);

Booth b1 = newBooth(r);

Booth b2 = newBooth(r);

Booth b3 = newBooth(r);

}

}

/**

* contain shared resource

*/

classReservoir {

private inttotal;

public Reservoir(intt)

{

this.total =t;

}

/*** Thread safe method

* serialized access to Booth.total

*/

public synchronized booleansellTicket()

{

if(this.total > 0) {

this.total = this.total - 1;

return true; //successfully sell one

}

else{

return false; //no more tickets

}

}

}

/*** create new thread by inheriting Thread

*/

class Booth extendsThread {

private static int threadID = 0; //owned by Class object

private Reservoir release; //sell this reservoir

private int count = 0; //owned by this thread object

/*** constructor

*/

publicBooth(Reservoir r) {

super("ID:" + (++threadID));

this.release = r; //all threads share the same reservoir

this.start();

}

/*** convert object to string

*/

publicString toString() {

return super.getName();

}

/*** what does the thread do?

*/

public voidrun() {

while(true) {

if(this.release.sellTicket()) {

this.count = this.count + 1;

System.out.println(this.getName() + ": sell 1");

try{

sleep((int) Math.random()*100); //random intervals

}

catch(InterruptedException e) {

throw newRuntimeException(e);

}

}

else{

break;

}

}

System.out.println(this.getName() + " I sold:" +count);

}

}

(Math.random()用于产生随机数)

Java的每个对象都自动包含有一个用于支持同步的计数器,记录synchronized方法的调用次数。线程获得该计数器,计数器加1,并执行synchronized方法。如果方法内部进一步调用了该对象的其他synchronized方法,计数器加1。当synchronized方法调用结束并退出时,计数器减1。其他线程如果也调用了同一对象的synchronized方法,必须等待该计数器变为0,才能锁定该计数器,开始执行。Java中的类同样也是对象(Class类对象)。Class类对象也包含有计数器,用于同步。

关键代码

上面,我们利用synchronized修饰符同步了整个方法。我们可以同步部分代码,而不是整个方法。这样的代码被称为关键代码(critical section)。我们使用下面的语法:

synchronized(syncObj) {

...;

}

花括号中包含的是想要同步的代码,syncObj是任意对象。我们将使用syncObj对象中的计数器,来同步花括号中的代码。

java 线程 free_Java进阶05 多线程相关推荐

  1. java 线程面试题_JAVA多线程面试题(一)

    1.进程和线程的区别 一个进程是一个独立(self contained)的运行环境,它可以被看作一个程序或者一个应用.而线程是在进程中执行的一个任务.Java运行环境是一个包含了不同的类和程序的单一进 ...

  2. java线程代码实现_Java 多线程代码实现讲解

    作为一个完全面向对象的语言,Java提供了类 java.lang.Thread 来方便多线程编程,这个类提供了大量的方法来方便我们控制自己的各个线程.那么如何提供给 Java 我们要线程执行的代码呢? ...

  3. java线程堆栈nid.tid_java多线程死锁 Java问题定位之Java线程堆栈分析(2)

    从上面的main线程看,线程堆栈里面的最直观的信息是当前线程的调用上下文,即从哪个函数调用到哪个函数(从下往上看),正执行到哪一类的哪一行,借助这些信息,我们就对当前系统正在做什么一目了然. 另外,从 ...

  4. java 线程同步condtion_Java:多线程,使用同步锁(Lock)时利用Condition类实现线程间通信...

    [我们不仅可以使用synchronized来实现多线程同步,还可以通过创建锁对象来实现多线程的同步,还是上次模拟取现的操作,这次利用lock对象实现同步,下面是代码:    import 如果程序不使 ...

  5. java线程知识梳理_Java多线程——多线程相关知识的逻辑关系梳理

    1 学习多线程知识的根本目标 多线程知识的根本目标是:设计稳健的并发程序. 当然,本文无法回答这个实践性很强的问题(这与具体的业务相关,涉及到具体的策略),本文主要阐述相关知识之间的关系,希望初学者不 ...

  6. java线程池 锁_java多线程——锁

    这是多线程系列第四篇,其他请关注以下: 如果你看过前面几篇关于线程的文字,会对线程的实现原理了然于胸,有了理论的支持会对实践有更好的指导,那么本篇会偏重于线程的实践,对线程的几种应用做个简要的介绍. ...

  7. java线程下载文件_Java多线程下载文件实例详解

    本文实例为大家分享了Java多线程下载文件的具体代码,供大家参考,具体内容如下 import java.io.File; import java.io.InputStream; import java ...

  8. java 线程锁概念_Java多线程——锁概念与锁优化

    为了性能与使用的场景,Java实现锁的方式有非常多.而关于锁主要的实现包含synchronized关键字.AQS框架下的锁,其中的实现都离不开以下的策略. 悲观锁与乐观锁 乐观锁.乐观的想法,认为并发 ...

  9. java线程异步传值_Java 多线程传值的四种方法

    Java 多线程传值的四种方法 作者: sunjs 更新时间:2020-09-11 15:20:16 原文链接 其实大家都知道多线程传值有三种方式: 1:通过构造方法传递数据 2:通过变量和方法传递数 ...

最新文章

  1. 学习LOWORD、 HIWORD、LOBYTE、HIBYTE
  2. python导入txt为dataframe-python读取文本中数据并转化为DataFrame的实例
  3. Django(part43)--分页
  4. Android开发之动画(转)
  5. linux的system () 函数详解
  6. activity绑定service
  7. 过来人的亲身经验告诉你,如何从菜鸟晋升月薪过万的测试工程师
  8. 错误:'BasicLSTMCell' object has no attribute '_kernel'
  9. (二十二)美萍酒店管理系统:系统维护_系统设置_房间设置_其他测试
  10. 【CVPR2022】论文阅读:Revisiting Skeleton-based Action Recognition
  11. win10安装免安装版的mysql5.7
  12. 选票统计 SDUT
  13. 项目管理知识体系系指南学习总结(一)
  14. 华为云对象存储服务OBS教你一招轻松解决存储难题
  15. 神雕侠侣手游服务器维护,神雕侠侣手游7月30日更新维护公告内容大全
  16. [猎豹网校 Lua] 第二课.HelloLua
  17. java mysql 分布式事务_java事务(三)——自己实现分布式事务
  18. 为什么装一键GHOST恢复,开机没有启动选项?
  19. oracle创建job并执行job
  20. 论文阅读 8 | Contrastive Decoder Generator for Few-shot Learning in Product Quality Prediction

热门文章

  1. 【Git】Python项目依赖库过大无法提交的问题
  2. 【Git】Git兼容GitHub和Gitee的问题与对策
  3. 【译】Node.js 日志打印指南
  4. ant design form表单的时间处理
  5. springMVC学习(7)-springMVC校验
  6. 使用sax解析xml
  7. 【BZOJ3524】 [Poi2014]Couriers
  8. 某个知名技术论坛用户名和密码泄露(第一部分)
  9. 广东鸿图:搭建业务报表,摆脱人工计数,工作效率提升150%
  10. 1个工具,4个技巧,就能高效开发各种报表!