【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(); } } } // 通过实现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(); } } } } 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(); } } } }
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(); } } } } 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(); } } } } 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线程)结束------ // 后台线程也应该随之结束 } } 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); } } } 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); } } } } 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); } } } 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(); } } 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; } } 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(); } } 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); } }
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; } } 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(); } } 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() + "取钱失败!余额不足!"); } } // 同步代码块结束,该线程释放同步锁 } } 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; } } 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(); } } 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); } } 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; } } 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(); } } 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(); } } 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() + "取钱失败!余额不足!"); } } } 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; } } 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); } } } 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(); } } 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); } } } 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; } } 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); } } } 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(); } } 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); } } } 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"); // ① 阻塞线程。 } } 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(); } } // 定义自己的异常处理器 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("程序正常结束!"); } } 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(); } } // 继承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(); } } // 继承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(); } } 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(); } } 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(); } }
|