王者荣耀 收费那个游戏收费吗

感谢本文作者
昵称:黑色平底锅
加入时间: 0:00:00
这家伙很懒,虾米都没写
以下内容也很赞哦Java程序设计课件第九章 多线程_百度文库
两大类热门资源免费畅读
续费一年阅读会员,立省24元!
评价文档:
Java程序设计课件第九章 多线程
上传于|0|0|文档简介
&&Java程序设计课件
大小:1.07MB
登录百度文库,专享文档复制特权,财富值每天免费拿!
你可能喜欢Java并发编程示例(一):线程的创建和执行
投稿:junjie
字体:[ ] 类型:转载 时间:
这篇文章主要介绍了Java并发编程示例(一):线程的创建和执行,本文是系列文章的第一篇,需要的朋友可以参考下
在IT圈里,每当我们谈论并发时,必定会说起在一台计算机上同时运行的一系列线程。如果这台电脑上有多个处理器或者是一个多核处理器,那么这时是实实在在的“同时运行”;但是,如果计算机只有一个单核处理器,那么这时的“同时运行”只是表象而已。
所有的现代操作系统全部支持任务的并发执行。你可以边听音乐,边上网看新闻,还不耽误首发电子邮件。我们可以说,这种并发是 进程级并发 。在进程内部,我也可以看到有许许多多的并发任务。我们把运行在一个进程里面的并发任务称 线程。
和并发相关的另外一个常见概念是 并行。并发与并行之间,存在着一些不同,也存在着一些联系。一些程序员(Author,窃译为“程序员”)认为,在一个单核处理器上多线程地执行应用程序就是并发,并且你可以观察到程序员的执行;另外,当你的程序以多线程的形式运行在多个处理器或者是多核处理器上时,就是并行。还有一些程序员认为如果应用程序的线程没有按照预先设定好的顺序执行就是并发;为了简化问题解决方案而是用个线程,并且这些线程是按照一定顺序在执行,那么这是并行。
本章将通过十二个示例来演示如何使用Java7的API来执行一些基本的线程操作。你将可以看到,在Java程序中,如何创建、执行线程,如何控制线程的执行,如何将一组线程作为一个单元来操纵等等。
在本节,我们将学习如何在Java程序中创建线程,以及如何运行。在Java程序中,一切皆为 Object ,线程也是如此。创建线程的方式有两种:
1.继承Thread类,并且重写run()方法;
2.创建一个类,实现Runnable接口,然后创建一个Thread类的对象,然后将实现Runnable接口的类的实例作为参数,传递给Thread类的实例。
在本节,我们将使用第二种方式,来创建十个线程,并且运行起来。每个线程计算并打印两个十以内的整数之积。
根据下面所述的步骤来实现这里例子:
1.创建一个名为Calculator的类,并且实现Runnable接口。代码如下:
public class Calculator implements Runnable {
2.声明一个私有的整形属性,名称为number,实现该类的构造函数来初始化刚刚声明的属性。代码如下:
public Calculator(int number) {
&&& this.number =
3.实现run()方法,该方法是我们创建的线程执行时运行的程序(instruction),故而该方法用于计算乘法表。具体代码如下:
public void run() {
&&& for (int i = 0; i & 10; i++) {
&&&&&&& System.out.printf("%s: %d * %d = %d\n",
&&&&&&&&&&&&&&& Thread.currentThread().getName(),
&&&&&&&&&&&&&&& number, i, i * number);
4.现在,是时候实现示例应用的主类(main class)了。创建名为Main的类,在该类中添加main方法。代码如下:
public class Main {
&&& public static void main(String[] args) {
5.在main()方法内部,创建一个遍历十次的for循环,在循环体内,创建一个Calculator类的对象calculator,创建一个Thread类的对象thread,将calculator作为构造函数的参数,传递给thread的初始化语句。最后,调用thread对象的start()方法。代码如下:
for (int i = 0; i & 10; i++) {
&&& Calculator calculator = new Calculator(i);
&&& Thread thread = new Thread(calculator);
&&& thread.start();
6.运行这个程序,看不同线程是如何并发执行的。
知其所以然
下面是运行程序时,控制台打印出来的的一段输出,我们可以看到我们创建的所有线程都在并发执行。
Thread-3: 3 * 5 = 15
Thread-0: 0 * 2 = 0
Thread-3: 3 * 6 = 18
Thread-1: 1 * 6 = 6
Thread-1: 1 * 7 = 7
Thread-3: 3 * 7 = 21
Thread-3: 3 * 8 = 24
Thread-0: 0 * 3 = 0
Thread-0: 0 * 4 = 0
Thread-3: 3 * 9 = 27
Thread-1: 1 * 8 = 8
所有的Java程序最少执行一个线程。当我们运行Java程序时,Java虚拟机(以后称为JVM)会运行一个线程,调用含有main()方法的程序。
当调用Thread对象的start()方法时,就会创建另外一个线程。调用多少次start()方法,就会创建多少个线程。
当所有线程执行完成后,Java程序会随之终止。(非特殊情况下,是所有非后台(non-daemon)线程执行完成)当启动线程(例如执行main()方法的线程)终止后,其余线程会继续执行直到完成计算任务。当其中一个线程调用System.exit(),请求JVM中止程序时,所有线程中止其执行。
调用Thread对象的run()方法时,不会创建线程;同样,调用实现Runnable接口的类run()方法时,也不会创建线程。只有调用Thread对象的start()方法时,才会创建线程。
正如本节开头所说,还有另外一种创建线程的方法:继承Thread类,重写run()方法,这样,就可以创建一个Thread子类的对象,然后调用该对象的start()方法来创建线程。
因为准备面试,找来一堆Java多线程方面的资料,其中包括这本《Java 7 Concurrency Cookbook》,讲解的非常浅显易懂,非常适合对多线程了解不多,又想认真学习一下的朋友。找了找,没找到中文版,干脆自己动手丰衣足食。所以,计划出一个非官方翻译版,书名暂时定为 《Java7并发示例集》。
本文是从 《Java 7 Concurrency Cookbook》 (D瓜哥窃译为 《Java7并发示例集》 )翻译而来,仅作为学习资料使用。没有授权,不得用于任何商业行为。
原书没有完整代码,不利于查看。所以,D瓜哥加了一个小节,专门展示本节所示的完整版代码。
Calculator类的完整代码
package com.diguage.books.concurrencycookbook.chapter1.recipe1;
&* Time: 21:42
public class Calculator implements Runnable {
&&& public Calculator(int number) {
&&&&&&& this.number =
&&& @Override
&&& public void run() {
&&&&&&& for (int i = 0; i & 10; i++) {
&&&&&&&&&&& System.out.printf("%s: %d * %d = %d\n",
&&&&&&&&&&&&&&&&&&& Thread.currentThread().getName(),
&&&&&&&&&&&&&&&&&&& number, i, i * number);
Main类的完整代码
package com.diguage.books.concurrencycookbook.chapter1.recipe1;
&* Time: 19:46
public class Main {
&&& public static void main(String[] args) {
&&&&&&& for (int i = 0; i & 10; i++) {
&&&&&&&&&&& Calculator calculator = new Calculator(i);
&&&&&&&&&&& Thread thread = new Thread(calculator);
&&&&&&&&&&& thread.start();
您可能感兴趣的文章:
大家感兴趣的内容
12345678910
最近更新的内容
常用在线小工具2140人阅读
JAVA(51)
Java多线程程序设计详细解析&
&&&& 一、理解多线程  多线程是这样一种机制,它允许在程序中并发执行多个指令流,每个指令流都称为一个线程,彼此间互相独立。  线程又称为轻量级进程,它和进程一样拥有独立的执行控制,由操作系统负责调度,区别在于线程没有独立的存储空间,而是和所属进程中的其它线程共享一个存储空间,这使得线程间的通信远较进程简单。  多个线程的执行是并发的,也就是在逻辑上&同时&,而不管是否是物理上的&同时&。如果系统只有一个CPU,那么真正的&同时&是不可能的,但是由于CPU的速度非常快,用户感觉不到其中的区别,因此我们也不用关心它,只需要设想各个线程是同时执行即可。  多线程和传统的单线程在程序设计上最大的区别在于,由于各个线程的控制流彼此独立,使得各个线程之间的代码是乱序执行的,由此带来的线程调度,同步等问题,将在以后探讨。  二、在Java中实现多线程  我们不妨设想,为了创建一个新的线程,我们需要做些什么?很显然,我们必须指明这个线程所要执行的代码,而这就是在Java中实现多线程我们所需要做的一切!  真是神奇!Java是如何做到这一点的?通过类!作为一个完全面向对象的语言,Java提供了类java.lang.Thread来方便多线程编程,这个类提供了大量的方法来方便我们控制自己的各个线程,我们以后的讨论都将围绕这个类进行。  那么如何提供给&Java&我们要线程执行的代码呢?让我们来看一看&Thread&类。Thread&类最重要的方法是run(),它为Thread类的方法start()所调用,提供我们的线程所要执行的代码。为了指定我们自己的代码,只需要覆盖它!  方法一:继承&Thread&类,覆盖方法&run(),我们在创建的&Thread&类的子类中重写&run()&,加入线程所要执行的代码即可。下面是一个例子:
public&class&MyThread&extends&Thread  ...{  int&count=&1,&  public&MyThread(int&num)  ...{  number&=&  System.out.println  (&创建线程&&&+&number);  }  public&void&run()&...{  while(true)&...{  System.out.println  (&线程&&&+&number&+&&:计数&&&+&count);  if(++count==&6)&return;  }  }  public&static&void&main(String&args[])  ...{  for(int&i&=&0;  i&〈&5;&i++)&new&MyThread(i+1).start();  }  }
    这种方法简单明了,符合大家的习惯,但是,它也有一个很大的缺点,那就是如果我们的类已经从一个类继承(如小程序必须继承自&Applet&类),则无法再继承&Thread&类,这时如果我们又不想建立一个新的类,应该怎么办呢?  我们不妨来探索一种新的方法:我们不创建Thread类的子类,而是直接使用它,那么我们只能将我们的方法作为参数传递给&Thread&类的实例,有点类似回调函数。但是&Java&没有指针,我们只能传递一个包含这个方法的类的实例。  那么如何限制这个类必须包含这一方法呢?当然是使用接口!(虽然抽象类也可满足,但是需要继承,而我们之所以要采用这种新方法,不就是为了避免继承带来的限制吗?)  Java&提供了接口&java.lang.Runnable&来支持这种方法。  方法二:实现&Runnable&接口   Runnable接口只有一个方法run(),我们声明自己的类实现Runnable接口并提供这一方法,将我们的线程代码写入其中,就完成了这一部分 的任务。但是Runnable接口并没有任何对线程的支持,我们还必须创建Thread类的实例,这一点通过Thread类的构造函数 &public&Thread(Runnable&target);来实现。下面是一个例子:
public&class&MyThread&implements&Runnable  ...{  int&count=&1,&  public&MyThread(int&num)  ...{  number&=&  System.out.println(&创建线程&&&+&number);  }  public&void&run()  ...{  while(true)  ...{  System.out.println  (&线程&&&+&number&+&&:计数&&&+&count);  if(++count==&6)&return;  }  }  public&static&void&main(String&args[])  ...{  for(int&i&=&0;&i&〈&5;  i++)&new&Thread(new&MyThread(i+1)).start();  }  }
    严格地说,创建Thread子类的实例也是可行的,但是必须注意的是,该子类必须没有覆盖&Thread&类的&run&方法,否则该线程执行的将是子类的&run&方法,而不是我们用以实现Runnable&接口的类的&run&方法,对此大家不妨试验一下。   使用&Runnable&接口来实现多线程使得我们能够在一个类中包容所有的代码,有利于封装,它的缺点在于,我们只能使用一套代码,若想创建多个线程 并使各个线程执行不同的代码,则仍必须额外创建类,如果这样的话,在大多数情况下也许还不如直接用多个类分别继承&Thread&来得紧凑。  综上所述,两种方法各有千秋,大家可以灵活运用。  下面让我们一起来研究一下多线程使用中的一些问题。  三、线程的四种状态  1.&新状态:线程已被创建但尚未执行(start()&尚未被调用)。  2.&可执行状态:线程可以执行,虽然不一定正在执行。CPU&时间随时可能被分配给该线程,从而使得它执行。  3.&死亡状态:正常情况下&run()&返回使得线程死亡。调用&stop()或&destroy()&亦有同样效果,但是不被推荐,前者会产生异常,后者是强制终止,不会释放锁。  4.&阻塞状态:线程不会被分配&CPU&时间,无法执行。  四、线程的优先级  线程的优先级代表该线程的重要程度,当有多个线程同时处于可执行状态并等待获得&CPU&时间时,线程调度系统根据各个线程的优先级来决定给谁分配&CPU&时间,优先级高的线程有更大的机会获得&CPU&时间,优先级低的线程也不是没有机会,只是机会要小一些罢了。  你可以调用&Thread&类的方法&getPriority()&和&setPriority()来存取线程的优先级,线程的优先级界于1(MIN_PRIORITY)和10(MAX_PRIORITY)之间,缺省是5(NORM_PRIORITY)。  五、线程的同步  由于同一进程的多个线程共享同一片存储空间,在带来方便的同时,也带来了访问冲突这个严重的问题。Java语言提供了专门机制以解决这种冲突,有效避免了同一个数据对象被多个线程同时访问。  由于我们可以通过&private&关键字来保证数据对象只能被方法访问,所以我们只需针对方法提出一套机制,这套机制就是&synchronized&关键字,它包括两种用法:synchronized&方法和&synchronized&块。  1.&synchronized&方法:通过在方法声明中加入&synchronized关键字来声明&synchronized&方法。如:  public&synchronized&void&accessVal(int&newVal);     &synchronized&方法控制对类成员变量的访问:每个类实例对应一把锁,每个&synchronized&方法都必须获得调用该方法的类实例 的锁方能执行,否则所属线程阻塞,方法一旦执行,就独占该锁,直到从该方法返回时才将锁释放,此后被阻塞的线程方能获得该锁,重新进入可执行状态。   这种机制确保了同一时刻对于每一个类实例,其所有声明为&synchronized&的成员函数中至多只有一个处于可执行状态(因为至多只有一个能够获 得该类实例对应的锁),从而有效避免了类成员变量的访问冲突(只要所有可能访问类成员变量的方法均被声明为&synchronized)。  在&Java&中,不光是类实例,每一个类也对应一把锁,这样我们也可将类的静态成员函数声明为&synchronized&,以控制其对类的静态成员变量的访问。   synchronized&方法的缺陷:若将一个大的方法声明为synchronized&将会大大影响效率,典型地,若将线程类的方法&run()& 声明为&synchronized&,由于在线程的整个生命期内它一直在运行,因此将导致它对本类任何&synchronized&方法的调用都永远不会 成功。当然我们可以通过将访问类成员变量的代码放到专门的方法中,将其声明为&synchronized&,并在主方法中调用来解决这一问题,但是 &Java&为我们提供了更好的解决办法,那就是&synchronized&块。  2.&synchronized&块:通过&synchronized关键字来声明synchronized&块。语法如下:  synchronized(syncObject)  {  //允许访问控制的代码  }    synchronized&块是这样一个代码块,其中的代码必须获得对象&syncObject&(如前所述,可以是类实例或类)的锁方能执行,具体机制同前所述。由于可以针对任意代码块,且可任意指定上锁的对象,故灵活性较高。  六、线程的阻塞   为了解决对共享存储区的访问冲突,Java&引入了同步机制,现在让我们来考察多个线程对共享资源的访问,显然同步机制已经不够了,因为在任意时刻所要 求的资源不一定已经准备好了被访问,反过来,同一时刻准备好了的资源也可能不止一个。为了解决这种情况下的访问控制问题,Java&引入了对阻塞机制的支 持。  阻塞指的是暂停一个线程的执行以等待某个条件发生(如某资源就绪),学过操作系统的同学对它一定已经很熟悉了。Java&提供了大量方法来支持阻塞,下面让我们逐一分析。   1.&sleep()&方法:sleep()&允许指定以毫秒为单位的一段时间作为参数,它使得线程在指定的时间内进入阻塞状态,不能得到CPU&时 间,指定的时间一过,线程重新进入可执行状态。典型地,sleep()&被用在等待某个资源就绪的情形:测试发现条件不满足后,让线程阻塞一段时间后重新 测试,直到条件满足为止。  2.&suspend()&和&resume()&方法:两个方法配套使用,suspend()使得线程进 入阻塞状态,并且不会自动恢复,必须其对应的resume()&被调用,才能使得线程重新进入可执行状态。典型地,suspend()&和&resume ()&被用在等待另一个线程产生的结果的情形:测试发现结果还没有产生后,让线程阻塞,另一个线程产生了结果后,调用&resume()&使其恢复。  3.&yield()&方法:yield()&使得线程放弃当前分得的&CPU&时间,但是不使线程阻塞,即线程仍处于可执行状态,随时可能再次分得&CPU&时间。调用&yield()&的效果等价于调度程序认为该线程已执行了足够的时间从而转到另一个线程。   4.&wait()&和&notify()&方法:两个方法配套使用,wait()&使得线程进入阻塞状态,它有两种形式,一种允许指定以毫秒为单位的 一段时间作为参数,另一种没有参数,前者当对应的&notify()&被调用或者超出指定时间时线程重新进入可执行状态,后者则必须对应的&notify ()&被调用。  初看起来它们与&suspend()&和&resume()&方法对没有什么分别,但是事实上它们是截然不同的。区别的核心在于,前面叙述的所有方法,阻塞时都不会释放占用的锁(如果占用了的话),而这一对方法则相反。  上述的核心区别导致了一系列的细节上的区别。   首先,前面叙述的所有方法都隶属于&Thread&类,但是这一对却直接隶属于&Object&类,也就是说,所有对象都拥有这一对方法。初看起来这十 分不可思议,但是实际上却是很自然的,因为这一对方法阻塞时要释放占用的锁,而锁是任何对象都具有的,调用任意对象的&wait()&方法导致线程阻塞, 并且该对象上的锁被释放。  而调用&任意对象的notify()方法则导致因调用该对象的&wait()&方法而阻塞的线程中随机选择的一个解除阻塞(但要等到获得锁后才真正可执行)。  其次,前面叙述的所有方法都可在任何位置调用,但是这一对方法却必须在&synchronized&方法或块中调用,理由也很简单,只有在synchronized&方法或块中当前线程才占有锁,才有锁可以释放。   同样的道理,调用这一对方法的对象上的锁必须为当前线程所拥有,这样才有锁可以释放。因此,这一对方法调用必须放置在这样的 &synchronized&方法或块中,该方法或块的上锁对象就是调用这一对方法的对象。若不满足这一条件,则程序虽然仍能编译,但在运行时会出现 &IllegalMonitorStateException&异常。  wait()&和&notify()&方法的上述特性决定了它 们经常和synchronized&方法或块一起使用,将它们和操作系统的进程间通信机制作一个比较就会发现它们的相似性:synchronized方法 或块提供了类似于操作系统原语的功能,它们的执行不会受到多线程机制的干扰,而这一对方法则相当于&block&和wakeup&原语(这一对方法均声明 为&synchronized)。  它们的结合使得我们可以实现操作系统上一系列精妙的进程间通信的算法(如信号量算法),并用于解决各种复杂的线程间通信问题。关于&wait()&和&notify()&方法最后再说明两点:  第一:调用&notify()&方法导致解除阻塞的线程是从因调用该对象的&wait()&方法而阻塞的线程中随机选取的,我们无法预料哪一个线程将会被选择,所以编程时要特别小心,避免因这种不确定性而产生问题。   第二:除了&notify(),还有一个方法&notifyAll()&也可起到类似作用,唯一的区别在于,调用&notifyAll()&方法将把因 调用该对象的&wait()&方法而阻塞的所有线程一次性全部解除阻塞。当然,只有获得锁的那一个线程才能进入可执行状态。  谈到阻塞,就不能不谈一谈死锁,略一分析就能发现,suspend()&方法和不指定超时期限的&wait()&方法的调用都可能产生死锁。遗憾的是,Java&并不在语言级别上支持死锁的避免,我们在编程中必须小心地避免死锁。   以上我们对&Java&中实现线程阻塞的各种方法作了一番分析,我们重点分析了&wait()&和&notify()方法,因为它们的功能最强大,使用 也最灵活,但是这也导致了它们的效率较低,较容易出错。实际使用中我们应该灵活使用各种方法,以便更好地达到我们的目的。  七、守护线程   守护线程是一类特殊的线程,它和普通线程的区别在于它并不是应用程序的核心部分,当一个应用程序的所有非守护线程终止运行时,即使仍然有守护线程在运 行,应用程序也将终止,反之,只要有一个非守护线程在运行,应用程序就不会终止。守护线程一般被用于在后台为其它线程提供服务。  可以通过调用方法&isDaemon()&来判断一个线程是否是守护线程,也可以调用方法&setDaemon()&来将一个线程设为守护线程。  八、线程组  线程组是一个&Java&特有的概念,在&Java&中,线程组是类ThreadGroup&的对象,每个线程都隶属于唯一一个线程组,这个线程组在线程创建时指定并在线程的整个生命期内都不能更改。  你可以通过调用包含&ThreadGroup&类型参数的&Thread&类构造函数来指定线程属的线程组,若没有指定,则线程缺省地隶属于名为&system&的系统线程组。   在&Java&中,除了预建的系统线程组外,所有线程组都必须显式创建。在&Java&中,除系统线程组外的每个线程组又隶属于另一个线程组,你可以在 创建线程组时指定其所隶属的线程组,若没有指定,则缺省地隶属于系统线程组。这样,所有线程组组成了一棵以系统线程组为根的树。  Java&允许我们对一个线程组中的所有线程同时进行操作,比如我们可以通过调用线程组的相应方法来设置其中所有线程的优先级,也可以启动或阻塞其中的所有线程。  Java&的线程组机制的另一个重要作用是线程安全。线程组机制允许我们通过分组来区分有不同安全特性的线程,对不同组的线程进行不同的处理,还可以通过线程组的分层结构来支持不对等安全措施的采用。  Java&的&ThreadGroup&类提供了大量的方法来方便我们对线程组树中的每一个线程组以及线程组中的每一个线程进行操作。  九、总结  在本文中,我们讲述了&Java&多线程编程的方方面面,包括创建线程,以及对多个线程进行调度、管理。我们深刻认识到了多线程编程的复杂性,以及线程切换开销带来的多线程程序的低效性,这也促使我们认真地思考一个问题:我们是否需要多线程?何时需要多线程?  多线程的核心在于多个代码块并发执行,本质特点在于各代码块之间的代码是乱序执行的。我们的程序是否需要多线程,就是要看这是否也是它的内在特点。   假如我们的程序根本不要求多个代码块并发执行,那自然不需要使用多线程;假如我们的程序虽然要求多个代码块并发执行,但是却不要求乱序,则我们完全可以 用一个循环来简单高效地实现,也不需要使用多线程;只有当它完全符合多线程的特点时,多线程机制对线程间通信和线程管理的强大支持才能有用武之地,这时使 用多线程才是值得的。&&&
&&相关文章推荐
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:1247250次
积分:9103
积分:9103
排名:第1851名
原创:114篇
评论:508条
(1)(1)(5)(2)(1)(1)(3)(1)(1)(1)(6)(6)(1)(1)(3)(3)(3)(1)(1)(2)(1)(1)(1)(2)(2)(1)(2)(1)(1)(2)(3)(7)(6)(2)(7)(8)(13)(10)(9)

我要回帖

更多关于 王者荣耀收费吗 的文章

 

随机推荐