java的多线程用法编程总结

一、进程与线程

1、进程是什么?

狭义定义:进程是正在运行的程序的实例(an instance of a computer program that is being executed)。

广义定义:进程是一个具有一定独立功能的程序关于某个数据集合的一次运行活动。它是操作系统动态执行的基本单元,在传统的操作系统中,进程既是基本的分配单元,也是基本的执行单元。

2、线程是什么?

线程,有时被称为轻量级进程(Lightweight Process,LWP),是程序执行流的最小单元。一个标准的线程由线程ID,当前指令指针(PC),寄存器集合和堆栈组成。另外,线程是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点儿在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源。

3、进程和线程的区别?

进程和线程的主要差别在于它们是不同的操作系统资源管理方式。

进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程只是一个进程中的不同执行路径。

线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间,一个线程死掉就等于整个进程死掉,所以多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差一些。但对于一些要求同时进行并且又要共享某些变量的并发操作,只能用线程,不能用进程。

简言之,线程与进程的区别就是:

(1)一个程序至少有一个进程,一个进程至少有一个线程;
(2) 线程的划分尺度小于进程,使得多线程程序的并发性高。
(3)进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序的运行效率。
(4)线程在执行过程中与进程是有区别的。每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口。但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。
(5)从逻辑角度来看,多线程的意义在于一个应用程序中,有多个执行部分可以同时执行。但操作系统并没有将多个线程看做多个独立的应用,来实现进程的调度和管理以及资源分配。

这就是进程和线程的重要区别。

二、线程的生命周期及五种基本状态

Java线程具有五种基本状态:

(1)新建状态(New):当线程对象对创建后,即进入了新建状态,如:Thread t = new MyThread();

(2)就绪状态(Runnable):当调用线程对象的start()方法(t.start();),线程即进入就绪状态。处于就绪状态的线程,只是说明此线程已经做好了准备,随时等待CPU调度执行,并不是说执行了t.start()此线程立即就会执行;

(3)运行状态(Running):当CPU开始调度处于就绪状态的线程时,此时线程才得以真正执行,即进入到运行状态。注:就 绪状态是进入到运行状态的唯一入口,也就是说,线程要想进入运行状态执行,首先必须处于就绪状态中;

(4)阻塞状态(Blocked):处于运行状态中的线程由于某种原因,暂时放弃对CPU的使用权,停止执行,此时进入阻塞状态,直到其进入到就绪状态,才 有机会再次被CPU调用以进入到运行状态。根据阻塞产生的原因不同,阻塞状态又可以分为三种:

①等待阻塞:运行状态中的线程执行wait()方法,使本线程进入到等待阻塞状态;

②同步阻塞:线程在获取synchronized同步锁失败(因为锁被其它线程所占用),它会进入同步阻塞状态;

③其他阻塞 : 通过调用线程的sleep()或join()或发出了I/O请求时,线程会进入到阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。

(5)死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。

三、Java多线程的实现

在Java中,如果要实现多线程的程序,那么必须依靠一个线程的主体类(好比主类的概念一样,表示一个线程的主类),但是这个线程的主体类在定义的时候需要有一些特殊的要求,这个类可以继承Thread类或实现Runnable接口来完成定义。

1、继承Thread类实现多线程

java.lang.Thread是一个负责线程操作的类,任何的类继承了Thread类就可以成为一个线程的主类。既然是主类,必须有它的使用方法,而线程启动的主方法需要覆写Thread类中的run()方法才可以。

定义一个线程的主体类:

class MyThread extends Thread { // 线程的主体类
 private String title;

 public MyThread(String title) {
 this.title = title;
 }

 @Override
 public void run() { // 线程的主方法
 for (int x = 0; x < 10; x++) {
  System.out.println(this.title + "运行,x = " + x);
 }
 }
}

现在已经有了线程类,并且里面也存在了相应的操作方法,那么就应该产生对象并调用里面的方法,于是编写出了下的程序:

public class TestDemo {
 public static void main(String[] args) {
 MyThread mt1 = new MyThread("线程A");
 MyThread mt2 = new MyThread("线程B");
 MyThread mt3 = new MyThread("线程C");
 mt1.run();
 mt2.run();
 mt3.run();
 }

运行结果:

线程A运行,x = 0
线程A运行,x = 1
线程A运行,x = 2
线程A运行,x = 3
线程A运行,x = 4
线程A运行,x = 5
线程A运行,x = 6
线程A运行,x = 7
线程A运行,x = 8
线程A运行,x = 9
线程B运行,x = 0
线程B运行,x = 1
线程B运行,x = 2
线程B运行,x = 3
线程B运行,x = 4
线程B运行,x = 5
线程B运行,x = 6
线程B运行,x = 7
线程B运行,x = 8
线程B运行,x = 9
线程C运行,x = 0
线程C运行,x = 1
线程C运行,x = 2
线程C运行,x = 3
线程C运行,x = 4
线程C运行,x = 5
线程C运行,x = 6
线程C运行,x = 7
线程C运行,x = 8
线程C运行,x = 9

我们发现:以上的操作并没有真正的启动多线程,因为多个线程彼此之间的执行一定是交替的方式运行,而此时是顺序执行,每一个对象的代码执行完之后才向下继续执行。

如果要想在程序之中真正的启动多线程,必须依靠Thread类的一个方法:public void start(),表示真正启动多线程,调用此方法后会间接调用run()方法:

public class TestDemo {
 public static void main(String[] args) {
 MyThread mt1 = new MyThread("线程A");
 MyThread mt2 = new MyThread("线程B");
 MyThread mt3 = new MyThread("线程C");
 mt1.start();
 mt2.start();
 mt3.start();
 }

}

运行结果:

线程C运行,x = 0
线程A运行,x = 0
线程B运行,x = 0
线程A运行,x = 1
线程C运行,x = 1
线程A运行,x = 2
线程B运行,x = 1
线程A运行,x = 3
线程A运行,x = 4
线程A运行,x = 5
线程C运行,x = 2
线程C运行,x = 3
线程C运行,x = 4
线程C运行,x = 5
线程C运行,x = 6
线程C运行,x = 7
线程C运行,x = 8
线程C运行,x = 9
线程A运行,x = 6
线程A运行,x = 7
线程A运行,x = 8
线程A运行,x = 9
线程B运行,x = 2
线程B运行,x = 3
线程B运行,x = 4
线程B运行,x = 5
线程B运行,x = 6
线程B运行,x = 7
线程B运行,x = 8
线程B运行,x = 9

此时可以发现:多个线程之间彼此交替执行,但每次的执行结果是不一样的。通过以上的代码就可以得出结论:要想启动线程必须依靠Thread类的start()方法执行,线程启动之后会默认调用了run()方法。

在调用start()方法之后,发生了一系列复杂的事情:
(1)启动新的执行线程(具有新的调用栈);
(2)该线程从新状态转移到可运行状态;
(3)当该线程获得机会执行时,其目标run()方法将运行。
注意:对Java来说,run()方法没有任何特别之处。像main()方法一样,它只是新线程知道调用的方法名称(和签名)。因此,在Runnable上或者Thread上调用run方法是合法的,但并不启动新的线程。

说明:为什么线程启动的时候必须调用start()而不是直接调用run()?

我们发现,在调用了start()之后,实际上它执行的还是覆写后的run()方法,那为什么不直接调用run()方法呢?为了解释此问题,下面打开Thread类的源代码,观察一下start()方法的定义:

 public synchronized void start() {
 if (threadStatus != 0)
  throw new IllegalThreadStateException();
 group.add(this);
 boolean started = false;
 try {
  start0();
  started = true;
 } finally {
  try {
  if (!started) {
   group.threadStartFailed(this);
  }
  } catch (Throwable ignore) {
  }
 }
 }
 private native void start0();

打开此方法的源代码首先可以发现:方法会抛出一个“IllegalThreadStateException”异常。一般来讲,如果一个方法中使用了throw抛出一个异常对象,那么这个异常应该使用try…catch捕获,或者是方法的声明上使用throws抛出,但是这块都没有,为什么呢?因为这个异常类是属于运行时异常(RuntimeException)的子类:

java.lang.Object
   |- java.lang.Throwable
     |- java.lang.Exception
       |- java.lang.RuntimeException
         |- java.lang.IllegalArgumentException
           |- java.lang.IllegalThreadStateException

当一个线程对象被重复启动之后会抛出此异常,即:一个线程对象只能启动一次。

在start()方法之中有一个最为关键的部分就是start0()方法,而且这个方法上使用了一个native关键字的定义。

native关键字指的是Java本地接口调用(Java Native Interface),即:是使用Java调用本机操作系统的函数功能完成一些特殊的操作,而这样的代码开发在Java之中几乎很少出现,因为Java的最大特点是可移植性,如果一个程序只能在固定的操作系统上使用,那么可移植性就将彻底的丧失,所以,此操作一般不用。

多线程的实现一定需要操作系统的支持,那么以上的start0()方法实际上就和抽象方法很类似没有方法体,而这个方法体交给JVM去实现,即:在windows下的JVM可能使用A方法实现了start0(),而在Linux下的JVM可能使用了B方法实现了start0(),但是在调用的时候并不会去关心具体是何方式实现了start0()方法,只会关心最终的操作结果,交给JVM去匹配了不同的操作系统。

所以在多线程操作之中,使用start()方法启动多线程的操作是需要进行操作系统函数调用的。

2、实现Runnable接口实现多线程

使用Thread类的确是可以方便的进行多线程的实现,但是这种方式最大的缺点就是单继承的问题。为此,在java之中也可以利用Runnable接口来实现多线程。这个接口的定义如下:

public interface Runnable {
 public void run();
}

通过Runnable接口实现多线程:

class MyThread implements Runnable { // 线程的主体类
 private String title;

 public MyThread(String title) {
 this.title = title;
 }

 @Override
 public void run() { // 线程的主方法
 for (int x = 0; x < 10; x++) {
  System.out.println(this.title + "运行,x = " + x);
 }
 }
}

这和之前继承Thread类的方式区别不大,但是有一个优点就是避免了单继承局限。

不过问题来了。之前说过,如果要启动多线程,需要依靠Thread类的start()方法完成,之前继承Thread类的时候可以将此方法直接继承过来使用,但现在实现的是Runable接口,没有这个方法可以继承了,怎么办?

要解决这个问题,还是需要依靠Thread类完成。在Thread类中定义了一个构造方法,接收Runnable接口对象:

public Thread(Runnable target);

利用Thread类启动多线程:

public class TestDemo {
 public static void main(String[] args) throws Exception {
 MyThread mt1 = new MyThread("线程A");
 MyThread mt2 = new MyThread("线程B");
 MyThread mt3 = new MyThread("线程C");
 new Thread(mt1).start();
 new Thread(mt2).start();
 new Thread(mt3).start();
 }
}

运行结果:

线程A运行,x = 0
线程B运行,x = 0
线程B运行,x = 1
线程C运行,x = 0
线程B运行,x = 2
线程A运行,x = 1
线程B运行,x = 3
线程C运行,x = 1
线程C运行,x = 2
线程B运行,x = 4
线程B运行,x = 5
线程A运行,x = 2
线程A运行,x = 3
线程A运行,x = 4
线程A运行,x = 5
线程A运行,x = 6
线程A运行,x = 7
线程A运行,x = 8
线程A运行,x = 9
线程B运行,x = 6
线程B运行,x = 7
线程B运行,x = 8
线程B运行,x = 9
线程C运行,x = 3
线程C运行,x = 4
线程C运行,x = 5
线程C运行,x = 6
线程C运行,x = 7
线程C运行,x = 8
线程C运行,x = 9

此时,不但实现了多线程的启动,而且没有了单继承局限。

3、实现多线程的第三种方法:.使用Callable接口实现多线程

使用Runnable接口实现的多线程可以避免单继承局限,但是有一个问题,Runnable接口里面的run()方法不能返回操作结果。为了解决这个问题,提供了一个新的接口:Callable接口(java.util.concurrent.Callable)。

public interface Callable<V>{
 public V call() throws Exception;
}

执行完Callable接口中的call()方法会返回一个结果,这个返回结果的类型由Callable接口上的泛型决定。

实现Callable接口来实现多线程的具体操作是:
创建Callable接口的实现类,并实现clall()方法;然后使用FutureTask类来包装Callable实现类的对象,且以此FutureTask对象作为Thread对象的target来创建线程。

定义一个线程主体类:

import java.util.concurrent.Callable;

class MyThread implements Callable<String>{

 private int ticket = 10;
 @Override
 public String call() throws Exception {
 for(int i = 0 ; i < 20 ; i++){
  if(this.ticket > 0){
  System.out.println("卖票,剩余票数为"+ this.ticket --);
  }
 }
 return "票已卖光";
 }

}

Thread类没有直接支持Callable接口。而在JDK1.5之后,提供了一个类:

java.util.concurrent.FutureTask<V>

这个类主要负责Callable接口对象操作。其定义结构如下:

public class FutureTask<V>
extends Object
implements RunnableFurture<V>

而RunnableFurture这个接口又有如下定义:

public interface RunnableFurture<V>
extends Runnable,Future<V>

在FutureTask 类里面定义有如下构造方法:

public FutureTask(Callable<V> callable)

现在,终于可以通过FutureTask类来接收Callable接口对象了,接收的目的是为了取得call()方法的返回结果。

从上面分析我们可以发现:
FutureTask类可以接收Callable接口对象,而FutureTask类实现了RunnableFurture接口,RunnableFurture接口又继承了Runnable接口。

于是,我们可以这样来启动多线程:

public class TestDemo {
 public static void main(String[] args) throws Exception {
 MyThread mt1 = new MyThread();
 MyThread mt2 = new MyThread();

 FutureTask<String> task1 = new FutureTask<String>(mt1);//取得call()方法返回结果
 FutureTask<String> task2 = new FutureTask<String>(mt2);//取得call()方法返回结果

 //FutureTask是Runnable接口的子类,可以使用Thread类的构造来接收task对象
 new Thread(task1).start();
 new Thread(task2).start();

 //多线程执行完毕后,可以使用FutureTask的父接口Future中的get()方法取得执行结果
 System.out.println("线程1的返回结果:"+task1.get());
 System.out.println("线程2的返回结果:"+task2.get());
 }
}

运行结果:

卖票,剩余票数为10
卖票,剩余票数为10
卖票,剩余票数为9
卖票,剩余票数为8
卖票,剩余票数为7
卖票,剩余票数为9
卖票,剩余票数为6
卖票,剩余票数为8
卖票,剩余票数为5
卖票,剩余票数为7
卖票,剩余票数为4
卖票,剩余票数为6
卖票,剩余票数为3
卖票,剩余票数为5
卖票,剩余票数为2
卖票,剩余票数为4
卖票,剩余票数为1
卖票,剩余票数为3
卖票,剩余票数为2
卖票,剩余票数为1
线程1的返回结果:票已卖光
线程2的返回结果:票已卖光

小结:

上述讲解了三种实现多线程的方式,对于线程的启动而言,都是调用线程对象的start()方法,需要特别注意的是:不能对同一线程对象两次调用start()方法。

时间: 2016-10-10

java多线程编程技术详解和实例代码

 java多线程编程技术详解和实例代码 1.   Java和他的API都可以使用并发. 可以指定程序包含不同的执行线程,每个线程都具有自己的方法调用堆栈和程序计数器,使得线程在与其他线程并发地执行能够共享程序范围内的资源,比如共享内存,这种能力被称为多线程编程(multithreading),在核心的C和C++语言中并不具备这种能力,尽管他们影响了JAVA的设计. 2.   线程的生命周期 新线程的生命周期从"新生"状态开始.程序启动线程前,线程一直是"新生"状态:

Java多线程编程之读写锁ReadWriteLock用法实例

读写锁:分为读锁和写锁,多个读锁不互斥,读锁与写锁互斥,这是由jvm自己控制的,你只要上好相应的锁即可.如果你的代码只读数据,可以很多人同时读,但不能同时写,那就上读锁:如果你的代码修改数据,只能有一个人在写,且不能同时读取,那就上写锁.总之,读的时候上读锁,写的时候上写锁! 三个线程读数据,三个线程写数据示例: 可以同时读,读的时候不能写,不能同时写,写的时候不能读. 读的时候上读锁,读完解锁:写的时候上写锁,写完解锁. 注意finally解锁. package com.ljq.test.th

详解Java多线程编程中互斥锁ReentrantLock类的用法

0.关于互斥锁 所谓互斥锁, 指的是一次最多只能有一个线程持有的锁. 在jdk1.5之前, 我们通常使用synchronized机制控制多个线程对共享资源的访问. 而现在, Lock提供了比synchronized机制更广泛的锁定操作, Lock和synchronized机制的主要区别: synchronized机制提供了对与每个对象相关的隐式监视器锁的访问, 并强制所有锁获取和释放均要出现在一个块结构中, 当获取了多个锁时, 它们必须以相反的顺序释放. synchronized机制对锁的释放是

详解Java多线程编程中CountDownLatch阻塞线程的方法

直译过来就是倒计数(CountDown)门闩(Latch).倒计数不用说,门闩的意思顾名思义就是阻止前进.在这里就是指 CountDownLatch.await() 方法在倒计数为0之前会阻塞当前线程. CountDownLatch是一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待. CountDownLatch 的作用和 Thread.join() 方法类似,可用于一组线程和另外一组线程的协作.例如,主线程在做一项工作之前需要一系列的准备工作,只有这些准备工

Java多线程编程中的两种常用并发容器讲解

ConcurrentHashMap并发容器 ConcurrentHashMap可以做到读取数据不加锁,并且其内部的结构可以让其在进行写操作的时候能够将锁的粒度保持地尽量地小,不用对整个ConcurrentHashMap加锁. ConcurrentHashMap的内部结构 ConcurrentHashMap为了提高本身的并发能力,在内部采用了一个叫做Segment的结构,一个Segment其实就是一个类Hash Table的结构,Segment内部维护了一个链表数组,我们用下面这一幅图来看下Con

Java多线程编程之访问共享对象和数据的方法

多个线程访问共享对象和数据的方式有两种情况: 1.每个线程执行的代码相同,例如,卖票:多个窗口同时卖这100张票,这100张票需要多个线程共享. 2.每个线程执行的代码不同,例如:设计四个线程,其中两个线程每次对j增加1,另外两个线程每次对j减少1. a.如果每个线程执行的代码相同,可以使用同一个Runnable对象,这个对象中有共享数据.卖票就可以这样做,每个窗口都在做卖票任务,卖的票都是同一个数据(点击查看具体案例). b.如果每个线程执行的代码不同,就需要使用不同的Runnable对象,有

java多线程编程之为什么要进行数据同步

Java中的变量分为两类:局部变量和类变量.局部变量是指在方法内定义的变量,如在run方法中定义的变量.对于这些变量来说,并不存在线程之间共享的问题.因此,它们不需要进行数据同步.类变量是在类中定义的变量,作用域是整个类.这类变量可以被多个线程共享.因此,我们需要对这类变量进行数据同步.数据同步就是指在同一时间,只能由一个线程来访问被同步的类变量,当前线程访问完这些变量后,其他线程才能继续访问.这里说的访问是指有写操作的访问,如果所有访问类变量的线程都是读操作,一般是不需要数据同步的.那么如果不

java多线程编程制作电子时钟

模拟一个电子时钟,它可以在任何时候被启动或者停止,并可以独立的运行. 1.定义一个Clock类.它继承Label类,并实现Runnable接口.这个类中有一个Thread类型的clocker域,以及start()和run()方法.在run()方法中,每隔一秒就把系统时间显示为label的文本. class Clock extends Label implements Runnable { //定义Thread类型的clocker域 public Thread clocker=null; publ

java多线程编程之使用Synchronized块同步变量

下面的代码演示了如何同步特定的类方法: 复制代码 代码如下: package mythread; public class SyncThread extends Thread{ private static String sync = ""; private String methodType = ""; private static void method(String s) {  synchronized (sync)  {sync = s;System.out

java多线程编程之使用Synchronized块同步方法

synchronized关键字有两种用法.第一种就是在<使用Synchronized关键字同步类方法>一文中所介绍的直接用在方法的定义中.另外一种就是synchronized块.我们不仅可以通过synchronized块来同步一个对象变量.也可以使用synchronized块来同步类中的静态方法和非静态方法.synchronized块的语法如下: 复制代码 代码如下: public void method(){    - -    synchronized(表达式)    {        -

java多线程编程之使用Synchronized关键字同步类方法

复制代码 代码如下: public synchronized void run(){     } 从上面的代码可以看出,只要在void和public之间加上synchronized关键字,就可以使run方法同步,也就是说,对于同一个Java类的对象实例,run方法同时只能被一个线程调用,并当前的run执行完后,才能被其他的线程调用.即使当前线程执行到了run方法中的yield方法,也只是暂停了一下.由于其他线程无法执行run方法,因此,最终还是会由当前的线程来继续执行.先看看下面的代码:sych

浅谈Java多线程编程中Boolean常量的同步问题

在JAVA中通过synchronized语句可以实现多线程并发.使用同步代码块,JVM保证同一时间只有一个线程可以拥有某一对象的锁.锁机制实现了多个线程安全地对临界资源进行访问.   同步代码写法如下:   代码1: Object obj = new Object(); ... synchronized(obj) { //TODO: 访问临界资源 } JAVA的多线程总是充满陷阱,如果我们用Boolean作为被同步的对象,可能会出现以下两种情况:   一. 以为对一个对象加锁,实际同步的是不同对

java多线程编程学习(线程间通信)

一.概要 线程是操作系统中独立的个体,但这些个体如果不经过特殊的处理就不能成为一个整体,线程间的通信就是成为整体的必用方案之一.可以说,使线程进行通信后,系统之间的交互性会更强大,在大大提高cpu利用率的同时还会使程序员对各线程任务在处理过程中进行有效的把控和监督. 二.等待/通知机制 1."wait/notify"机制:等待/通知机制,wait使线程暂停运行,而notify 使暂停的线程继续运行.用一个厨师和服务员的交互来说明: (1) 服务员取到菜的时间取决于厨师,所以服务员就有&

java多线程编程实例

一.相关知识: Java多线程程序设计到的知识: (一)对同一个数量进行操作 (二)对同一个对象进行操作 (三)回调方法使用 (四)线程同步,死锁问题 (五)线程通信 等等 二.示例一:三个售票窗口同时出售20张票; 程序分析: 1.票数要使用同一个静态值 2.为保证不会出现卖出同一个票数,要java多线程同步锁. 设计思路: 1.创建一个站台类Station,继承Thread,重写run方法,在run方法里面执行售票操作!售票要使用同步锁:即有一个站台卖这张票时,其他站台要等这张票卖完! 2.

JAVA多线程编程实例详解

本文实例讲述了JAVA多线程编程.分享给大家供大家参考,具体如下: 进程是系统进行资源调度和分配的一个独立单位. 进程的特点 独立性:进程是系统中独立存在的实体,拥有自己的独立资源和私有空间.在没有经过进程本身允许的情况下,不能直接访问其他进程. 动态性:进程与程序的区别在于,前者是一个正在系统中活动的指令,而后者仅仅是一个静态的指令集合 并发性:多个进程可以在单个处理器上并发执行,而不受影响. 并发性和并行性的区别: 并行性:在同一时刻,有多条指令在多个处理器上同时执行(多个CPU) 并发性:

浅谈java多线程编程

一.多线程的优缺点 多线程的优点: 1)资源利用率更好 2)程序设计在某些情况下更简单 3)程序响应更快 多线程的代价: 1)设计更复杂 虽然有一些多线程应用程序比单线程的应用程序要简单,但其他的一般都更复杂.在多线程访问共享数据的时候,这部分代码需要特别的注意.线程之间的交互往往非常复杂.不正确的线程同步产生的错误非常难以被发现,并且重现以修复. 2)上下文切换的开销 当CPU从执行一个线程切换到执行另外一个线程的时候,它需要先存储当前线程的本地的数据,程序指针等,然后载入另一个线程的本地数据

Java多线程编程小实例模拟停车场系统

下面分享的是一个Java多线程模拟停车场系统的小实例(Java的应用还是很广泛的,哈哈),具体代码如下: Park类 public class Park { boolean []park=new boolean[3]; public boolean equals() { return true; } } Car: public class Car { private String number; private int position=0; public Car(String number)