多线程概述
多线程是Java的特点之一,掌握多线程编程技术,可以充分利用CPU的资源,更容易解决实际问题,并且多线程技术广泛应用于网络相关的程序设计中。
进程
进程是程序的一次动态执行过程,它对应了从代码加载、执行至完毕的一个完整过程,这个过程也是进程本身从产、发展至消亡的过程。
线程
线程是比进程更小的执行单位,一个进程在其执行过程中,可以产生多个线程,形成多条执行线索,每条线索,即每个线程,也有它自身产生、存在和消亡的过程。与进程可以共享操作系统的资源类似,线程间也可以共享进程中的某些内存单元(包括代码与数据),并利用这些共享单元来实现数据交换、实时通信与必要的同步操作。与进程不同的是,线程的中断与恢复可以更加节省系统的开销。通俗地讲,线程是运行在进程中的“小进程”。
线程与进程的区别
(1)线程是程序执行的最小单位,而进程是操作系统分配资源的最小单位。 (2)一个进程由一个或多个线程组成,线程是一个进程中代码的不同执行路线。 (3)进程之间相互独立,但同一进程下的各个线程之间共享程序的内存空间(包括代码段、数据集、堆等)及一些进程级的资源(如打开文件和信号),某进程内的线程在其他进程不可见。 (4)线程上下文切换比进程上下文切换要快得多。
线程的执行
虽然执行线程给人一种几个事件同时发生的感觉,但这是一种错觉,因为计算机在任何给定的时刻只能执行那些线程中的一个。为了建立这些线程正在同步执行的感觉,Java 虚拟机快速地把控制从一个线程切换到另一个线程。这些线程将被轮流执行,使得每个线程都有机会使用CPU资源。
Java程序执行
Java应用程序总是从主类的 main方法开始执行。当JVM加载代码,发现 main方法之后,就会启动一个线程,这个线程称为“主线程”( main 线程),该线程负责执行main方法。那么,在main方法的执行中再创建的线程,就成为程序中的其他线程。如果main方法中没有创建其他线程,那么当main方法执行完最后一个语句,即main方法返回时,JVM就会结束Java应用程序。如果main方法中创建了其他线程,那么JVM就要在主线程和其他线程之间轮流切换,保证每个线程都有机会使用CPU资源,main方法即使执行完最后的语句(主线程结束),JVM 也一直要等到Java应用程序中的所有线程都结束之后,才结束Java应用程序。
线程的生命周期
(1)新建态。 当一个Thread类或其子类的对象被声明并创建,但还没有调用其 start()方法时,线程处于新建状态。此时它已经有了相应的内存空间和其他资源。 (2)就绪态。 当线程有资格运行,但调度程序还没有把它选定为运行线程时线程所处的状态。当start()方法被调用时,线程首先进入就绪态。在线程运行之后或者从阻塞、等待或睡眠状态回来后,也返回到可运行状态。此状态的线程已经具备了运行的条件但还没开始运行。 (3))运行态。 线程调度程序从可运行池中选择一个线程,将CPU使用权切换给线程,将其作为当前线程时线程所处的状态。这也是线程进入运行状态的唯一一种方式。此时线程对象的run()方法执行,run()方法定义了线程的具体任务,所以程序必须在子类中重写父类的run()方法。在线程没有结束run()方法之前,不要让线程再次调用start()方法,否则将发生llegalThread-StateException异常。 (4)中断态。 处于运行态的线程可能由于以下原因而中断执行,进入中断态:
- 执行了sleep( int millsecond)方法。sleep是Thread类的一个类方法,线程一旦执行该方法,就立刻让出 CPU的使用权,使当前线程处于中断状态。经过指定的毫秒数( millsec-ond)后,线程重新进入就绪态。
- 执行了wait()方法。wait是 object类的方法,线程对象一旦调用wait(),必须由其他线程调用notify()或notifyAll()方法通知它,才能使得它重新进入就绪态。
- 执行了某个中断线程执行的操作,比如执行读/写操作引起线程阻塞。只有当引起阻塞的原因消除时,线程才能重新进入就绪态等待CPU资源,以便从原来中断处开始继续运行。
(5)死亡态。 处于死亡状态的线程不具有继续运行的能力。线程死亡的原因有两个,一个是正常运行的线程完成了它的全部工作,即执行完run()方法中的全部语句,结束了run()方法;另一个原因是线程被提前强制性终止。线程一旦死亡,就不能复生。
线程的优先级
??处于就绪状态的线程首先进入就绪队列排队等候CPU资源,同一时刻在就绪队列中的线程可能有多个。Java 虚拟机中的线程调度器负责管理线程,调度器把线程的优先级分为10个级别,分别用Thread类中的常量表示。每个Java线程的优先级都在常数1和10之间,即Thread.MIN_PRIORITY和Thread.MAX_PRIORITY 之间。如果没有明确地设置线程的优先级别,每个线程的优先级都为常数5,即 Thread.NORM_PRIORITY。 ??处于就绪状态的线程首先进入就绪队列排队等候CPU资源,同一时刻在就绪队列中的线程可能有多个。Java 虚拟机中的线程调度器负责管理线程,调度器把线程的优先级分为10个级别,分别用Thread类中的常量表示。每个Java线程的优先级都在常数1和10之间,即Thread.MIN_PRIORITY和Thread.MAX_PRIORITY 之间。如果没有明确地设置线程的优先级别,每个线程的优先级都为常数5,即 Thread.NORM_PRIORITY。 ??线程的优先级可以通过setPriority(int grade)方法调整,如果参数不在1~10范围内,那么setPriority便产生一个llegalArgumenException异常。getPriority方法返回线程的优先级。需要注意是,有些操作系统只识别3个级别:1、5和 10。 ??在采用时间片的系统中,每个线程都有机会获得CPU的使用权,以便使用CPU资源执行线程中的操作。当线程使用CPU资源的时间到时后,即使线程没有完成自己的全部操作,JVM也会中断当前线程的执行,把 CPU 的使用权切换给下一个排队等待的线程,当前线程将等待CPU资源的下一次轮回,然后从中断处继续执行。 ??JVM的线程调度器的任务是使高优先级的线程能始终运行,一旦时间片有空闲,则使具有同等优先级的线程以轮流的方式顺序使用时间片。也就是说,如果有A、B、C、D四个线程,A和B的级别高于C和D,那么,Java调度器首先以轮流的方式执行A和B,一直等到A、B都执行完毕进入死亡状态,才会在C、D之间轮流切换。 在实际编程时,不提倡使用线程的优先级来保证算法的正确执行。
public class Test {
@org.junit.Test
public void test() {
A a = new A("线程A");
B b = new B("线程B");
a.start();
b.start();
int aPriority = a.getPriority();
int bPriority = b.getPriority();
System.out.println("A线程的优先级:"+aPriority);
System.out.println("B线程的优先级"+bPriority);
}
public static void main(String[] args) {
A a = new A("线程A");
B b = new B("线程B");
a.start();
b.start();
System.out.println("main线程---"+Thread.currentThread().getName());
}
}
class A extends Thread{
public A(String name) {
super(name);
}
@Override
public void run() {
Thread.currentThread().setPriority(9);
System.out.println("线程A被调用");
System.out.println(Thread.currentThread().getName()+"优先级:"+Thread.currentThread().getPriority());
}
}
class B extends Thread{
public B(String name) {
super(name);
}
@Override
public void run() {
Thread.currentThread().setPriority(1);
System.out.println("线程B被调用");
System.out.println(Thread.currentThread().getName()+"优先级:"+Thread.currentThread().getPriority());
}
}
结果显示
线程的创建
- 方式一:继承于Thread类
①创建一个继承于Thread类的子类 ②重写Thread类的run()–>将此线程要执行的操作声明在run()中 ③创建Thread类的子类对象 ④通过此对象调用start() - 方式二:实现runnable接口,重写run方法,创建Thread类对象,参数为runnable的实现类
- 方式三:JDK5.0新增线程创建方式,实现Callable接口
与Runnable接口相比Callable功能更强大一些 ?●相比run方法,可以有返回值 ?●方法可抛出异常 ?●支持泛型的返回值 ?●需要借助FutureTask类,比如获取返回结果 - Future接口:
①可以对具体的Runnable、Callable任务的执行结果进行取消、查询是否完成、获取结果等 ②FutureTask是Future接口唯一的实现类 ③FutureTask同时实现了Runnable,Future接口。它既可以作为Runnable被线程执行,又可以作为Future得到Callable的返回值
继承Thread类创建线程
public class ThreadTest {
public static void main(String[] args) {
Child child = new Child("Child");
Adult adult = new Adult("Adult");
Old old = new Old("Old");
child.start();
adult.start();
old.start();
}
}
class Child extends Thread{
public Child(String name) {
super(name);
}
int ticket = 50;
@Override
public void run(){
for (int i = 0; i < ticket; i++) {
System.out.println(Thread.currentThread().getName()+"卖出票:"+i);
}
}
}
class Adult extends Thread{
public Adult(String name) {
super(name);
}
int ticket = 50;
@Override
public void run(){
for (int i = 0; i < ticket; i++) {
System.out.println(Thread.currentThread().getName()+"卖出票:"+i);
}
}
}
class Old extends Thread{
public Old(String name) {
super(name);
}
int ticket = 50;
@Override
public void run(){
for (int i = 0; i < ticket; i++) {
System.out.println(Thread.currentThread().getName()+"卖出票:"+i);
}
}
}
输出结果 Runnable实现类
public class MyRunnable implements Runnable{
int ticket = 50;
String name;
public MyRunnable(String name) {
this.name = name;
}
@Override
public void run() {
for (int i = 1; i <= 50; i++) {
System.out.println(name+"卖出票:" +i);
}
}
}
main方法
public static void main(String[] args) {
MyRunnable child = new MyRunnable("Child");
MyRunnable adult = new MyRunnable("Adult");
MyRunnable old = new MyRunnable("Old");
Thread childThread = new Thread(child);
Thread adultThread = new Thread(adult);
Thread oldThread = new Thread(old);
childThread.start();
adultThread.start();
oldThread.start();
}
结果显示
线程的休眠与中断
??Thread的 sleep()方法能使当前线程暂停运行一段时间(单位:毫秒)。需要注意的是,sleep()方法的参数不能为负,否则会抛出IllegalArgumentException 异常。而Thread的 interrupt()方法经常用来“吵醒”休眠的线程。当一些线程调用sleep 方法处于休眠状态时,一个占有CPU资源的线程可以让休眠的线程调用interrupt()方法“吵醒”自己,即导致休眠的线程发生InterruptedException异常,从而结束休眠,重新排队等待CPU资源。 ??Thread.sleep()与线程调度器交互,它将当前线程设置为等待一段时间的状态。一旦等待时间结束,线程状态就会被改为可运行(runnable),并开始等待CPU来执行后续的任务。
代码示例
主方法
public class InterruptTest {
public static void main(String[] args) {
InterruptRunnable interruptRunnable = new InterruptRunnable();
interruptRunnable.seller.start();
interruptRunnable.passenger.start();
}
}
Runnable实现类
public class InterruptRunnable implements Runnable{
private int ticket = 50;
public Thread seller;
public Thread passenger;
public InterruptRunnable() {
seller = new Thread(this);
passenger = new Thread(this);
}
@Override
public void run() {
if (Thread.currentThread() == seller) {
try {
System.out.println("暂时没人买票休息30分钟");
seller.sleep(1000*60*30);
} catch (InterruptedException e) {
System.out.println("售票员被吵醒了,开始卖票");
}
} else if (Thread.currentThread() == passenger){
System.out.println("顾客来了,售票员要开始卖票");
seller.interrupt();
}
}
}
结果显示
线程让步与插队
1. 线程让步
线程让步的方法就是让正在执行的任务暂定,使其他任务继续执行。线程让步的方法如下:
public static void yield();
主方法
public class YieldTest {
public static void main(String[] args) {
YieldRunnable runnable = new YieldRunnable();
Thread redThread = runnable.red;
Thread blueThread = runnable.blue;
redThread.start();
blueThread.start();
}
}
Runnable实现类
public class YieldRunnable implements Runnable{
int count = 5;
Thread red;
Thread blue;
public YieldRunnable() {
red = new Thread(this);
blue = new Thread(this);
red.setName("红队");
blue.setName("蓝队");
}
@Override
public void run() {
while (count-- > 0) {
System.out.println(Thread.currentThread().getName()+"拍第"+count+"次球");
Thread.yield();
}
}
}
输出结果
注意: ??两次执行的结果不同,第一次是红蓝交替执行,第二次蓝队连续执行两次,红队也连续执行两次。说明调用yield方法仅仅只是让线程重新回到就绪状态,仍有可能被CPU调用。
2.线程插队
??线程插队是通过join()方法阻塞当前线程,先完成被join()方法加入的线程,之后再完成其他线程。使用线程插队join()方法时,需要抛出InterruptedException异常。 ??如果在线程A占有CPU资源期间,B线程插人,那么A线程将立刻中断执行,一直等到线程B执行完毕,A线程再重新排队等待CPU资源,以便恢复执行。
主方法
public class JoinTest {
public static void main(String[] args) {
JoinRunnable runnable = new JoinRunnable();
runnable.thread1.start();
}
}
Runnable实现类
public class JoinRunnable implements Runnable{
Thread thread1;
Thread thread2;
public JoinRunnable() {
thread1 = new Thread(this);
thread2 = new Thread(this);
thread1.setName("线程A");
thread2.setName("线程B");
}
@Override
public void run() {
if (Thread.currentThread() == thread1) {
System.out.println(Thread.currentThread().getName()+"准备买票...");
thread2.start();
try {
thread2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"开始买票...");
try {
Thread.sleep(1000*3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"结束买票。");
} else {
System.out.println("线程B现在插入...");
System.out.println("线程B开始买票...");
try {
Thread.sleep(1000*3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程B结束买票.");
}
}
}
输出结果
线程同步与死锁
1.线程同步
??在处理多线程问题时,必须要注意这样一个问题:当两个或多个线程同时访问同一个变量,并且一些线程需要修改这个变量的情况。程序应对这样的问题做出正确的处理,否则可能发生混乱。 ??对于多个线程修改同一个资源的情况,采用线程同步的方式。线程同步的基本实现思路是给共享资源加一把锁,这把锁只有一把钥匙。哪个线程获取了这把钥匙,哪个线程才有权利访问该共享资源。基于上述思想,编程语言的设计思路是把同步锁加在代码段上,即把同步锁加在“访问共享资源的代码段”上。Java语言的线程同步就是若干个线程都需要使用synchronized关键字给代码段加锁。 ??处于运行状态的线程若遇到sleep()方法,则线程进入睡眠状态,不会让出资源锁,sleep()方法结束,线程转为就绪状态,等待系统重新调度;处于运行状态的线程可能在等待IO,也可能进入挂起状态。当IO完成,转为就绪状态;处于运行状态的线程遇到yield()方法,线程转为就绪状态( yield 只让给权限比自己高的);处于运行状态的线程遇到wait()方法,线程处于等待状态,需要notify(YnotifyALL()来唤醒线程,唤醒后的线程处于锁定状态,获取了“同步锁”之后,线程才转为就绪状态;处于运行状态的线程,加上 synchronized后变成同步操作,处于锁定状态。获取了“同步锁”之后,线程才转为就绪状态。 ??当一个线程使用的同步方法中用到某个变量,而此变量要在其他线程修改后才能符合本线程的需要,那么可以在同步方法中使用wait()方法。wait()方法可以中断线程的执行,使本线程等待,暂时让出CPU的使用权,并允许其他线程使用这个同步方法。其他线程如果在使用这个同步方法时不需要等待,那么它在使用同步方法的同时,应当用notifyAll()方法通知所有由于使用这个同步方法而处于等待的线程结束等待,曾中断的线程就会从刚才的中断处继续执行这个同步方法,并遵循“先中断先继续”的原则。如果使用notify()方法,那么只是通知处于等待中的线程的某一个结束等待。wait()、notify()和 notifyAll()都是Object类中的final方法,被所有的类继承且不允许中断的方法。特别需要注意的是,不可以在非同步方法中使用wait()、notify()和 notifyAll()。
线程同步有两种方式:
1.同步代码块 synchronized(同步监视器){ ??//需要被同步的代码,不能多了,也不能少了 } 说明: 1.操作共享数据的代码即为需要被同步的代码 2.共享数据:需要多个线程共同操作的数据 3.同步监视器:俗称,“锁”。任何一个类的对象都可以充当这个锁 要求 多个线程必须共用一把锁 补充 1.在实现Runnable接口创建多线程的方式中,我们可以考虑使用this来充当同步监视器 2.在继承Thread类创建多线程的方式中,慎用this充当同步监视器,考虑使用当前类充当同步监视器。 2.同步方法 (1)如果共享数据的代码完整的声明在一个方法中,我们不妨将此方法声明为同步的。 (2)同步方法中没有显示的出现锁,直接将方法声明为synchronized后,隐式调用this为锁
主方法
public class SynchroTest1 {
public static void main(String[] args) {
SynchroRunnable runnable = new SynchroRunnable();
Thread thread1 = new Thread(runnable);
thread1.setName("平顶山");
Thread thread2 = new Thread(runnable);
thread2.setName("杭州");
thread1.start();
thread2.start();
}
}
线程同步前
public class SynchroRunnable implements Runnable{
private int ticket = 50;
@Override
public void run() {
while (ticket > 0) {
System.out.println(Thread.currentThread().getName()+": 出票号 :"+ticket);
ticket--;
}
}
}
结果输出 问题 出现了两次50,出现的原因是当平顶山拿到50票号的时候还没让票号减一,然后杭州就也拿到了50票号,至于为什么接下来不是从48开始,我没想明白,希望有大佬能够解答一下。
解决方式1:同步代码块
public class SynchroRunnable implements Runnable{
private int ticket = 10;
@Override
public void run() {
while (true) {
synchronized (this) {
if (ticket <= 0) break;
System.out.println(Thread.currentThread().getName()+"出票号:"+ticket);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
ticket--;
}
}
}
}
输出结果 解决方式二:同步方法
public class SynchroRunnable implements Runnable{
private int ticket = 10;
@Override
public void run() {
show();
}
public synchronized void show() {
while (ticket > 0) {
System.out.println(Thread.currentThread().getName()+"出票号:"+ticket);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
ticket--;
try {
notifyAll();
wait();
notifyAll();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
结果输出
注意 wait、notify、notifyAll只能使用在同步代码块中 notifyAll,唤醒所有被wait的线程 sleep和wait的异同 1.相同点:一旦执行方法,都可以使得当前线程进入阻塞状态 2.不同点: ??①两个方法声明的位置不同,Thread类中声明sleep,Object类(作为锁的那个类)中声明wait。 ??②调用的要求不同:sleep()可以在任何需要的场景下调用,wait必须使用在同步代码块中。 ??③关于是否会释放同步监视器,如果两个方法都使用在同步代码块或同步方法中,sleep不会释放锁,wait会释放锁。
2.线程死锁
??资源共享时需要进行同步操作,程序中过多的同步会产生死锁。比如,张三想要李四的画,李四想要张三的书,张三对李四说:“先把你的画给我,我就给你书。”李四也对张三说:“先把你的书给我,我就给你画。”此时,张三等李四答复,而李四也等张三答复,那么这样下去最终结果就是张三得不到李四的画,李四也得不到张三的书,这实际上就是死锁。 main方法
public class DeadTest {
public static void main(String[] args) {
DeadRunnable runnable = new DeadRunnable();
runnable.thread1.start();
runnable.thread2.start();
}
}
Runnable实现类
public class DeadRunnable implements Runnable{
private static ZhangSan zhangSan = new ZhangSan();
private static LiSi liSi = new LiSi();
Thread thread1;
Thread thread2;
public DeadRunnable() {
thread1 = new Thread(this);
thread2 = new Thread(this);
}
@Override
public void run() {
if (Thread.currentThread() == thread1) {
synchronized (zhangSan) {
zhangSan.say();
synchronized (liSi) {
zhangSan.want();
}
}
} else {
synchronized (liSi) {
liSi.say();
synchronized (zhangSan) {
liSi.want();
}
}
}
}
}
class ZhangSan {
public void say() {
System.out.println("我是张三,我有书");
}
public void want() {
System.out.println("我是张三,我想要李四的书");
}
}
class LiSi {
public void say() {
System.out.println("我是李四,我有画");
}
public void want() {
System.out.println("我是李四,我想要张三的书");
}
}
输出结果 可以看出张三和李四都在等待对方去释放锁,然后输出想要对方的书和画,于是程序一直处于等待状态。
实现Callable接口
main方法
public class CallTest {
public static void main(String[] args) {
CallableTest callable = new CallableTest();
FutureTask futureTask = new FutureTask(callable);
Thread thread = new Thread(futureTask);
thread.start();
try {
Object sum = futureTask.get();
System.out.println("总和为:"+sum);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
Callable实现类
class CallableTest implements Callable {
int sum;
@Override
public Object call() {
for (int i = 1; i <= 100; i++) {
if (i % 2 == 0) {
System.out.println(i);
sum += i;
}
}
return sum;
}
}
输出结果 线程池 ●JDK5.0起提供了线程池相关的API:ExecutorService和Executors ●ExecutorService:真正的线程池接口,常见子类ThreadPoolExecutor ???void execute(Runnable command),执行任务/命令,没有返回值,一般用来执行Runnable ???Future submit(Callable task):执行任务,有返回值,一般用来执行Callable ●Executors:工具类,线程池的工厂类,用于创建并返回不同类型的线程池 ???Executors.newCachedThreadPool():创建一个可根据需要创建新线程的线程池 ???Executors.newFixedThreadPool(n):创建一个可重用固定线程数的线程池 ???Executors.newSingleThreadExecutor():创建一个只有一个线程的线程池 ???Executors.newScheduledThreadPool(n) :创建一个线程池,它可安排在给定延迟后运行命令或定期的执行。 代码示例
public static void main(String[] args) {
ExecutorService service = Executors.newFixedThreadPool(10);
service.execute(new Number1Thread());
FutureTask futureTask = new FutureTask(new Number2Thread());
service.submit(futureTask);
try {
Object sum = futureTask.get();
System.out.println("10以内偶数和为:"+sum);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
service.shutdown();
}
class Number1Thread implements Runnable {
@Override
public void run() {
for (int i = 1; i <= 10; i++) {
if (i%2 != 0) {
System.out.println(Thread.currentThread().getName()+"输出:"+i);
}
}
}
}
class Number2Thread implements Callable {
@Override
public Object call() {
int sum = 0;
for (int i = 1; i <= 10; i++) {
if (i%2 == 0) {
sum += i;
System.out.println(Thread.currentThread().getName()+"输出:"+i);
}
}
return sum;
}
}
输出结果
Lock ??1.从JDK5.0开始,Java提供了更强大的线程同步机制——通过显示定义同步锁对象来实现同步,同步锁对象使用Lock对象来充当 ??2.java.util.concurrent.locks.Lock接口是控制多个线程对共享资源进行访问的工具。锁提供了对共享资源的独占访问,每次只能有一个线程对Lock对象加锁,线程开始访问共享资源之前应先获得Lock对象。 ??3.ReentrantLock类实现了Lock,它拥有与synchronized相同的并发性和内存语义,在实现线程安全的控制中,比较常用的是ReentrantLock,可以显示加锁,释放锁。 ??4.synchronized 与 lock 的异同: ??相同点:二者都可以解决线程安全问题 ??不同点:synchronized机制在执行完相应的同步代码以后,自动的释放同步监视器; lock需要手动的启动同步(lock()),同时结束同步也需要手动的实现(unlock())。 代码示例
public class LockTest {
public static void main(String[] args) {
Depositor depositor = new Depositor();
depositor.thread1.start();
depositor.thread2.start();
}
}
class Depositor implements Runnable {
private ReentrantLock reentrantLock = new ReentrantLock();
private double account = 0;
Thread thread1;
Thread thread2;
int a1 = 3;
int a2 = 3;
public Depositor() {
thread1 = new Thread(this);
thread2 = new Thread(this);
thread1.setName("储户A");
thread2.setName("储户B");
}
@Override
public void run() {
if (Thread.currentThread() == thread1) {
for (int i = 1; i <= a1; i++) {
System.out.println(Thread.currentThread().getName()+"进行第"+i+"次操作");
save();
show();
System.out.println(Thread.currentThread().getName()+"结束第"+i+"次操作");
}
} else {
for (int i = 1; i <= a2; i++) {
System.out.println(Thread.currentThread().getName()+"进行第"+i+"次操作");
save();
show();
System.out.println(Thread.currentThread().getName()+"结束第"+i+"次操作");
}
}
}
private void save() {
try {
reentrantLock.lock();
account += 1000;
System.out.println(Thread.currentThread().getName()+"存入1000元");
} finally {
reentrantLock.unlock();
}
}
private void show() {
System.out.println(Thread.currentThread().getName()+"显示账户余额为:"+account);
}
}
输出结果
|