【15-多线程】

多线程


进程与线程

 

•几乎所有的操作系统都支持同时运行多个任务,一个任务通常就是一个程序,每个运行中的程序就是

一个进程。当一个程序运行时,内部可能包含了多个顺序执行流,每个顺序执行流就是一个线程。

 

•几乎所有操作系统都支持进程的概念,所有运行中的任务通常对应一条进程(Process)。当一个程序进入内存运行,即变成一个进程。进程是处于运行过程中的程序,并且具有一定独立功能,进程是系统进行资源分配和调度的一个独立单位。

 

•一般而言,进程包含如下三个特征:

 

独立性:进程是系统中独立存在的实体,它可以拥有自己独立的资源,每一个进程都拥有自己私有的地址空间。在没有经过进程本身允许的情况下,一个用户进程不可以直接访问其他进程的地址空间。

 

动态性:进程与程序的区别在于:程序只是一个静态的指令集合,而进程是一个正在系统中活动的指令集合。在进程中加入了时间的概念。进程具有自己的生命周期和各种不同的状态,这些概念在程序中都是不具备的。

 

并发性:多个进程可以在单个处理器上并发执行,多个进程之间不会互相影响。 

 


多线程

 

•多线程则扩展了多进程的概念,使得同一个进行可以同时并发处理多个任务。线程(Thread)也被

称作轻量级进程(Ligheweight Process),线程是进程的执行单元。就象进程在操作系统中的地位

一样,线程在程序中是独立的、并发的执行流。当进程被初始化后,主线程就被创建了。对于绝大多数

的应用程序来说,通常仅要求有一个主线程,但我们也可以在该进程内创建多条顺序执行流,这些顺序

执行流就是线程,每条线程也是互相独立的。

 

•线程是进程的组成部分,一个进程可以拥有多个线程,一个线程必须有一个父进程。线程可以拥有自

己的堆栈、自己的程序计数器和自己的局部变量,但不再拥有系统资源,它与父进程的其它线程共享该

进程所拥有的全部资源。因为多个线程共享父进程里的全部资源,因此编程更加方便;但必须更加小

心,我们必须确保线程不会妨碍同一进程里的其它线程。

 

•线程可以完成一定的任务,可与其他线程共享父进程中的共享变量及部分环境、相互之间协同来完成

进程所要完成的任务。

 

•简而言之:一个程序运行后至少有一个进程,一个进程里可以包含多个线程,但至少包含一个线程。


 

 多线程编程的优势

 

 

•进程间不能共享内存,但线程之间共享内存非常容易。

•系统创建进程需要为该进程重新分配系统资源,但创建线程则代价小得多,因此使用多线程来实现多任务并发比多进程的效率高。

•Java语言内置的多线程功能支持,而不是单纯地作为底层操作系统的调度方式,从而简化了Java的多线程编程。


继承Thread类创建线程类 

 

 

•(1)定义Thread类的子类,并重写该类的run方法,该run方法的方法体就是代表了线程需要完成的任务。因此,我们经常把run方法称为线程执行体。

•(2)创建Thread子类的实例,即创建了线程对象。

•(3)调用线程对象的start方法来启动该线程。


实现Runnable接口创建线程类 

 

 

•(1)定义Runnable接口的实现类,并重写该接口的run方法,该run方法的方法体同样是该线程的线程执行体。

•(2)创建Runnable实现类的实例,并以此实例作为Thread的target来创建Thread对象,该Thread对象才是真正的线程对象。

•(3)调用线程对象的start方法来启动该线程。 


Callable接口

 

 

•Callable接口,该接口怎么看都像是Runnable接口的增强版,Callable接口也提供了一个call()方法可以作为线程执行体,但call方法比run()方法功能更强大:

–call()方法可以有返回值。

–call()可以声明抛出异常 


 

 Future接口

 

 

•JDK1.5提供了Future接口来代表Callable接口里call()方法的返回值,并为Future接口提供了一

个FutureTask实现类,该实现类实现了Future接口,并实现了Runnable接口——可以作为

Thread类的target。


 

 实现Callable创建多线程

 

 

•(1)定义Callable接口的实现类,并重写该接口的call方法,该call方法的方法体同样是该线程的线程执行体。

•(2)创建Callable实现类的实例,并将该实例包装成FutureTask,FutureTask实现了Runnable接口。

•(3)将FutureTask实例作为Thread的target来创建Thread对象,该Thread对象才是真正的线程对象。

•(4)调用线程对象的start方法来启动该线程。 


两种线程方式的对比

 

 

•采用实现Runnable接口方式的多线程:

–线程类只是实现了Runnable接口,还可以可以继承其他类。

–在这种方式下,可以多个线程共享同一个target对象,所以非常适合多个相同线程来处理同一份资源的情况,从而可以将CPU,代码和数据分开,形成清晰的模型,较好地体现了面向对象的思想。

–劣势是:编程稍稍复杂,如果需要访问当前线程,必须使用Thread.currentThread()方法。

•采用继承Thread类方式的多线程:

–劣势是:因为线程类已经继承了Thread类,所以不能再继承其他父类。

–优势是:编写简单,如果需要访问当前线程,无需使用Thread.currentThread()方法,直接使用this即可获得当前线程。 


 

 线程的生命周期 

 

 


线程死亡 

 

 

•线程会以以下三种方式之一结束,一个结束后就处于死亡状态:

•run()方法执行完成,线程正常结束。

•线程抛出一个未捕获的 Exception或Error。

•直接调用该线程的stop()方法来结束该线程——该方法容易导致死锁,通常不推荐使用。


 

 join线程 

 

 

•Thread提供了让一个线程等待另一个线程完成的方法:join() 方法。当在某个程序执行流中调用其

他线程的join()方法时,调用线程将被阻塞,直到被join方法加入的join线程完成为止。

 

•join()方法通常由使用线程的程序调用,以将大问题划分成许多小问题,每个小问题分配一个线程。

当所有的小问题都得到处理后,再调用主线程来进一步操作。


 

 后台线程 

 

 

•有一种线程,它是在后台运行的,它的任务是为其他的线程提供服务,这种线程被称为“后台线程(Daemon Thread)”,又称为“守护线程” 或“精灵线程”。JVM的垃圾回收线程就是典型的后台线程。

•后台线程有个特征:如果所有的前台线程都死亡,后台线程会自动死亡。

•调用Thread对象setDaemon(true)方法可将指定线程设置成后台线程。 


线程睡眠 

 

 

•如果我们需要让当前正在执行的线程暂停一段时间,并进入阻塞状态,则可以通过调用Thread类的静态sleep方法,sleep方法有两种重载的形式:

–static void sleep(long millis):让当前正在执行的线程暂停millis毫秒,并进入阻塞状态,该方法受到系统计时器和线程调度器的精度和准确度的影响。

–static void sleep(long millis, int nanos):让当前正在执行的线程暂停millis毫秒加nanos毫微妙,并进入阻塞状态,该方法受到系统计时器和线程调度器的精度和准确度的影响。


 

 线程让步 

 

 

•yield()方法是一个和sleep方法有点相似的方法,它也是一个Thread类提供的一个静态方法,它也

可以让当前正在执行的线程暂停,但它不会阻塞该线程。yield只是让当前线程暂停一下,让系统的线

程调度器重新调度一次,完全可能的情况是:当某个线程调用了yield方法暂停之后,线程调度器又将

其调度出来重新执行。

 

•实际上,当某个线程调用了yield方法暂停之后,只有优先级与当前线程相同,或者优先级比当前线

程更高的、就绪状态的线程才会获得执行的机会。 


 

 sleep方法和yield方法的区别

 

 

•sleep方法暂停当前线程后,会给其他线程执行机会,不会理会其他线程的优先级。但yield方法只会给优先级相同,或优先级更高的线程执行机会。

•sleep方法会将线程转入阻塞状态,直到经过阻塞时间才会转入就绪状态。而yield不会将线程转入阻塞状态,它只是强制当前线程进入就绪状态。因此完全有可能某个线程调用yield方法暂停之后,立即再次获得处理器资源被执行。

•sleep方法声明抛出了InterruptedException异常,所以调用sleep方法时要么捕捉该异常,要么显式声明抛出该异常。而yield方法则没有声明抛出任何异常。

•sleep方法比yield方法有更好的可移植性,通常不要依靠yield来控制并发线程的执行。 


 

 线程优先级 

 

 

•每个线程执行时都有具有一定的优先级,优先级高的线程获得较多的执行机会,而优先级低的线程则获得较少的执行机会。

•每个线程默认的优先级都与创建它的父线程具有相同的优先级,默认情况下,main线程的具有普通优先级,由main线程创建的子线程也有普通优先级。

•Thread提供了setPriority(int newPriority)和getPriority()方法来设置和返回指定线程的优先级,其中setPriority方法的参数可以是一个整数,范围是1~10之间,也可以使Thread类的三个静态常量:

–MAX_PRIORITY:其值是10。

–MIN_PRIORITY:其值是1。

–NORM_PRIORITY:其值是5。


 

 线程安全问题 

 

 

•多条线程并发修改共享资源就容易引发线程安全问题。

 


 

 同步代码块 

 

 

•Java的多线程支持引入了同步监视器来解决这个问题,使用同步监视器的通用方法就是同步代码块。

•synchronized后括号里的obj就是同步监视器,上面代码的含义是:线程开始执行同步代码块之前,必须先获得对同步监视器的锁定。


选择同步监视器

 

 

•选择监视器的目的:阻止两条线程对同一个共享资源进行并发访问。因此通常推荐使用可能被并发访问的共享资源充当同步监视器。对于上面的取钱模拟程序,我们应该考虑使用账户(account)作为同步监视器。 


同步方法 

 

 

•Java的多线程安全支持还提供了同步方法,同步方法就是使用synchronized关键字来修饰某个方

法,则该方法成为同步方法。对于同步方法而言,无需显式指定同步监视器,同步方法的同步监视器是

this,也就是该对象本身。 


 

 线程安全的类

 

 

•该类的对象可以被多个线程安全的访问。

•每个线程调用该对象的任意方法之后都将得到正确结果。

•每个线程调用该对象的任意方法之后,该对象状态依然保持合理状态。


线程安全的处理

 

 

•可变类的线程安全是以降低程序的运行效率作为代价的,为了减少线程安全所带来的负面影响,程序

可以采用如下策略:

 

–不要对线程安全类的所有方法都进行同步,只对那些会改变竞争资源(竞争资源也就是共享资

源)的方法进行同步。例如上面的Account类中accountNo属性就无需同步,所以程序只对

draw方法进行同步控制。

 

如果可变类有两种运行环境:单线程环境和多线程环境,则应该为该可变类提供两种版本:线程

不安全版本和线程安全版本。在单线程环境中使用线程不安全版本以保证性能,在多线程环境中使

用线程安全版本


 

 释放同步监视器 

 

 

•线程会在如下几种情况下释放对同步监视器的锁定:

–当前线程的同步方法、同步代码块执行结束,当前线程即释放同步监视器。

–当线程在同步代码块、同步方法中遇到break、return终止了该代码块、该方法的继续执行,当前线程将会释放同步监视器。

–当线程在同步代码块、同步方法中出现了未处理的Error或Exception,导致了该代码块、该方法异常结束时将会释放同步监视器。

–当线程执行同步代码块或同步方法时,程序执行了同步监视器对象的wait()方法,则当前线程暂停,并释放同步监视器。


 

 同步锁(Lock) 

 

 

•Lock是控制多个线程对共享资源进行访问的工具。通常,锁提供了对共享资源的独占访问,每次次

只能有一个线程对Lock对象加锁,线程开始访问共享资源之前应先获得Lock对象。不过,某些锁可能

允许对共享资源并发访问,如 ReadWriteLock(读写锁)。当然,在实现线程安全的控制中,通常

喜欢使用ReentrantLock(可重入锁)。使用该Lock对象可以显式地加锁、释放锁。

 

•ReentrantLock锁具有可重入性,也就是说线程可以对它已经加锁的ReentrantLock锁再次加

锁,ReentrantLock对象会维持一个计数器来追踪lock方法的嵌套调用,线程在每次调用lock()方

法加锁后,必须显式调用unlock()方法来释放锁,所以一段被锁保护的代码可以调用另一个被相同锁

保护的方法  


 

 死锁 

 

 

•当两个线程相互等待对方释放同步监视器时就会发生死锁,Java虚拟机没有监测、也没有采用措施

来处理死锁情况,所以多线程编程时应该采取措施避免死锁的出现。一旦出现死锁,整个程序既不会发

生任何异常,也不会给出任何提示,只是所有线程处于阻塞状态,无法继续。


 

 线程的协调运行 

 

 

•以借助于Object类提供的wait()、notify()和notifyAll()三个方法,这三个方法并不属于Thread

类,而是属于Object类。但这三个方法必须同步监视器对象调用。

 

•关于这三个方法的解释如下:

wait():导致当前线程等待,直到其他线程调用该同步监视器的notify()方法或notifyAll()方

法来唤醒该线程。该wait()方法有三种形式:无时间参数的wait(一直等待,直到其他线程通

知),带毫秒参数的wait和带毫秒、微秒参数的wait(这两种方法都是等待指定时间后自动苏

醒)。调用wait()方法的当前线程会释放对该同步监视器的锁定。

 

–notify():唤醒在此同步监视器上等待的单个线程。如果所有线程都在此同步监视器上等待,则

会选择唤醒其中一个线程。选择是任意性的。只有当前线程放弃对该同步监视器的锁定后(使用

wait()方法),才可以执行被唤醒的线程。

 

–notifyAll():唤醒在此同步监视器上等待的所有线程。只有当前线程放弃对该同步监视器的锁定

后,才可以执行被唤醒的线程。


 

 使用条件变量控制协调 

 

 

•当使用Lock对象来保证同步时,Java提供了一个Condition类来保持协调,使用Condition可以让

那些已经得到Lock对象、却无法继续执行的线程释放Lock对象,Condtion对象也可以唤醒其他处于

等待的线程。

 

•Condition 将同步监视锁方法(wait、notify 和 notifyAll)分解成截然不同的对象,以便通过将

这些对象与Lock对象组合使用,为每个对象提供多个等待集(wait-set)。在这种情况下,Lock 替

代了同步方法或同步代码块,Condition替代了同步监视锁的功能。

 

•Condition实例实质上被绑定在一个Lock对象上。要获得特定Lock实例的Condition实例,调用

Lock对象newCondition()方法即可。Condtion类提供了如下三个方法:

 

await():类似于隐式同步监视器上的wait()方法,导致当前线程等待,直到其他线程调用该

Condtion的signal ()方法或signalAll ()方法来唤醒该线程。该await方法有更多变体:long

awaitNanos(long nanosTimeout)、void awaitUninterruptibly()、awaitUntil(Date

deadline)等,可以完成更丰富的等待操作。

 

signal ():唤醒在此Lock对象上等待的单个线程。如果所有线程都在该Lock对象上等待,则会

选择唤醒其中一个线程。选择是任意性的。只有当前线程放弃对该Lock对象的锁定后(使用

await()方法),才可以执行被唤醒的线程。

 

signalAll():唤醒在此Lock对象上等待的所有线程。只有当前线程放弃对该该Lock对象的锁定

后,才可以执行被唤醒的线程。 


 

 使用管道流通信

 

 

•管道流有3种存在形式:PipedInputStream和PipedOutputStream,PipedReader和

PipedWriter以及Pipe.SinkChannel和Pipe.SourceChannel,它们分别是管道字节流、管道字

符流和新IO的管道Channel。

•使用管道流实现多线程通信可按如下步骤进行:

–(1)使用new操作符分别创建管道输入流和管道输出流。

–(2)使用管道输入流或管道输出流的connect方法两个输入流和输出流连接起来。

–(3)将管道输入流、管道输出流分别传入两个线程。

–(4)两个线程可以分别依赖各自的管道输入流、管道输出流进行通信。


 

 线程组

 

 

•Java使用ThreadGroup来表示线程组,它可以对一批线程进行分类管理,Java允许程序直接对线程组进行控制。

•一旦某个线程加入了指定线程组之后,该线程将一直属于该线程组,直到该线程死亡,线程运行中途不能改变它所属的线程组。

•Thread类提供了如下几个构造器来设置新创建的线程属于哪个线程组:

Thread(ThreadGroup group, Runnable target):以target的run方法作为线程执行体创建新线程,属于group线程组。

Thread(ThreadGroup group, Runnable target, String name):以target的run方法作为线程执行体创建新线程,该线程属于group线程组,且线程名为name。

Thread(ThreadGroup group, String name):创建新线程,新线程名为name,属于group线程组。 


 

 创建线程组

 

 

•Thread类没有提供setThreadGroup的方法来改变线程所属的线程组,但提供了一个

getThreadGroup()方法来返回该线程所属的线程组,getThreadGroup()方法的返回值是

ThreadGroup对象,表示一个线程组。ThreadGroup类有如下两个简单的构造器来创建实例:

 

–ThreadGroup(String name):以指定线程组名字来创建新的线程组。

 

–ThreadGroup(ThreadGroup parent, String name):以指定的名字、指定的父线程组创

建一个新线程组。


 

 线程组与未处理异常

 

 

•从JDK1.5开始,Java加强了线程的异常处理,如果线程执行过程中抛出了一个未处理的异常,JVM

在结束该线程之前会自动查找是否有对应的Thread.UncaughtExceptionHandler对象,如果找到

该处理器对象,将会调用该对象的uncaughtException(Thread t, Throwable e)方法来处理该异

常。

 

•Thread.UncaughtExceptionHandler是Thread类的一个内部公共静态接口,该接口内只有一

个方法:void uncaughtException(Thread t, Throwable e),该方法中的t代表出现异常的线

程,而e代表该线程抛出的异常。


 

 线程组处理异常的默认流程

 

 

•(1)如果该线程组有父线程组,则调用父线程组的uncaughtException方法来处理该异常。

•(2)如果该线程实例所属的线程类有默认的异常处理器(由setDefaultUncaughtExceptionHandler方法设置的异常处理器),那就调用该异常处理器来处理该异常。

•(3)如果该异常对象是ThreadDeath的对象,将不做任何处理;否则将异常跟踪栈的信息打印到System.err错误输出流,并结束该线程。 


 

 线程池

 

 

•系统启动一个新线程的成本是比较高的,因为它涉及到与操作系统交互。在这种情形下,使用线程池

可以很好地提高性能,尤其是当程序中需要创建大量生存期很短暂的线程时,更应该考虑使用线程池。

 

•与数据库连接池类似的是,线程池在系统启动时即创建大量空闲的线程,程序将一个Runnable对象

传给线程池,线程池就会启动一条线程来执行该对象的run方法,当run方法执行结束后,该线程并不

会死亡,而是再次返回线程池中成为空闲状态,等待执行下一个Runnable对象的run方法。


 

 JDK1.5的线程池支持

 

 

•JDK1.5提供了一个Executors工厂类来产生线程池,该工厂类里包含如下几个静态工厂方法来创建连接池:

–newCachedThreadPool():创建一个具有缓存功能的线程池,系统根据需要创建线程,这些线程将会被缓存在线程池中。

–newFixedThreadPool(int nThreads):创建一个可重用的、具有固定线程数的线程池。

–newSingleThreadExecutor():创建一个只有单线程的线程池,它相当于newFixedThreadPool方法时传入参数为1。

–newScheduledThreadPool(int corePoolSize):创建具有指定线程数的线程池,它可以在指定延迟后执行线程任务。corePoolSize指池中所保存的线程数,即使线程是空闲的也被保存在线程池内。

–newSingleThreadScheduledExecutor():创建只有一条线程的线程池,它可以在指定延迟后执行线程任务。


 

 ExecutorService

 

 

•ExecutorService代表尽快执行线程的线程池(只要线程池中有空闲线程立即执行线程任务),程

序只要将一个Runnable对象或Callable对象(代表线程任务)提交给该线程池即可,该线程池就会

尽快执行该任务。ExecutorService里提供了如下三个方法:

 

–Future<?> submit(Runnable task):将一个Runnable对象提交给指定的线程池。线程池将

在有空闲线程时执行Runnable对象代表的任务。其中Future对象代表Runnable任务的返回值——

但run方法没有返回值,所以Future对象将在run方法执行结束后返回null。但可以调用Future的

isDone()、isCancelled()方法来获得Runnable对象的执行状态。

 

–<T> Future<T> submit(Runnable task, T result):将一个Runnable对象提交给指定的线

程池。线程池将在有空闲线程时执行Runnable对象代表的任务,result显式指定线程执行结束后的

返回值。,所以Future对象将在run方法执行结束后返回result。

 

–<T> Future<T> submit(Callable<T> task):将一个Callable对象提交给指定的线程池。线

程池将在有空闲线程时执行Callable对象代表的任务,Future代表Callable对象里call方法的返回

值。


 

 ScheduledExecutorService

 

 

•ScheduledExecutorService代表可在指定延迟,或周期性执行线程任务的线程池,它提供了如下四个方法:

–ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit

unit):指定callable任务将在delay延迟后执行。

 

–ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit

unit):指定command任务将在delay延迟后执行。

 

–ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long

initialDelay, long period, TimeUnit unit):指定command任务将在delay延迟后执行,而

且以设定频率重复执行。也就是说,在initialDelay后开始执行,依次在 initialDelay+period

、initialDelay + 2 * period...处重复执行,依此类推。

 

–ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long

initialDelay, long delay, TimeUnit unit):创建并执行一个在给定初始延迟后首次启用的定

期操作,随后,在每一次执行终止和下一次执行开始之间都存在给定的延迟。如果任务的任一次执

行时遇到异常,就会取消后续执行。否则,只能通过程序来显式取消或终止来终止该任务。


 

 使用线程池的步骤

 

 

•(1)调用Executors类的静态工厂方法创建一个ExecutorService对象或ScheduledExecutorService对象,其中前者代表简单的线程池,后者代表能以任务调度方式执行线程的线程池。

•(2)创建Runnable实现类或Callable实现类的实例,作为线程执行任务。

•(3)调用ExecutorService对象的submit方法来提交Runnable实例或Callable实例;或调用ScheduledExecutorService的schedule来执行线程。

•(4)当不想提交任何任务时调用ExecutorService对象的shutdown方法来关闭线程池。


ThreadLocal类

 

 

•ThreadLocal,是Thread Local Variable(线程局部变量)的意思,也许将它命名为

ThreadLocalVar更加合适。线程局部变量(ThreadLocal)其实的功用非常简单,就是为每一个使

用该变量的线程都提供一个变量值的副本,使每一个线程都可以独立地改变自己的副本,而不会和其它

线程的副本冲突。从线程的角度看,就好像每一个线程都完全拥有该变量。

 

–ThreadLocal类的用法非常简单,它只提供了如下三个public方法:

–T get():返回此线程局部变量中当前线程副本中的值。

–void remove():删除此线程局部变量中当前线程的值。

–void set(T value):设置此线程局部变量中当前线程副本中的值。


 

 包装线程不安全的集合 

 

 

•如果程序有多条线程可能访问以上ArrayList、HashMap等集合,可以使用Collections提供的静态方法来把这些集合包装成线程安全的集合。Collections提供了如下几个静态方法:

 

–static <T> Collection<T> synchronizedCollection(Collection<T> c):返回指定 collection 对应的线程安全的collection。

 

–static <T> List<T> synchronizedList(List<T> list):返回指定List对应的线程安全的List对象。

 

–static <K,V> Map<K,V> synchronizedMap(Map<K,V> m):返回指定Map对象对应的线程安全的Map对象。

 

–static <T> Set<T> synchronizedSet(Set<T> s):返回指定Set对应的线程安全的Set。

 

–static <K,V> SortedMap<K,V> synchronizedSortedMap(SortedMap<K,V> m):返回指定SortedMap对象对应的线程安全的SortedMap对象。

 

–static <T> SortedSet<T> synchronizedSortedSet(SortedSet<T> s):返回指定SortedSet对象对应的线程安全的SortedSet对象。 


 

 线程安全的集合类

 

 

•java.util.concurrent包下提供了提供了ConcurrentHashMap、ConcurrentLinkedQueue两

个支持并发访问的集合,它们分别代表了支持并发访问的HashMap和支持并发访问的Queue。


 

 

public class ThirdThread
{
    public static void main(String[] args)
    {
        // 创建Callable对象
        ThirdThread rt = new ThirdThread();
        // 先使用Lambda表达式创建Callable<Integer>对象
        // 使用FutureTask来包装Callable对象
        FutureTask<Integer> task = new FutureTask<Integer>((Callable<Integer>)() -> {
            int i = 0;
            for ( ; i < 100 ; i++ )
            {
                System.out.println(Thread.currentThread().getName()
                    + " 的循环变量i的值:" + i);
            }
            // call()方法可以有返回值
            return i;
        });
        for (int i = 0 ; i < 100 ; i++)
        {
            System.out.println(Thread.currentThread().getName()
                + " 的循环变量i的值:" + i);
            if (i == 20)
            {
                // 实质还是以Callable对象来创建、并启动线程
                new Thread(task , "有返回值的线程").start();
            }
        }
        try
        {
            // 获取线程返回值
            System.out.println("子线程的返回值:" + task.get());
        }
        catch (Exception ex)
        {
            ex.printStackTrace();
        }
    }
}
View Code
// 通过实现Runnable接口来创建线程类
public class SecondThread implements Runnable
{
    private int i ;
    // run方法同样是线程执行体
    public void run()
    {
        for ( ; i < 100 ; i++ )
        {
            // 当线程类实现Runnable接口时,
            // 如果想获取当前线程,只能用Thread.currentThread()方法。
            System.out.println(Thread.currentThread().getName()
                + "  " + i);
        }
    }

    public static void main(String[] args)
    {
        for (int i = 0; i < 100;  i++)
        {
            System.out.println(Thread.currentThread().getName()
                + "  " + i);
            if (i == 20)
            {
                SecondThread st = new SecondThread();     //// 通过new Thread(target , name)方法创建新线程
                new Thread(st , "新线程1").start();
                new Thread(st , "新线程2").start();
            }
        }
    }
}
View Code
public class FirstThread extends Thread
{
    private int i ;
    // 重写run方法,run方法的方法体就是线程执行体
    public void run()
    {
        for ( ; i < 100 ; i++ )
        {
            // 当线程类继承Thread类时,直接使用this即可获取当前线程
            // Thread对象的getName()返回当前该线程的名字
            // 因此可以直接调用getName()方法返回当前线程的名
            System.out.println(getName() +  " " + i);
        }
    }
    public static void main(String[] args)
    {
        for (int i = 0; i < 100;  i++)
        {
            // 调用Thread的currentThread方法获取当前线程
            System.out.println(Thread.currentThread().getName()
                +  " " + i);
            if (i == 20)
            {
                // 创建、并启动第一条线程
                new FirstThread().start();
                // 创建、并启动第二条线程
                new FirstThread().start();
            }
        }
    }
}
View Code

 

 

 

    public class InvokeRun extends Thread {
        private int i;
    
        // 重写run方法,run方法的方法体就是线程执行体
        public void run() {
            for (; i < 100; i++) {
                // 直接调用run方法时,Thread的this.getName返回的是该对象名字,
                // 而不是当前线程的名字。
                // 使用Thread.currentThread().getName()总是获取当前线程名字
                System.out.println(Thread.currentThread().getName() + " " + i); //
            }
        }
    
        public static void main(String[] args) {
            for (int i = 0; i < 100; i++) {
                // 调用Thread的currentThread方法获取当前线程
                System.out.println(Thread.currentThread().getName() + " " + i);
                if (i == 20) {
                    // 直接调用线程对象的run方法,
                    // 系统会把线程对象当成普通对象,run方法当成普通方法,
                    // 所以下面两行代码并不会启动两条线程,而是依次执行两个run方法
                    new InvokeRun().run();
                    new InvokeRun().run();
                }
            }
        }
    }
View Code
public class StartDead extends Thread {
    private int i;

    // 重写run方法,run方法的方法体就是线程执行体
    public void run() {
        for (; i < 100; i++) {
            System.out.println(getName() + " " + i);
        }
    }

    public static void main(String[] args) {
        // 创建线程对象
        StartDead sd = new StartDead();
        for (int i = 0; i < 300; i++) {
            // 调用Thread的currentThread方法获取当前线程
            System.out.println(Thread.currentThread().getName() + " " + i);
            if (i == 20) {
                // 启动线程
                sd.start();
                // 判断启动后线程的isAlive()值,输出true
                System.out.println(sd.isAlive());
            }
            // 只有当线程处于新建、死亡两种状态时isAlive()方法返回false。
            // 当i > 20,则该线程肯定已经启动过了,如果sd.isAlive()为假时,
            // 那只能是死亡状态了。
            if (i > 20 && !sd.isAlive())

            {
                // 试图再次启动该线程
                sd.start();
            }
        }
    }
}
View Code

public class DaemonThread extends Thread {
    // 定义后台线程的线程执行体与普通线程没有任何区别
    public void run() {
        for (int i = 0; i < 1000; i++) {
            System.out.println(getName() + "  " + i);
        }
    }

    public static void main(String[] args) {
        DaemonThread t = new DaemonThread();
        // 将此线程设置成后台线程
        t.setDaemon(true);
        // 启动后台线程
        t.start();
        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName() + "  " + i);
        }
        // -----程序执行到此处,前台线程(main线程)结束------
        // 后台线程也应该随之结束
    }
}
View Code
public class JoinThread extends Thread {
    // 提供一个有参数的构造器,用于设置该线程的名字
    public JoinThread(String name) {
        super(name);
    }

    // 重写run()方法,定义线程执行体
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println(getName() + "  " + i);
        }
    }

    public static void main(String[] args) throws Exception {
        // 启动子线程
        new JoinThread("新线程").start();
        for (int i = 0; i < 100; i++) {
            if (i == 20) {
                JoinThread jt = new JoinThread("被Join的线程");
                jt.start();
                // main线程调用了jt线程的join()方法,main线程
                // 必须等jt执行结束才会向下执行
                jt.join();
            }
            System.out.println(Thread.currentThread().getName() + "  " + i);
        }
    }
}
View Code
public class PriorityTest extends Thread {
    // 定义一个有参数的构造器,用于创建线程时指定name
    public PriorityTest(String name) {
        super(name);
    }

    public void run() {
        for (int i = 0; i < 50; i++) {
            System.out.println(getName() + ",其优先级是:" + getPriority()
                    + ",循环变量的值为:" + i);
        }
    }

    public static void main(String[] args) {
        // 改变主线程的优先级
        Thread.currentThread().setPriority(6);
        for (int i = 0; i < 30; i++) {
            if (i == 10) {
                PriorityTest low = new PriorityTest("低级");
                low.start();
                System.out.println("创建之初的优先级:" + low.getPriority());
                // 设置该线程为最低优先级
                low.setPriority(Thread.MIN_PRIORITY);
            }
            if (i == 20) {
                PriorityTest high = new PriorityTest("高级");
                high.start();
                System.out.println("创建之初的优先级:" + high.getPriority());
                // 设置该线程为最高优先级
                high.setPriority(Thread.MAX_PRIORITY);
            }
        }
    }
}
View Code
public class SleepTest {
    public static void main(String[] args) throws Exception {
        for (int i = 0; i < 10; i++) {
            System.out.println("当前时间: " + new Date());
            // 调用sleep方法让当前线程暂停1s。
            Thread.sleep(1000);
        }
    }
}
View Code
public class YieldTest extends Thread {
    public YieldTest(String name) {
        super(name);
    }

    // 定义run方法作为线程执行体
    public void run() {
        for (int i = 0; i < 50; i++) {
            System.out.println(getName() + "  " + i);
            // 当i等于20时,使用yield方法让当前线程让步
            if (i == 20) {
                Thread.yield();
            }
        }
    }

    public static void main(String[] args) throws Exception {
        // 启动两条并发线程
        YieldTest yt1 = new YieldTest("高级");
        // 将ty1线程设置成最高优先级
        yt1.setPriority(Thread.MAX_PRIORITY);
        yt1.start();
        YieldTest yt2 = new YieldTest("低级");
        // 将yt2线程设置成最低优先级
        yt2.setPriority(Thread.MIN_PRIORITY);
        yt2.start();
    }
}
View Code

public class Account {
    // 封装账户编号、账户余额两个成员变量
    private String accountNo;
    private double balance;

    public Account() {
    }

    // 构造器
    public Account(String accountNo, double balance) {
        this.accountNo = accountNo;
        this.balance = balance;
    }

    // accountNo的setter和getter方法
    public void setAccountNo(String accountNo) {
        this.accountNo = accountNo;
    }

    public String getAccountNo() {
        return this.accountNo;
    }

    // 因此账户余额不允许随便修改,所以只为balance提供getter方法,
    public double getBalance() {
        return this.balance;
    }

    // 提供一个线程安全draw()方法来完成取钱操作
    public synchronized void draw(double drawAmount) {
        // 账户余额大于取钱数目
        if (balance >= drawAmount) {
            // 吐出钞票
            System.out.println(Thread.currentThread().getName() + "取钱成功!吐出钞票:"
                    + drawAmount);
            try {
                Thread.sleep(1);
            } catch (InterruptedException ex) {
                ex.printStackTrace();
            }
            // 修改余额
            balance -= drawAmount;
            System.out.println("\t余额为: " + balance);
        } else {
            System.out.println(Thread.currentThread().getName() + "取钱失败!余额不足!");
        }
    }

    // 下面两个方法根据accountNo来重写hashCode()和equals()方法
    public int hashCode() {
        return accountNo.hashCode();
    }

    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj != null && obj.getClass() == Account.class) {
            Account target = (Account) obj;
            return target.getAccountNo().equals(accountNo);
        }
        return false;
    }
}
View Code
public class DrawTest {
    public static void main(String[] args) {
        // 创建一个账户
        Account acct = new Account("1234567", 1000);
        // 模拟两个线程对同一个账户取钱
        new DrawThread("甲", acct, 800).start();
        new DrawThread("乙", acct, 800).start();
    }
}
View Code
public class DrawThread extends Thread {
    // 模拟用户账户
    private Account account;
    // 当前取钱线程所希望取的钱数
    private double drawAmount;

    public DrawThread(String name, Account account, double drawAmount) {
        super(name);
        this.account = account;
        this.drawAmount = drawAmount;
    }

    // 当多条线程修改同一个共享数据时,将涉及数据安全问题。
    public void run() {
        // 直接调用account对象的draw方法来执行取钱
        // 同步方法的同步监视器是this,this代表调用draw()方法的对象。
        // 也就是说:线程进入draw()方法之前,必须先对account对象的加锁。
        account.draw(drawAmount);
    }
}
View Code

 

 

public class Account {
    // 封装账户编号、账户余额的两个成员变量
    private String accountNo;
    private double balance;

    public Account() {
    }

    // 构造器
    public Account(String accountNo, double balance) {
        this.accountNo = accountNo;
        this.balance = balance;
    }

    // 此处省略了accountNo和balance的setter和getter方法

    // accountNo的setter和getter方法
    public void setAccountNo(String accountNo) {
        this.accountNo = accountNo;
    }

    public String getAccountNo() {
        return this.accountNo;
    }

    // balance的setter和getter方法
    public void setBalance(double balance) {
        this.balance = balance;
    }

    public double getBalance() {
        return this.balance;
    }

    // 下面两个方法根据accountNo来重写hashCode()和equals()方法
    public int hashCode() {
        return accountNo.hashCode();
    }

    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj != null && obj.getClass() == Account.class) {
            Account target = (Account) obj;
            return target.getAccountNo().equals(accountNo);
        }
        return false;
    }
}
View Code
public class DrawTest {
    public static void main(String[] args) {
        // 创建一个账户
        Account acct = new Account("1234567", 1000);
        // 模拟两个线程对同一个账户取钱
        new DrawThread("甲", acct, 800).start();
        new DrawThread("乙", acct, 800).start();
    }
}
View Code
public class DrawThread extends Thread {
    // 模拟用户账户
    private Account account;
    // 当前取钱线程所希望取的钱数
    private double drawAmount;

    public DrawThread(String name, Account account, double drawAmount) {
        super(name);
        this.account = account;
        this.drawAmount = drawAmount;
    }

    // 当多条线程修改同一个共享数据时,将涉及数据安全问题。
    public void run() {
        // 使用account作为同步监视器,任何线程进入下面同步代码块之前,
        // 必须先获得对account账户的锁定——其他线程无法获得锁,也就无法修改它
        // 这种做法符合:“加锁 → 修改 → 释放锁”的逻辑
        synchronized (account) {
            // 账户余额大于取钱数目
            if (account.getBalance() >= drawAmount) {
                // 吐出钞票
                System.out.println(getName() + "取钱成功!吐出钞票:" + drawAmount);
                try {
                    Thread.sleep(1);
                } catch (InterruptedException ex) {
                    ex.printStackTrace();
                }
                // 修改余额
                account.setBalance(account.getBalance() - drawAmount);
                System.out.println("\t余额为: " + account.getBalance());
            } else {
                System.out.println(getName() + "取钱失败!余额不足!");
            }
        }
        // 同步代码块结束,该线程释放同步锁
    }
}
View Code

public class Account {
    // 定义锁对象
    private final ReentrantLock lock = new ReentrantLock();
    // 封装账户编号、账户余额的两个成员变量
    private String accountNo;
    private double balance;

    public Account() {
    }

    // 构造器
    public Account(String accountNo, double balance) {
        this.accountNo = accountNo;
        this.balance = balance;
    }

    // accountNo的setter和getter方法
    public void setAccountNo(String accountNo) {
        this.accountNo = accountNo;
    }

    public String getAccountNo() {
        return this.accountNo;
    }

    // 因此账户余额不允许随便修改,所以只为balance提供getter方法,
    public double getBalance() {
        return this.balance;
    }

    // 提供一个线程安全draw()方法来完成取钱操作
    public void draw(double drawAmount) {
        // 加锁
        lock.lock();
        try {
            // 账户余额大于取钱数目
            if (balance >= drawAmount) {
                // 吐出钞票
                System.out.println(Thread.currentThread().getName()
                        + "取钱成功!吐出钞票:" + drawAmount);
                try {
                    Thread.sleep(1);
                } catch (InterruptedException ex) {
                    ex.printStackTrace();
                }
                // 修改余额
                balance -= drawAmount;
                System.out.println("\t余额为: " + balance);
            } else {
                System.out.println(Thread.currentThread().getName()
                        + "取钱失败!余额不足!");
            }
        } finally {
            // 修改完成,释放锁
            lock.unlock();
        }
    }

    // 下面两个方法根据accountNo来重写hashCode()和equals()方法
    public int hashCode() {
        return accountNo.hashCode();
    }

    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj != null && obj.getClass() == Account.class) {
            Account target = (Account) obj;
            return target.getAccountNo().equals(accountNo);
        }
        return false;
    }
}
View Code
public class DrawTest {
    public static void main(String[] args) {
        // 创建一个账户
        Account acct = new Account("1234567", 1000);
        // 模拟两个线程对同一个账户取钱
        new DrawThread("甲", acct, 800).start();
        new DrawThread("乙", acct, 800).start();
    }
}
View Code
public class DrawThread extends Thread {
    // 模拟用户账户
    private Account account;
    // 当前取钱线程所希望取的钱数
    private double drawAmount;

    public DrawThread(String name, Account account, double drawAmount) {
        super(name);
        this.account = account;
        this.drawAmount = drawAmount;
    }

    // 当多条线程修改同一个共享数据时,将涉及数据安全问题。
    public void run() {
        // 直接调用account对象的draw方法来执行取钱
        // 同步方法的同步监视器是this,this代表调用draw()方法的对象。
        // 也就是说:线程进入draw()方法之前,必须先对account对象的加锁。
        account.draw(drawAmount);
    }
}
View Code

public class Account {
    // 封装账户编号、账户余额的两个成员变量
    private String accountNo;
    private double balance;

    public Account() {
    }

    // 构造器
    public Account(String accountNo, double balance) {
        this.accountNo = accountNo;
        this.balance = balance;
    }

    // 此处省略了accountNo和balance的setter和getter方法

    // accountNo的setter和getter方法
    public void setAccountNo(String accountNo) {
        this.accountNo = accountNo;
    }

    public String getAccountNo() {
        return this.accountNo;
    }

    // balance的setter和getter方法
    public void setBalance(double balance) {
        this.balance = balance;
    }

    public double getBalance() {
        return this.balance;
    }

    // 下面两个方法根据accountNo来重写hashCode()和equals()方法
    public int hashCode() {
        return accountNo.hashCode();
    }

    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj != null && obj.getClass() == Account.class) {
            Account target = (Account) obj;
            return target.getAccountNo().equals(accountNo);
        }
        return false;
    }
}
View Code
class A {
    public synchronized void foo(B b) {
        System.out.println("当前线程名: " + Thread.currentThread().getName()
                + " 进入了A实例的foo()方法"); //
        try {
            Thread.sleep(200);
        } catch (InterruptedException ex) {
            ex.printStackTrace();
        }
        System.out.println("当前线程名: " + Thread.currentThread().getName()
                + " 企图调用B实例的last()方法"); //
        b.last();
    }

    public synchronized void last() {
        System.out.println("进入了A类的last()方法内部");
    }
}

class B {
    public synchronized void bar(A a) {
        System.out.println("当前线程名: " + Thread.currentThread().getName()
                + " 进入了B实例的bar()方法"); //
        try {
            Thread.sleep(200);
        } catch (InterruptedException ex) {
            ex.printStackTrace();
        }
        System.out.println("当前线程名: " + Thread.currentThread().getName()
                + " 企图调用A实例的last()方法"); //
        a.last();
    }

    public synchronized void last() {
        System.out.println("进入了B类的last()方法内部");
    }
}

public class DeadLock implements Runnable {
    A a = new A();
    B b = new B();

    public void init() {
        Thread.currentThread().setName("主线程");
        // 调用a对象的foo方法
        a.foo(b);
        System.out.println("进入了主线程之后");
    }

    public void run() {
        Thread.currentThread().setName("副线程");
        // 调用b对象的bar方法
        b.bar(a);
        System.out.println("进入了副线程之后");
    }

    public static void main(String[] args) {
        DeadLock dl = new DeadLock();
        // 以dl为target启动新线程
        new Thread(dl).start();
        // 调用init()方法
        dl.init();
    }
}
View Code
public class DrawTest {
    public static void main(String[] args) {
        // 创建一个账户
        Account acct = new Account("1234567", 1000);
        // 模拟两个线程对同一个账户取钱
        new DrawThread("甲", acct, 800).start();
        new DrawThread("乙", acct, 800).start();
    }
}
View Code
public class DrawThread extends Thread {
    // 模拟用户账户
    private Account account;
    // 当前取钱线程所希望取的钱数
    private double drawAmount;

    public DrawThread(String name, Account account, double drawAmount) {
        super(name);
        this.account = account;
        this.drawAmount = drawAmount;
    }

    // 当多条线程修改同一个共享数据时,将涉及数据安全问题。
    public void run() {
        // 账户余额大于取钱数目
        if (account.getBalance() >= drawAmount) {
            // 吐出钞票
            System.out.println(getName() + "取钱成功!吐出钞票:" + drawAmount);
            try {
                Thread.sleep(1);
            } catch (InterruptedException ex) {
                ex.printStackTrace();
            }
            // 修改余额
            account.setBalance(account.getBalance() - drawAmount);
            System.out.println("\t余额为: " + account.getBalance());
        } else {
            System.out.println(getName() + "取钱失败!余额不足!");
        }
    }
}
View Code

public class Account {
    // 显式定义Lock对象
    private final Lock lock = new ReentrantLock();
    // 获得指定Lock对象对应的Condition
    private final Condition cond = lock.newCondition();
    // 封装账户编号、账户余额的两个成员变量
    private String accountNo;
    private double balance;
    // 标识账户中是否已有存款的旗标
    private boolean flag = false;

    public Account() {
    }

    // 构造器
    public Account(String accountNo, double balance) {
        this.accountNo = accountNo;
        this.balance = balance;
    }

    // accountNo的setter和getter方法
    public void setAccountNo(String accountNo) {
        this.accountNo = accountNo;
    }

    public String getAccountNo() {
        return this.accountNo;
    }

    // 因此账户余额不允许随便修改,所以只为balance提供getter方法,
    public double getBalance() {
        return this.balance;
    }

    public void draw(double drawAmount) {
        // 加锁
        lock.lock();
        try {
            // 如果flag为假,表明账户中还没有人存钱进去,取钱方法阻塞
            if (!flag) {
                cond.await();
            } else {
                // 执行取钱
                System.out.println(Thread.currentThread().getName() + " 取钱:"
                        + drawAmount);
                balance -= drawAmount;
                System.out.println("账户余额为:" + balance);
                // 将标识账户是否已有存款的旗标设为false。
                flag = false;
                // 唤醒其他线程
                cond.signalAll();
            }
        } catch (InterruptedException ex) {
            ex.printStackTrace();
        }
        // 使用finally块来释放锁
        finally {
            lock.unlock();
        }
    }

    public void deposit(double depositAmount) {
        lock.lock();
        try {
            // 如果flag为真,表明账户中已有人存钱进去,则存钱方法阻塞
            if (flag) //
            {
                cond.await();
            } else {
                // 执行存款
                System.out.println(Thread.currentThread().getName() + " 存款:"
                        + depositAmount);
                balance += depositAmount;
                System.out.println("账户余额为:" + balance);
                // 将表示账户是否已有存款的旗标设为true
                flag = true;
                // 唤醒其他线程
                cond.signalAll();
            }
        } catch (InterruptedException ex) {
            ex.printStackTrace();
        }
        // 使用finally块来释放锁
        finally {
            lock.unlock();
        }
    }

    // 下面两个方法根据accountNo来重写hashCode()和equals()方法
    public int hashCode() {
        return accountNo.hashCode();
    }

    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj != null && obj.getClass() == Account.class) {
            Account target = (Account) obj;
            return target.getAccountNo().equals(accountNo);
        }
        return false;
    }
}
View Code
public class DepositThread extends Thread {
    // 模拟用户账户
    private Account account;
    // 当前取钱线程所希望存款的钱数
    private double depositAmount;

    public DepositThread(String name, Account account, double depositAmount) {
        super(name);
        this.account = account;
        this.depositAmount = depositAmount;
    }

    // 重复100次执行存款操作
    public void run() {
        for (int i = 0; i < 100; i++) {
            account.deposit(depositAmount);
        }
    }
}
View Code
public class DrawTest {
    public static void main(String[] args) {
        // 创建一个账户
        Account acct = new Account("1234567", 0);
        new DrawThread("取钱者", acct, 800).start();
        new DepositThread("存款者甲", acct, 800).start();
        new DepositThread("存款者乙", acct, 800).start();
        new DepositThread("存款者丙", acct, 800).start();
    }
}
View Code
public class DrawThread extends Thread {
    // 模拟用户账户
    private Account account;
    // 当前取钱线程所希望取的钱数
    private double drawAmount;

    public DrawThread(String name, Account account, double drawAmount) {
        super(name);
        this.account = account;
        this.drawAmount = drawAmount;
    }

    // 重复100次执行取钱操作
    public void run() {
        for (int i = 0; i < 100; i++) {
            account.draw(drawAmount);
        }
    }
}
View Code

public class Account {
    // 封装账户编号、账户余额的两个成员变量
    private String accountNo;
    private double balance;
    // 标识账户中是否已有存款的旗标
    private boolean flag = false;

    public Account() {
    }

    // 构造器
    public Account(String accountNo, double balance) {
        this.accountNo = accountNo;
        this.balance = balance;
    }

    // accountNo的setter和getter方法
    public void setAccountNo(String accountNo) {
        this.accountNo = accountNo;
    }

    public String getAccountNo() {
        return this.accountNo;
    }

    // 因此账户余额不允许随便修改,所以只为balance提供getter方法,
    public double getBalance() {
        return this.balance;
    }

    public synchronized void draw(double drawAmount) {
        try {
            // 如果flag为假,表明账户中还没有人存钱进去,取钱方法阻塞
            if (!flag) {
                wait();
            } else {
                // 执行取钱
                System.out.println(Thread.currentThread().getName() + " 取钱:"
                        + drawAmount);
                balance -= drawAmount;
                System.out.println("账户余额为:" + balance);
                // 将标识账户是否已有存款的旗标设为false。
                flag = false;
                // 唤醒其他线程
                notifyAll();
            }
        } catch (InterruptedException ex) {
            ex.printStackTrace();
        }
    }

    public synchronized void deposit(double depositAmount) {
        try {
            // 如果flag为真,表明账户中已有人存钱进去,则存钱方法阻塞
            if (flag) //
            {
                wait();
            } else {
                // 执行存款
                System.out.println(Thread.currentThread().getName() + " 存款:"
                        + depositAmount);
                balance += depositAmount;
                System.out.println("账户余额为:" + balance);
                // 将表示账户是否已有存款的旗标设为true
                flag = true;
                // 唤醒其他线程
                notifyAll();
            }
        } catch (InterruptedException ex) {
            ex.printStackTrace();
        }
    }

    // 下面两个方法根据accountNo来重写hashCode()和equals()方法
    public int hashCode() {
        return accountNo.hashCode();
    }

    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj != null && obj.getClass() == Account.class) {
            Account target = (Account) obj;
            return target.getAccountNo().equals(accountNo);
        }
        return false;
    }
}
View Code
public class DepositThread extends Thread {
    // 模拟用户账户
    private Account account;
    // 当前取钱线程所希望存款的钱数
    private double depositAmount;

    public DepositThread(String name, Account account, double depositAmount) {
        super(name);
        this.account = account;
        this.depositAmount = depositAmount;
    }

    // 重复100次执行存款操作
    public void run() {
        for (int i = 0; i < 100; i++) {
            account.deposit(depositAmount);
        }
    }
}
View Code
public class DrawTest {
    public static void main(String[] args) {
        // 创建一个账户
        Account acct = new Account("1234567", 0);
        new DrawThread("取钱者", acct, 800).start();
        new DepositThread("存款者甲", acct, 800).start();
        new DepositThread("存款者乙", acct, 800).start();
        new DepositThread("存款者丙", acct, 800).start();
    }
}
View Code
public class DrawThread extends Thread {
    // 模拟用户账户
    private Account account;
    // 当前取钱线程所希望取的钱数
    private double drawAmount;

    public DrawThread(String name, Account account, double drawAmount) {
        super(name);
        this.account = account;
        this.drawAmount = drawAmount;
    }

    // 重复100次执行取钱操作
    public void run() {
        for (int i = 0; i < 100; i++) {
            account.draw(drawAmount);
        }
    }
}
View Code

public class BlockingQueueTest {
    public static void main(String[] args) throws Exception {
        // 定义一个长度为2的阻塞队列
        BlockingQueue<String> bq = new ArrayBlockingQueue<>(2);
        bq.put("Java"); // 与bq.add("Java"、bq.offer("Java")相同
        bq.put("Java"); // 与bq.add("Java"、bq.offer("Java")相同
        bq.put("Java"); // ① 阻塞线程。
    }
}
View Code
class Producer extends Thread {
    private BlockingQueue<String> bq;

    public Producer(BlockingQueue<String> bq) {
        this.bq = bq;
    }

    public void run() {
        String[] strArr = new String[] { "Java", "Struts", "Spring" };
        for (int i = 0; i < 999999999; i++) {
            System.out.println(getName() + "生产者准备生产集合元素!");
            try {
                Thread.sleep(200);
                // 尝试放入元素,如果队列已满,线程被阻塞
                bq.put(strArr[i % 3]);
            } catch (Exception ex) {
                ex.printStackTrace();
            }
            System.out.println(getName() + "生产完成:" + bq);
        }
    }
}

class Consumer extends Thread {
    private BlockingQueue<String> bq;

    public Consumer(BlockingQueue<String> bq) {
        this.bq = bq;
    }

    public void run() {
        while (true) {
            System.out.println(getName() + "消费者准备消费集合元素!");
            try {
                Thread.sleep(200);
                // 尝试取出元素,如果队列已空,线程被阻塞
                bq.take();
            } catch (Exception ex) {
                ex.printStackTrace();
            }
            System.out.println(getName() + "消费完成:" + bq);
        }
    }
}

public class BlockingQueueTest2 {
    public static void main(String[] args) {
        // 创建一个容量为1的BlockingQueue
        BlockingQueue<String> bq = new ArrayBlockingQueue<>(1);
        // 启动3条生产者线程
        new Producer(bq).start();
        new Producer(bq).start();
        new Producer(bq).start();
        // 启动一条消费者线程
        new Consumer(bq).start();
    }
}
View Code

// 定义自己的异常处理器
class MyExHandler implements Thread.UncaughtExceptionHandler {
    // 实现uncaughtException方法,该方法将处理线程的未处理异常
    public void uncaughtException(Thread t, Throwable e) {
        System.out.println(t + " 线程出现了异常:" + e);
    }
}

public class ExHandler {
    public static void main(String[] args) {
        // 设置主线程的异常处理器
        Thread.currentThread().setUncaughtExceptionHandler(new MyExHandler());
        int a = 5 / 0; //
        System.out.println("程序正常结束!");
    }
}
View Code
class MyThread extends Thread {
    // 提供指定线程名的构造器
    public MyThread(String name) {
        super(name);
    }

    // 提供指定线程名、线程组的构造器
    public MyThread(ThreadGroup group, String name) {
        super(group, name);
    }

    public void run() {
        for (int i = 0; i < 20; i++) {
            System.out.println(getName() + " 线程的i变量" + i);
        }
    }
}

public class ThreadGroupTest {
    public static void main(String[] args) {
        // 获取主线程所在的线程组,这是所有线程默认的线程组
        ThreadGroup mainGroup = Thread.currentThread().getThreadGroup();
        System.out.println("主线程组的名字:" + mainGroup.getName());
        System.out.println("主线程组是否是后台线程组:" + mainGroup.isDaemon());
        new MyThread("主线程组的线程").start();
        ThreadGroup tg = new ThreadGroup("新线程组");
        tg.setDaemon(true);
        System.out.println("tg线程组是否是后台线程组:" + tg.isDaemon());
        MyThread tt = new MyThread(tg, "tg组的线程甲");
        tt.start();
        new MyThread(tg, "tg组的线程乙").start();
    }
}
View Code

// 继承RecursiveAction来实现"可分解"的任务
class PrintTask extends RecursiveAction {
    // 每个“小任务”只最多只打印50个数
    private static final int THRESHOLD = 50;
    private int start;
    private int end;

    // 打印从start到end的任务
    public PrintTask(int start, int end) {
        this.start = start;
        this.end = end;
    }

    @Override
    protected void compute() {
        // 当end与start之间的差小于THRESHOLD时,开始打印
        if (end - start < THRESHOLD) {
            for (int i = start; i < end; i++) {
                System.out.println(Thread.currentThread().getName() + "的i值:"
                        + i);
            }
        } else {
            // 如果当end与start之间的差大于THRESHOLD时,即要打印的数超过50个
            // 将大任务分解成两个小任务。
            int middle = (start + end) / 2;
            PrintTask left = new PrintTask(start, middle);
            PrintTask right = new PrintTask(middle, end);
            // 并行执行两个“小任务”
            left.fork();
            right.fork();
        }
    }
}

public class ForkJoinPoolTest {
    public static void main(String[] args) throws Exception {
        ForkJoinPool pool = new ForkJoinPool();
        // 提交可分解的PrintTask任务
        pool.submit(new PrintTask(0, 300));
        pool.awaitTermination(2, TimeUnit.SECONDS);
        // 关闭线程池
        pool.shutdown();
    }
}
View Code
// 继承RecursiveTask来实现"可分解"的任务
class CalTask extends RecursiveTask<Integer> {
    // 每个“小任务”只最多只累加20个数
    private static final int THRESHOLD = 20;
    private int arr[];
    private int start;
    private int end;

    // 累加从start到end的数组元素
    public CalTask(int[] arr, int start, int end) {
        this.arr = arr;
        this.start = start;
        this.end = end;
    }

    @Override
    protected Integer compute() {
        int sum = 0;
        // 当end与start之间的差小于THRESHOLD时,开始进行实际累加
        if (end - start < THRESHOLD) {
            for (int i = start; i < end; i++) {
                sum += arr[i];
            }
            return sum;
        } else {
            // 如果当end与start之间的差大于THRESHOLD时,即要累加的数超过20个时
            // 将大任务分解成两个小任务。
            int middle = (start + end) / 2;
            CalTask left = new CalTask(arr, start, middle);
            CalTask right = new CalTask(arr, middle, end);
            // 并行执行两个“小任务”
            left.fork();
            right.fork();
            // 把两个“小任务”累加的结果合并起来
            return left.join() + right.join(); //
        }
    }
}

public class Sum {
    public static void main(String[] args) throws Exception {
        int[] arr = new int[100];
        Random rand = new Random();
        int total = 0;
        // 初始化100个数字元素
        for (int i = 0, len = arr.length; i < len; i++) {
            int tmp = rand.nextInt(20);
            // 对数组元素赋值,并将数组元素的值添加到sum总和中。
            total += (arr[i] = tmp);
        }
        System.out.println(total);
        // 创建一个通用池
        ForkJoinPool pool = ForkJoinPool.commonPool();
        // 提交可分解的CalTask任务
        Future<Integer> future = pool.submit(new CalTask(arr, 0, arr.length));
        System.out.println(future.get());
        // 关闭线程池
        pool.shutdown();
    }
}
View Code
public class ThreadPoolTest {
    public static void main(String[] args)
        throws Exception
    {
        // 创建足够的线程来支持4个CPU并行的线程池
        // 创建一个具有固定线程数(6)的线程池
        ExecutorService pool = Executors.newFixedThreadPool(6);
        // 使用Lambda表达式创建Runnable对象
        Runnable target = () -> {
            for (int i = 0; i < 100 ; i++ )
            {
                System.out.println(Thread.currentThread().getName()
                    + "的i值为:" + i);
            }
        };
        // 向线程池中提交两个线程
        pool.submit(target);
        pool.submit(target);
        // 关闭线程池
        pool.shutdown();
    }
}
View Code

class Account {
    /*
     * 定义一个ThreadLocal类型的变量,该变量将是一个线程局部变量 每个线程都会保留该变量的一个副本
     */
    private ThreadLocal<String> name = new ThreadLocal<>();

    // 定义一个初始化name成员变量的构造器
    public Account(String str) {
        this.name.set(str);
        // 下面代码用于访问当前线程的name副本的值
        System.out.println("---" + this.name.get());
    }

    // name的setter和getter方法
    public String getName() {
        return name.get();
    }

    public void setName(String str) {
        this.name.set(str);
    }
}

class MyTest extends Thread {
    // 定义一个Account类型的成员变量
    private Account account;

    public MyTest(Account account, String name) {
        super(name);
        this.account = account;
    }

    public void run() {
        // 循环10次
        for (int i = 0; i < 10; i++) {
            // 当i == 6时输出将账户名替换成当前线程名
            if (i == 6) {
                account.setName(getName());
            }
            // 输出同一个账户的账户名和循环变量
            System.out.println(account.getName() + " 账户的i值:" + i);
        }
    }
}

public class ThreadLocalTest {
    public static void main(String[] args) {
        // 启动两条线程,两条线程共享同一个Account
        Account at = new Account("初始名");
        /*
         * 虽然两条线程共享同一个账户,即只有一个账户名 但由于账户名是ThreadLocal类型的,所以每条线程
         * 都完全拥有各自的账户名副本,所以从i == 6之后,将看到两条 线程访问同一个账户时看到不同的账户名。
         */
        new MyTest(at, "线程甲").start();
        new MyTest(at, "线程乙").start();
    }
}
View Code

 

 

posted @ 2016-04-26 02:28  pipi-changing  阅读(302)  评论(0编辑  收藏  举报