java多线程开发,Java多线程问题及处理(笔记)
java多线程开发,Java多线程问题及处理(笔记)详细介绍
本文目录一览: 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, number;public MyThread(int num){number = num;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, number;public MyThread(int num){number = num;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){//允许访问控制的代码}#p#副标题#e#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 多线程编程的方方面面,包括创建线程,以及对多个线程进行调度、管理。我们深刻认识到了多线程编程的复杂性,以及线程切换开销带来的多线程程序的低效性,这也促使我们认真地思考一个问题:我们是否需要多线程?何时需要多线程?多线程的核心在于多个代码块并发执行,本质特点在于各代码块之间的代码是乱序执行的。我们的程序是否需要多线程,就是要看这是否也是它的内在特点。假如我们的程序根本不要求多个代码块并发执行,那自然不需要使用多线程;假如我们的程序虽然要求多个代码块并发执行,但是却不要求乱序,则我们完全可以用一个循环来简单高效地实现,也不需要使用多线程;只有当它完全符合多线程的特点时,多线程机制对线程间通信和线程管理的强大支持才能有用武之地,这时使用多线程才是值得的。#p#副标题#e#
java如何实现多线程
JAVA中怎么处理高并发的情况
一、背景综述
并发就是可以使用多个线程或进程,同时处理(就是并发)不同的操作。
高并发的时候就是有很多用户在访问,导致系统数据不正确、糗事数据的现象。对于一些大型网站,比如门户网站,在面对大量用户访问、高并发请求方面,基本的解决方案集中在这样几个环节:使用高性能的服务器、高性能的数据库、高效率的编程语言、还有高性能的Web容器。这几个解决思路在一定程度上意味着更大的投入。
使用一般的synchronized或者是lock或者是队列都是无法满足高并发的问题。
二、解决方法有三:
1.使用缓存
2.使用生成静态页面
html纯静态页面是效率最高、消耗最小的页面。我们可以使用信息发布系统来实现简单的信息录入自动生成静态页面,频道管理、权限管理和自动抓取等功能,对于一个大型网站来说,拥有一套高效、可管理的信息发布系统CMS是必不可少的。
3.图片服务器分离
图片是最消耗资源的,僵图片和页面分离可以降低提供页面访问请求的服务器系统压力,并且可以保证系统不会因为图片问题而崩溃。
3.写代码的时候减少不必要的资源浪费:
不要频繁得使用new对象,对于在整个应用中只需要存在一个实例的类使用单例模式.对于String的连接操作,使用StringBuffer或者StringBuilder.对于utility类型的类通过静态方法来访问。
避免使用错误的方式,如Exception可以控制方法推出,但是Exception要保留stacktrace消耗性能,除非必要不要使用 instanceof做条件判断,尽量使用比的条件判断方式.使用JAVA中效率高的类,比如ArrayList比Vector性能好。)
使用线程安全的集合对象vector hashtable
使用线程池
java多线程编程中涉及的基础知识点?
线程设计在软件开发领域中是非常常见的一个设计构成,今天南邵北大青鸟就一起来了解一下,java多线程编程中都涉及到了哪些基础知识点。
顺序
用于表示多个操作“依次处理”。比如把十个操作交给一个人来处理时,这个人要一个一个地按顺序来处理
并行
用于标识多个操作“同时处理”。比如十个操作分给两个人处理时,这两个人就会并行来处理。
并发
相对于顺序和并行来说比较抽象,用于表示“将一个操作分割成多个部分并且允许无序处理”。比如将十个操作分成相对独立的两类,这样便能够开始并发处理了。如果一个人来处理,这个人就是顺序处理分开的并发操作,而如果是两个人,这两个人就可以并行处理同一个操作。
总结
多线程程序都是并发处理的。如果CPU只有一个,那么并发处理就是顺序执行的,而如果有多个CPU,那么并发处理就可能会并行运行。
等待队列
所有实例都拥有一个等待队列,它是在实例的wait方法执行后停止操作的线程队列。就好比为每个实例准备的线程休息室
在执行wait方法后,线程便会暂停操作,进入等待队列这个休息室。除非发生下列某一情况,否则线程会一直在等待队列中休眠。
有其他线程的notify方法来唤醒线程
有其他线程的notifyAll方法来唤醒线程
有其他线程的interrupt方法来唤醒线程
wait方法超时
notify方法
该方法会将等待队列中的一个线程去除。同wait方法一样,若要执行notify方法,线程也必须持有要调用的实例的锁。
notifyAll方法
notify方法仅唤醒一个线程,而notifyAll则唤醒所有线程,这是两者之间的区别
同wait方法和notify方法一样,notifyAll方法也只能由持有要调用的实例锁的线程调用
notify和notifyAll选择
notify方法和notifyAll方法非常相似,到底该使用哪个?
实际上,这很难选择,由于notify唤醒的线程较少,所以处理速度要比使用notifyAll时快。但使用notify时,如果处理不好,程序便可能会停止。一般来说,使用notifyAll时的代码要比使用notify时的更为健壮。
java面试为什么很喜欢问多线程高并发
多线程高并发是Java面试中经常被问到的一个重要话题。这是因为多线程和高并发是Java开发中的核心概念和常见问题,对于企业来说,能够掌握多线程和高并发的知识和技能是评判一个候选人是否具备优秀Java开发能力的重要标准。
首先,多线程和高并发是当今互联网应用开发中常见的挑战。随着互联网用户数量的不断增加,应用程序需要同时处理大量的请求和数据,并保持快速响应的同时保证数据的一致性和准确性。因此,对于企业来说,具备处理高并发的能力是非常重要的。
其次,多线程和高并发的问题涉及到了并发编程的核心概念和技术,包括线程安全、锁、同步、死锁等。在多线程环境下,如果不正确地处理并发访问共享资源的问题,就会出现数据不一致、线程安全问题等bug。因此,企业更倾向于招聘那些对多线程编程有深入理解和实践经验的候选人。
此外,多线程和高并发的问题还涉及到了性能优化和资源管理方面的考虑。合理地利用多线程可以提高系统的并发处理能力和响应速度,从而提升用户体验。在多线程环境下,合理管理资源、避免资源竞争和浪费也是一个重要的技能。
综上所述,Java面试喜欢问多线程高并发的原因是因为它是Java开发中的重要问题,并且涉及到并发编程的核心概念和技术,以及性能优化和资源管理方面的考虑。对于候选人来说,深入理解和掌握多线程高并发的知识和技能,可以提升自己的竞争力,并在实际工作中更好地处理并发问题。
在Java开发领域,多线程和高并发是非常重要的概念和技术。因此,在Java面试中经常会被问及多线程和高并发的问题。以下是对为什么面试中喜欢问多线程高并发的原因的解释:
1. 多线程和高并发是Java开发的核心概念:多线程和高并发是Java开发中必不可少的技术要素之一。在实际项目中,经常需要处理大量并发请求,因此,了解多线程和高并发的概念、原理和实践经验是Java开发人员必备的技能。
2. 多线程和高并发是性能优化的关键点:在处理大量并发请求时,有效地利用多线程和实现高并发是提升系统性能的关键。因此,面试官会关注面试者对性能优化的理解和实践经验,特别是在多线程和高并发场景下的应用能力。
3. 多线程和高并发问题具有挑战性:多线程和高并发问题涉及到线程安全、锁、同步、死锁等复杂的概念和技术。针对这些问题,面试官可以考察面试者对于并发编程的理解和解决问题的能力,以及对Java并发包(如java.util.concurrent)的熟悉程度。
拓展内容:
除了上述原因,多线程和高并发问题在面试中还能考察面试者的系统设计能力、并发算法的理解、线程池的使用等方面的知识。面试者应该了解如何设计线程安全的程序,如何使用锁和同步机制来保证数据的一致性和避免竞态条件。此外,掌握并发编程的一些常见模式和技巧,如线程池的使用、并发集合类的使用,也是面试过程中的加分项。
总结起来,多线程和高并发问题经常被问及是因为它们是Java开发中的重要概念,对于系统性能和稳定性至关重要。了解多线程和高并发的原理、技术和最佳实践,对于Java开发人员来说至关重要。
Java面试中常问关于多线程和高并发的问题,原因如下:
1. 多线程和高并发是Java开发中常见的问题:Java是一种广泛应用于并发编程的语言,多线程和高并发是Java开发中常遇到的挑战。因此,面试官经常会问相关问题,以了解面试者对于这方面的理解和实践经验。
2. 多线程和高并发涉及到核心的编程概念和技术:理解多线程和高并发需要掌握线程的基本概念、线程的生命周期、线程同步与互斥、锁机制、线程池等知识。这些是Java开发中非常重要的技术,对于能否编写高效、可靠的并发程序起着关键作用。
3. 多线程和高并发是性能优化的重要方向:在现代应用程序开发中,高并发是一个常见的需求。通过合理地设计和优化多线程和并发,可以提高系统的性能和响应速度。因此,对于面试者来说,理解和掌握多线程和高并发的技术,对于解决性能问题和提升系统效率具有重要意义。
拓展内容:
除了上述原因外,多线程和高并发在现代的计算机系统中也具有重要的意义。随着计算机硬件的发展,多核处理器已经成为普遍存在的情况,而多线程的使用可以更好地利用多核处理器的优势,提高系统的并行处理能力。而高并发则是现代互联网应用中普遍存在的情况,如高并发的请求处理、数据库并发访问、分布式系统的并发操作等。因此,对于Java开发者来说,熟练掌握多线程和高并发编程技术,将有助于提高自己的竞争力和应对现实开发中的挑战。
Java多线程问题及处理(笔记)
死锁
多线程编程在实际的网络程序开发中 在客户端程序实现中使用的比较简单 但是在服务器端程序实现中却不仅是大量使用 而且会出现比客户端更多的问题
另外一个容易在服务器端出现的多线程问题是——死锁 死锁指两个或两个以上的线程为了使用某个临界资源而无限制的等待下去 还是以前面卫生间的例子来说明死锁 例如两个人都同时到达卫生间 而且两个人都比较礼貌 第一个人和第二个人说 你先吧 第二个人和第一个人说 你先吧 这两个人就这样一直在互相礼让 谁也不进入 这种现象就是死锁 这里的两个人就好比是线程 而卫生间在这里就是临界资源 而由于这两个线程在一直谦让 谁也不使用临界资源
死锁不仅使程序无法达到预期实现的功能 而且浪费系统的资源 所以在服务器端程序中危害比较大 在实际的服务器端程序开发中 需要注意避免死锁
而死锁的检测比较麻烦 而且不一定每次都出现 这就需要在测试服务器端程序时 有足够的耐心 仔细观察程序执行时的性能检测 如果发现执行的性能显著降低 则很可能是发生了死锁 然后再具体的查找死锁出现的原因 并解决死锁的问题
死锁出现的最本质原因还是逻辑处理不够严谨 在考虑时不是很周全 所以一般需要修改程序逻辑才能够很好的解决死锁
线程优先级
在日常生活中 例如火车售票窗口等经常可以看到 XXX优先 那么多线程编程中每个线程是否也可以设置优先级呢?
在多线程编程中 支持为每个线程设置优先级 优先级高的线程在排队执行时会获得更多的CPU执行时间 得到更快的响应 在实际程序中 可以根据逻辑的需要 将需要得到及时处理的线程设置成较高的优先级 而把对时间要求不高的线程设置成比较低的优先级
在Thread类中 总计规定了三个优先级 分别为
l MAX_PRIORITY——最高优先级
l NORM_PRIORITY——普通优先级 也是默认优先级
l MIN_PRIORITY——最低优先级
在前面创建的线程对象中 由于没有设置线程的优先级 则线程默认的优先级是NORM_PRIORITY 在实际使用时 也可以根据需要使用Thread类中的setPriority方法设置线程的优先级 该方法的声明为
public final void setPriority(int newPriority)
假设t是一个初始化过的线程对象 需要设置t的优先级为最高 则实现的代码为
t setPriority(Thread MAX_PRIORITY);
这样 在该线程执行时将获得更多的执行机会 也就是优先执行 如果由于安全等原因 不允许设置线程的优先级 则会抛出SecurityException异常
下面使用一个简单的输出数字的线程演示线程优先级的使用 实现的示例代码如下
package priority;
/**
* 测试线程优先级
* author by ;
*/
public class TestPriority {
public static void main(String[] args) {
PrintNumberThread p = new PrintNumberThread( 高优先级 );
PrintNumberThread p = new PrintNumberThread( 普通优先级 );
PrintNumberThread p = new PrintNumberThread( 低优先级 );
p setPriority(Thread MAX_PRIORITY);
p setPriority(Thread NORM_PRIORITY);
p setPriority(Thread MIN_PRIORITY);
p start();
p start();
p start();
}
}
package priority;
/**
* 输出数字的线程
*/
public class PrintNumberThread extends Thread {
String name;
public PrintNumberThread(String name){
this name = name;
}
public void run(){
try{
for(int i = ;i < ;i++){
System out println(name + : + i);
}
}catch(Exception e){}
}
}
程序的一种执行结果为
高优先级
高优先级
高优先级
普通优先级
高优先级
普通优先级
高优先级
普通优先级
高优先级
高优先级
高优先级
高优先级
高优先级
普通优先级
普通优先级
普通优先级
普通优先级
普通优先级
普通优先级
普通优先级
低优先级
低优先级
低优先级
低优先级
低优先级
低优先级
低优先级
低优先级
低优先级
低优先级
在该示例程序 PrintNumberThread线程实现的功能是输出数字 每次数字输出之间没有设置时间延迟 在测试类TestPriority中创建三个PrintNumberThread类型的线程对象 然后分别设置线程优先级是最高 普通和最低 接着启动线程执行程序 从执行结果可以看出高优先级的线程获得了更多的执行时间 首先执行完成 而低优先级的线程由于优先级较低 所以最后一个执行结束
其实 对于线程优先级的管理主要由系统的线程调度实现 较高优先级的线程优先执行 所以可以通过设置线程的优先级影响线程的执行
总结
关于多线程的基础知识就介绍这么多 在本章中介绍了线程的概念 线程的实现方式以及使用多线程时会遇到的问题以及解决办法 而需要建立多线程的概念 也就是并发编程的概念还需要进行比较多的练习 理解多线程的概念并熟悉多线程的编程
lishixinzhi/Article/program/Java/gj/201311/27491
JAVA编程多线程
继承java.lang.Thread类。
package com.cchongda.threadtest;
public class ThreadTest extends Thread{
public int i =10;
public void run(){ System.out.println("当前线程是:"+Thread.currentThread()); System.out.println("现在i的值为:"+i); try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } i = 15; System.out.println("修改后的i值为:"+i+Thread.currentThread()); }
public static void main(String[] args) {
ThreadTest tt1 = new ThreadTest(); ThreadTest tt2 = new ThreadTest(); tt1.start(); tt2.start(); }
} 这样就能实现创建了两个线程 并启动这两个线程去执行run()方法了,。
//两种方式
public class ThreadDemo extends Thread{ public void run(){ System.out.println("线程"+Thread.currentThread().getName()); } public static void main(String[] args){ ThreadDemo t1 = new ThreadDemo();//创建线程 t1.start();//启动线程 //创建第二个线程 ThreadDemo t2 = new ThreadDemo();//创建线程 t2.start();//启动线程 }}
//方式二,实现Runnable接口
public class ThreadDemo implements Runnable{ public void run(){ System.out.println("线程"+Thread.currentThread().getName()); } public static void main(String[] args){ //创建线程实例 ThreadDemo td = new ThreadDemo() //创建线程1 Thread t1 = new Thread(td); t1.start(); //创建线程2 Thread t2 = new Thread(td); t2.start(); }}
java 多线程有几种实现方法
1、继承Thread类实现多线程
继承Thread类的方法尽管被我列为一种多线程实现方式,但Thread本质上也是实现了Runnable接口的一个实例,它代表一个线程的实例,并且,启动线程的唯一方法就是通过Thread类的start()实例方法。start()方法是一个native方法,它将启动一个新线程,并执行run()方法。这种方式实现多线程很简单,通过自己的类直接extend Thread,并复写run()方法,就可以启动新线程并执行自己定义的run()方法。例如:
[java] view plain copy
public class MyThread extends Thread {
public void run() {
System.out.println("MyThread.run()");
}
}
在合适的地方启动线程如下:
[java] view plain copy
MyThread myThread1 = new MyThread();
MyThread myThread2 = new MyThread();
myThread1.start();
myThread2.start();
2、实现Runnable接口方式实现多线程
如果自己的类已经extends另一个类,就无法直接extends Thread,此时,必须实现一个Runnable接口,如下:
[java] view plain copy
public class MyThread extends OtherClass implements Runnable {
public void run() {
System.out.println("MyThread.run()");
}
}
为了启动MyThread,需要首先实例化一个Thread,并传入自己的MyThread实例:
[java] view plain copy
MyThread myThread = new MyThread();
Thread thread = new Thread(myThread);
thread.start();
事实上,当传入一个Runnable target参数给Thread后,Thread的run()方法就会调用target.run(),参考JDK源代码:
[java] view plain copy
public void run() {
if (target != null) {
target.run();
}
}
3、使用ExecutorService、Callable、Future实现有返回结果的多线程
ExecutorService、Callable、Future这个对象实际上都是属于Executor框架中的功能类。想要详细了解Executor框架的可以访问http://www.javaeye.com/topic/366591 ,这里面对该框架做了很详细的解释。返回结果的线程是在JDK1.5中引入的新特征,确实很实用,有了这种特征我就不需要再为了得到返回值而大费周折了,而且即便实现了也可能漏洞百出。
可返回值的任务必须实现Callable接口,类似的,无返回值的任务必须Runnable接口。执行Callable任务后,可以获取一个Future的对象,在该对象上调用get就可以获取到Callable任务返回的Object了,再结合线程池接口ExecutorService就可以实现传说中有返回结果的多线程了。下面提供了一个完整的有返回结果的多线程测试例子,在JDK1.5下验证过没问题可以直接使用。
用Java实现多线程有哪些途径?
通常线程是在系统层被实现的。java是第一个在语言中实现的。java在语言级提供了对多线程设计的支持。线程:是进程中的一个单一的连续控制流程。一个进程可以拥有多个线程。多线程帮助你写出CPU最大利用率的高效程序。举例来说,网络的数据传送速率远远低于CPU处理能力,本地文件系统资源的读写速度也远远低于CPU的处理能力,在传统的单线程环境中,你的程序必须等待每一个这样的任务完成以后才能执行下一步--尽管CPU大部分时间处于空闲。而JAVA的多线程能使你充分利用这些空闲的时间。在一个单线程程序中如果出现阻塞则整个程序都可能停止运行,而在一个多线程的程序中这不会出现这样的问题。当一个线程阻塞时,别的线程会运行,这样可以大大的提高CPU效率。
第一种是继承Thread类
第二种是实现Runnable接口
但是为了实现多态性,推荐第二种方式
具体实现方法 见楼上!
1.
public class AAA extends Thread{
public void run(){
}
}
2. public class BBB implements Runnable{
public void run(){}
}
83794229
1,创建Thread类的子类
在这个途径中,用户程序需要创建自己的Thread类的子类,并在子类中重新定义自己的run()方法,这个run()方法中包含了用户线程的操作。这样在用户程序需要建立自己的线程时,它只需要创建一个已定义好的Thread子类的实例就可以了。
例:
public class TestThread{
...(中间的就不写了)
}
class Test1 extends Thread{
public void run(){
...............
}
}
2.实现Runnable接口
在这个途径中,已经有了一个父类的用户类可以通过实现Runnable接口的方法来定义用户线程的操作。Runnable接口只有一个方法run(),实现这个借口,就必须要定义run()方法的具体内容,用户新建线程的操作也就由这个方法来决定。定义好run()方法之后,当用户程序需要建立新线程时,只要以这个实现了run()方法的类对象为参数创建系统类Thread的对象,就可以把用户实现的run()方法借用过来。
public class TestRunnable implements Runnable{
Lable prompt1 = new Label("1");
Lable prompt2 = new Label("2");
.......
Thread thread1,thread2;
.......
}
public void init(){
add(.....);
.......
.......
}
public void start(){
thread1 = new Thread(........);//(我就写一个。)创建1,2个线程对象,其他的不写了
thread1.start();
}
public void run(){........}
}
高技术软件交流>
在Java 中多线程的实现方法有哪些,如何使用
1、 认识Thread和Runnable
Java中实现多线程有两种途径:继承Thread类或者实现Runnable接口。Runnable是接口,建议用接口的方式生成线程,因为接口可以实现多继承,况且Runnable只有一个run方法,很适合继承。在使用Thread的时候只需继承Thread,并且new一个实例出来,调用start()方法即可以启动一个线程。
Thread Test = new Thread();
Test.start();
在使用Runnable的时候需要先new一个实现Runnable的实例,之后启动Thread即可。
Test impelements Runnable;
Test t = new Test();
Thread test = new Thread(t);
test.start();
总结:Thread和Runnable是实现java多线程的2种方式,runable是接口,thread是类,建议使用runable实现java多线程,不管如何,最终都需要通过thread.start()来使线程处于可运行状态。
2、 认识Thread的start和run
1) start:
用start方法来启动线程,真正实现了多线程运行,这时无需等待run方法体代码执行完毕而直接继续执行下面的代码。通过调用Thread类的start()方法来启动一个线程,这时此线程处于就绪(可运行)状态,并没有运行,一旦得到spu时间片,就开始执行run()方法,这里方法run()称为线程体,它包含了要执行的这个线程的内容,Run方法运行结束,此线程随即终止。
2) run:
run()方法只是类的一个普通方法而已,如果直接调用Run方法,程序中依然只有主线程这一个线程,其程序执行路径还是只有一条,还是要顺序执行,还是要等待run方法体执行完毕后才可继续执行下面的代码,这样就没有达到写线程的目的。
总结:调用start方法方可启动线程,而run方法只是thread的一个普通方法调用,还是在主线程里执行。
3、 线程状态说明
线程状态从大的方面来说,可归结为:初始状态、可运行状态、不可运行状态和消亡状态,具体可细分为上图所示7个状态,说明如下:
1) 线程的实现有两种方式,一是继承Thread类,二是实现Runnable接口,但不管怎样,当我们new了thread实例后,线程就进入了初始状态;
2) 当该对象调用了start()方法,就进入可运行状态;
3) 进入可运行状态后,当该对象被操作系统选中,获得CPU时间片就会进入运行状态;
4) 进入运行状态后case就比较多,大致有如下情形:
·run()方法或main()方法结束后,线程就进入终止状态;
·当线程调用了自身的sleep()方法或其他线程的join()方法,就会进入阻塞状态(该状态既停止当前线程,但并不释放所占有的资源)。当sleep()结束或join()结束后,该线程进入可运行状态,继续等待OS分配时间片;
·当线程刚进入可运行状态(注意,还没运行),发现将要调用的资源被锁牢(synchroniza,lock),将会立即进入锁池状态,等待获取锁标记(这时的锁池里也许已经有了其他线程在等待获取锁标记,这时它们处于队列状态,既先到先得),一旦线程获得锁标记后,就转入可运行状态,等待OS分配CPU时间片;
·当线程调用wait()方法后会进入等待队列(进入这个状态会释放所占有的所有资源,与阻塞状态不同),进入这个状态后,是不能自动唤醒的,必须依靠其他线程调用notify()或notifyAll()方法才能被唤醒(由于notify()只是唤醒一个线程,但我们由不能确定具体唤醒的是哪一个线程,也许我们需要唤醒的线程不能够被唤醒,因此在实际使用时,一般都用notifyAll()方法,唤醒有所线程),线程被唤醒后会进入锁池,等待获取锁标记。
·当线程调用stop方法,即可使线程进入消亡状态,但是由于stop方法是不安全的,不鼓励使用,大家可以通过run方法里的条件变通实现线程的stop。
java 创建多线程
Java 多线程的同步依靠的是对象锁机制,这个问题需要我们不断的学习相关的问题。下面我们就来详细的学习下如何才能更好的进行具体内容的使用。synchronized关键字的背后就是利用了封锁来实现对共享资源的互斥访问。
下面以一个简单的实例来进行对比分析。实例要完成的工作非常简单,就是创建10个线程,每个线程都打印从0到99这100个数字,我们希望线程之间不会出现交叉乱序打印,而是顺序地打印。
先来看第一段代码,这里我们在run()方法中加入了synchronized关键字,希望能对run方法进行互斥访问,但结果并不如我们希望那样,这是因为这里synchronized锁住的是this对象,即当前运行线程对象本身。 Java 多线程代码中创建了10个线程,而每个线程都持有this对象的对象锁,这不能实现线程的同步。
Java多线程代码如下
1.package com.vista;
2.class MyThread implements java.lang.Runnable
3.{
4.private int threadId;
5.public MyThread(int id)
6.{
7.this.threadId = id;
8.}
9.@Override
10.public synchronized void run()
11.{
12.for (int i = 0; i < 100; ++i)
13.{
14.System.out.println("Thread ID: " + this.threadId + " : " + i);
15.}
16.}
17.}
18.public class ThreadDemo
19.{
20./**
21.* @param args
22.* @throws InterruptedException
23.*/
24.public static void main(String[] args) throws InterruptedException
25.{
26.for (int i = 0; i < 10; ++i)
27.{
28.new Thread(new MyThread(i)).start();
29.Thread.sleep(1);
30.}
31.}
32.}
以上就是对Java多线程的详细代码介绍。