JAVA线程间通信的几种方式

今天在群里面看到一个很有意思的面试题:
“编写两个线程,一个线程打印1~25,另一个线程打印字母A~Z,打印顺序为12A34B56C……5152Z,要求使用线程间的通信。”

这是一道非常好的面试题,非常能彰显被面者关于多线程的功力,一下子就勾起了我的兴趣。这里抛砖引玉,给出7种想到的解法。

通用代码:

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * Created by Edison Xu on 2017/3/2.
 */
public enum Helper {

    instance;

    private static final ExecutorService tPool = Executors.newFixedThreadPool(2);

    public static String[] buildNoArr(int max) {
        String[] noArr = new String[max];
        for(int i=0;i<max;i++){
            noArr[i] = Integer.toString(i+1);
        }
        return noArr;
    }

    public static String[] buildCharArr(int max) {
        String[] charArr = new String[max];
        int tmp = 65;
        for(int i=0;i<max;i++){
            charArr[i] = String.valueOf((char)(tmp+i));
        }
        return charArr;
    }

    public static void print(String... input){
        if(input==null)
            return;
        for(String each:input){
            System.out.print(each);
        }
    }

    public void run(Runnable r){
        tPool.submit(r);
    }

    public void shutdown(){
        tPool.shutdown();
    }

}

1. 第一种解法,包含多种小的不同实现方式,但一个共同点就是靠一个共享变量来做控制

a. 利用最基本的synchronizednotifywait

public class MethodOne {
    private final ThreadToGo threadToGo = new ThreadToGo();
    public Runnable newThreadOne() {
        final String[] inputArr = Helper.buildNoArr(52);
        return new Runnable() {
            private String[] arr = inputArr;
            public void run() {
                try {
                    for (int i = 0; i < arr.length; i=i+2) {
                        synchronized (threadToGo) {
                            while (threadToGo.value == 2)
                                threadToGo.wait();
                            Helper.print(arr[i], arr[i + 1]);
                            threadToGo.value = 2;
                            threadToGo.notify();
                        }
                    }
                } catch (InterruptedException e) {
                    System.out.println("Oops...");
                }
            }
        };
    }
    public Runnable newThreadTwo() {
        final String[] inputArr = Helper.buildCharArr(26);
        return new Runnable() {
            private String[] arr = inputArr;
            public void run() {
                try {
                    for (int i = 0; i < arr.length; i++) {
                        synchronized (threadToGo) {
                            while (threadToGo.value == 1)
                                threadToGo.wait();
                            Helper.print(arr[i]);
                            threadToGo.value = 1;
                            threadToGo.notify();
                        }
                    }
                } catch (InterruptedException e) {
                    System.out.println("Oops...");
                }
            }
        };
    }
    class ThreadToGo {
        int value = 1;
    }
    public static void main(String args[]) throws InterruptedException {
        MethodOne one = new MethodOne();
        Helper.instance.run(one.newThreadOne());
        Helper.instance.run(one.newThreadTwo());
        Helper.instance.shutdown();
    }
}

b. 利用LockCondition

public class MethodTwo {
    private Lock lock = new ReentrantLock(true);
    private Condition condition = lock.newCondition();
    private final ThreadToGo threadToGo = new ThreadToGo();
    public Runnable newThreadOne() {
        final String[] inputArr = Helper.buildNoArr(52);
        return new Runnable() {
            private String[] arr = inputArr;
            public void run() {
                for (int i = 0; i < arr.length; i=i+2) {
                    try {
                        lock.lock();
                        while(threadToGo.value == 2)
                            condition.await();
                        Helper.print(arr[i], arr[i + 1]);
                        threadToGo.value = 2;
                        condition.signal();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } finally {
                        lock.unlock();
                    }
                }
            }
        };
    }
    public Runnable newThreadTwo() {
        final String[] inputArr = Helper.buildCharArr(26);
        return new Runnable() {
            private String[] arr = inputArr;
            public void run() {
                for (int i = 0; i < arr.length; i++) {
                    try {
                        lock.lock();
                        while(threadToGo.value == 1)
                            condition.await();
                        Helper.print(arr[i]);
                        threadToGo.value = 1;
                        condition.signal();
                    } catch (Exception e) {
                        e.printStackTrace();
                    } finally {
                        lock.unlock();
                    }
                }
            }
        };
    }
    class ThreadToGo {
        int value = 1;
    }
    public static void main(String args[]) throws InterruptedException {
        MethodTwo two = new MethodTwo();
        Helper.instance.run(two.newThreadOne());
        Helper.instance.run(two.newThreadTwo());
        Helper.instance.shutdown();
    }
}
c. 利用volatile:
volatile 修饰的变量值直接存在main memory里面,子线程对该变量的读写直接写入main memory,而不是像其它变量一样在local thread里面产生一份copy。 volatile 能保证所修饰的变量对于多个线程可见性,即只要被修改,其它线程读到的一定是最新的值。
public class MethodThree {
    private volatile ThreadToGo threadToGo = new ThreadToGo();
    class ThreadToGo {
        int value = 1;
    }
    public Runnable newThreadOne() {
        final String[] inputArr = Helper.buildNoArr(52);
        return new Runnable() {
            private String[] arr = inputArr;
            public void run() {
                for (int i = 0; i < arr.length; i=i+2) {
                    while(threadToGo.value==2){}
                    Helper.print(arr[i], arr[i + 1]);
                    threadToGo.value=2;
                }
            }
        };
    }
    public Runnable newThreadTwo() {
        final String[] inputArr = Helper.buildCharArr(26);
        return new Runnable() {
            private String[] arr = inputArr;
            public void run() {
                for (int i = 0; i < arr.length; i++) {
                    while(threadToGo.value==1){}
                    Helper.print(arr[i]);
                    threadToGo.value=1;
                }
            }
        };
    }
    public static void main(String args[]) throws InterruptedException {
        MethodThree three = new MethodThree();
        Helper.instance.run(three.newThreadOne());
        Helper.instance.run(three.newThreadTwo());
        Helper.instance.shutdown();
    }
}

d. 利用AtomicInteger

public class MethodFive {
    private AtomicInteger threadToGo = new AtomicInteger(1);
    public Runnable newThreadOne() {
        final String[] inputArr = Helper.buildNoArr(52);
        return new Runnable() {
            private String[] arr = inputArr;
            public void run() {
                for (int i = 0; i < arr.length; i=i+2) {
                    while(threadToGo.get()==2){}
                    Helper.print(arr[i], arr[i + 1]);
                    threadToGo.set(2);
                }
            }
        };
    }
    public Runnable newThreadTwo() {
        final String[] inputArr = Helper.buildCharArr(26);
        return new Runnable() {
            private String[] arr = inputArr;
            public void run() {
                for (int i = 0; i < arr.length; i++) {
                    while(threadToGo.get()==1){}
                    Helper.print(arr[i]);
                    threadToGo.set(1);
                }
            }
        };
    }
    public static void main(String args[]) throws InterruptedException {
        MethodFive five = new MethodFive();
        Helper.instance.run(five.newThreadOne());
        Helper.instance.run(five.newThreadTwo());
        Helper.instance.shutdown();
    }
}

2. 第二种解法,是利用CyclicBarrierAPI;

CyclicBarrier可以实现让一组线程在全部到达Barrier时(执行await()),再一起同时执行,并且所有线程释放后,还能复用它,即为Cyclic。
CyclicBarrier类提供两个构造器:

public CyclicBarrier(int parties, Runnable barrierAction) {
}
public CyclicBarrier(int parties) {
}
public class MethodFour{
      private final CyclicBarrier barrier;
      private final List<String> list;
      public MethodFour() {
          list = Collections.synchronizedList(new ArrayList<String>());
          barrier = new CyclicBarrier(2,newBarrierAction());
      }
      public Runnable newThreadOne() {
          final String[] inputArr = Helper.buildNoArr(52);
          return new Runnable() {
              private String[] arr = inputArr;
              public void run() {
                  for (int i = 0, j=0; i < arr.length; i=i+2,j++) {
                      try {
                          list.add(arr[i]);
                          list.add(arr[i+1]);
                          barrier.await();
                      } catch (InterruptedException | BrokenBarrierException e) {
                          e.printStackTrace();
                      }
                  }
              }
          };
      }
      public Runnable newThreadTwo() {
          final String[] inputArr = Helper.buildCharArr(26);
          return new Runnable() {
              private String[] arr = inputArr;
              public void run() {
                  for (int i = 0; i < arr.length; i++) {
                      try {
                          list.add(arr[i]);
                          barrier.await();
                      } catch (InterruptedException | BrokenBarrierException e) {
                          e.printStackTrace();
                      }
                  }
              }
          };
      }
      private Runnable newBarrierAction(){
          return new Runnable() {
              @Override
              public void run() {
                  Collections.sort(list);
                  list.forEach(c->System.out.print(c));
                  list.clear();
              }
          };
      }
      public static void main(String args[]){
          MethodFour four = new MethodFour();
          Helper.instance.run(four.newThreadOne());
          Helper.instance.run(four.newThreadTwo());
          Helper.instance.shutdown();
      }
}
这里多说一点,这个API其实还是利用lockcondition,无非是多个线程去争抢CyclicBarrier的instance的lock罢了,最终barrierAction执行时,是在抢到CyclicBarrierinstance的那个线程上执行的。

3. 第三种解法,是利用PipedInputStreamAPI;

这里用流在两个线程间通信,但是Java中的Stream是单向的,所以在两个线程中分别建了一个input和output。这显然是一种很搓的方式,不过也算是一种通信方式吧……-_-T,执行的时候那种速度简直。。。请不要BS我。
public class MethodSix {
    private final PipedInputStream inputStream1;
    private final PipedOutputStream outputStream1;
    private final PipedInputStream inputStream2;
    private final PipedOutputStream outputStream2;
    private final byte[] MSG;
    public MethodSix() {
        inputStream1 = new PipedInputStream();
        outputStream1 = new PipedOutputStream();
        inputStream2 = new PipedInputStream();
        outputStream2 = new PipedOutputStream();
        MSG = "Go".getBytes();
        try {
            inputStream1.connect(outputStream2);
            inputStream2.connect(outputStream1);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    public void shutdown() throws IOException {
        inputStream1.close();
        inputStream2.close();
        outputStream1.close();
        outputStream2.close();
    }
    public Runnable newThreadOne() {
        final String[] inputArr = Helper.buildNoArr(52);
        return new Runnable() {
            private String[] arr = inputArr;
            private PipedInputStream in = inputStream1;
            private PipedOutputStream out = outputStream1;
            public void run() {
                for (int i = 0; i < arr.length; i=i+2) {
                    Helper.print(arr[i], arr[i + 1]);
                    try {
                        out.write(MSG);
                        byte[] inArr = new byte[2];
                        in.read(inArr);
                        while(true){
                            if("Go".equals(new String(inArr)))
                                break;
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        };
    }
    public Runnable newThreadTwo() {
        final String[] inputArr = Helper.buildCharArr(26);
        return new Runnable() {
            private String[] arr = inputArr;
            private PipedInputStream in = inputStream2;
            private PipedOutputStream out = outputStream2;
            public void run() {
                for (int i = 0; i < arr.length; i++) {
                    try {
                        byte[] inArr = new byte[2];
                        in.read(inArr);
                        while(true){
                            if("Go".equals(new String(inArr)))
                                break;
                        }
                        Helper.print(arr[i]);
                        out.write(MSG);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        };
    }
    public static void main(String args[]) throws IOException {
        MethodSix six = new MethodSix();
        Helper.instance.run(six.newThreadOne());
        Helper.instance.run(six.newThreadTwo());
        Helper.instance.shutdown();
        six.shutdown();
    }

4. 第四种解法,是利用BlockingQueue

顺便总结下BlockingQueue的一些内容。
BlockingQueue定义的常用方法如下:

  • add(Object):把Object加到BlockingQueue里,如果BlockingQueue可以容纳,则返回true,否则抛出异常。
  • offer(Object):表示如果可能的话,将Object加到BlockingQueue里,即如果BlockingQueue可以容纳,则返回true,否则返回false。
  • put(Object):把Object加到BlockingQueue里,如果BlockingQueue没有空间,则调用此方法的线程被阻断直到BlockingQueue里有空间再继续。
  • poll(time):获取并删除BlockingQueue里排在首位的对象,若不能立即取出,则可以等time参数规定的时间,取不到时返回null。当不传入time值时,立刻返回。
  • peek():立刻获取BlockingQueue里排在首位的对象,但不从队列里删除,如果队列为空,则返回null。
  • take():获取并删除BlockingQueue里排在首位的对象,若BlockingQueue为空,阻断进入等待状态直到BlockingQueue有新的对象被加入为止。

BlockingQueue有四个具体的实现类:

  • ArrayBlockingQueue:规定大小的BlockingQueue,其构造函数必须带一个int参数来指明其大小。其所含的对象是以FIFO(先入先出)顺序排序的。
  • LinkedBlockingQueue:大小不定的BlockingQueue,若其构造函数带一个规定大小的参数,生成的BlockingQueue有大小限制,若不带大小参数,所生成的BlockingQueue的大小由Integer.MAX_VALUE来决定。其所含的对象是以FIFO顺序排序的。
  • PriorityBlockingQueue:类似于LinkedBlockingQueue,但其所含对象的排序不是FIFO,而是依据对象的自然排序顺序或者是构造函数所带的Comparator决定的顺序。
  • SynchronousQueue:特殊的BlockingQueue,对其的操作必须是放和取交替完成的。

这里我用了两种玩法:

  • 一种是共享一个queue,根据peekpoll的不同来实现;
  • 第二种是两个queue,利用take()会自动阻塞来实现。
public class MethodSeven {
    private final LinkedBlockingQueue<String> queue = new LinkedBlockingQueue<>();
    public Runnable newThreadOne() {
        final String[] inputArr = Helper.buildNoArr(52);
        return new Runnable() {
            private String[] arr = inputArr;
            public void run() {
                for (int i = 0; i < arr.length; i=i+2) {
                    Helper.print(arr[i], arr[i + 1]);
                    queue.offer("TwoToGo");
                    while(!"OneToGo".equals(queue.peek())){}
                    queue.poll();
                }
            }
        };
    }
    public Runnable newThreadTwo() {
        final String[] inputArr = Helper.buildCharArr(26);
        return new Runnable() {
            private String[] arr = inputArr;
            public void run() {
                for (int i = 0; i < arr.length; i++) {
                    while(!"TwoToGo".equals(queue.peek())){}
                    queue.poll();
                    Helper.print(arr[i]);
                    queue.offer("OneToGo");
                }
            }
        };
    }
    private final LinkedBlockingQueue<String> queue1 = new LinkedBlockingQueue<>();
    private final LinkedBlockingQueue<String> queue2 = new LinkedBlockingQueue<>();
    public Runnable newThreadThree() {
        final String[] inputArr = Helper.buildNoArr(52);
        return new Runnable() {
            private String[] arr = inputArr;
            public void run() {
                for (int i = 0; i < arr.length; i=i+2) {
                    Helper.print(arr[i], arr[i + 1]);
                    try {
                        queue2.put("TwoToGo");
                        queue1.take();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        };
    }
    public Runnable newThreadFour() {
        final String[] inputArr = Helper.buildCharArr(26);
        return new Runnable() {
            private String[] arr = inputArr;
            public void run() {
                for (int i = 0; i < arr.length; i++) {
                    try {
                        queue2.take();
                        Helper.print(arr[i]);
                        queue1.put("OneToGo");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        };
    }
    public static void main(String args[]) throws InterruptedException {
        MethodSeven seven = new MethodSeven();
        Helper.instance.run(seven.newThreadOne());
        Helper.instance.run(seven.newThreadTwo());
        Thread.sleep(2000);
        System.out.println("");
        Helper.instance.run(seven.newThreadThree());
        Helper.instance.run(seven.newThreadFour());
        Helper.instance.shutdown();
    }

原文链接: http://edisonxu.org/2017/03/02/java-thread-communication.html






  • 27
    点赞
  • 179
    收藏
    觉得还不错? 一键收藏
  • 10
    评论
Java经典入门教pdf完整版Java私塾跟我学系列JAⅥ篇网址:htp:/www.lavass.Cn电话:010-86835215 3;Java是一种软件运行平台 3.1:什么是软件的运行平台 如同我们需要阳光、空气、水和食物才能正常存活一样,软件最终要能够运行,也需要 系列旳外部环境,来为软件的运行提供支持,而提供这些支持的就是运行平台。 3.2:Java是一种运行平台 Java本身提供Java软件所需要的运行环境,Java应用可运行在安装了JRE(Java Runtime environment)的机器上,所以我们说Java是一个运行平台。 JRE: Java Runtime Environment,Java运行环境。 4:Java是一种软件部署环境 4.1:什么是软件的部署 简单地讲,部署就是安装,就是把软件放置到相应的地方,并且进行相应的配置(一般 称作部署描述),让软件能够正常运行起来。 4.2:Java是一种软件部署环境 ava本身是一个开发的平台,开发后的Java序也是运行在Java平台上的。也就是说, 开发后的Java序也是部署在Java平台上的,这个尤其在后面学习JEE(Java的企业版) 的时候,体现更为明显 :Java能干什么 JaⅦa能做的事情很多,涉及到编领域的各个方面。 1:桌面级应用:尤其是需要跨平台的桌面级应用序。 先解释一下桌面级应用:简单的说就是主要功能都在我们本机上运行的序,比如 word、 excel等运行在木机上的应用就属」桌面应用。 2:企业级应用 先解释一下企业级应用:简单的说是大规模的应用,一般使用人数较多,数据量较大, 对系统的稳定性、安全性、可扩展性和可装配性等都有比较高的要求 这是目前Java应用最广泛的一个领域,几乎一枝独秀。包括各种行业应用,企业信息、 化,也包括电子政务等,领域涉及:办公自动化OA,客户关系管理CRM,人力资源HR, 企业资源计划ERP、知识管理KM、供应链管理SCM、企业设备管理系统EAM、产品生命 周期管理PLM、面向服务体系架构SOA、商业智能BⅠ、项日管理PM、营销管理、流管 理 Work Flow、财务管理…..等几乎所有你能想到的应用 3:嵌入式设备及消费类电子产品 包括无线手持设备、智能卡、通信终端、医疗设备、信息家电(如数字电视、机顶盒 电冰箱)、汽车电子没备等都是近年以来热门的Java应用领域,尤其是手机上的Java应用 序和Java游戏,更是普及。 4:除了上面提到的,Java还有很多功能:如进行数学运算、显示图形界面、进行网络操作、 进行数据库操作、进行文件的操作等等。 PDF文件使用" pdfFactory Pro"试用版本创建ww, fineprint,cn Java私塾跟我学系列JAⅥ篇网址:htp:/www.lavass.Cn电话:010-86835215 三:Java有什么 Java体系比较庞杂,功能繁多,这也导致很多人在自学Java的时候总是感觉无法建立 全面的知识体系,无法从整体上把握Java的原因。在这里我们先简单了解一下Java的版本 具体的Java体系知识结构,将在后面详细讲述。 Java分成三种版本,分别是Java标准版(JSE)、Java微缩版JME)和Java企业版(JE), 每一和版本都有自己的功能和应用方向。 1:Java标准版:JSE( Java standard Edition) JSE( Java Standard edition)是sun公司针对桌面开发以及低端商务计算解决方案而开 发的版本,例如:我们平常熟悉的 Application桌面应用序。这个版本是个基础,它也是 我们半常开发和使用最多的技术,Java的主要的技术将在这个版本中体现。本书主要讲的 就是JSF。 2:Java微缩版:JME( Java Micro edition) JE(Java, Micro edition)是对标准版JSE进行功能缩减后的版本,于199年6月 由 Sun Microsystems第一次推向Java团体。它是一项能更好满足Java开发人员不同需求 的广泛倡议的一部分。 Sun Microsystems将JM定义为“一种以广泛的消费性产品为目标 的高度优化的Java运行时环境,包括寻呼机、移动电话、可视电话、数字机顶盒和汽车导 航系统。” JE是致丿于消费产品和嵌入式设备的开发人员的最佳选择。尽管早期人们对它看好而 且Java开发人员团体中的热衷人土也不少,然而,J最近才开始从其影响更大的同属 品JEE和JSE的阴影中走出其不成熟期。 JME在开发面向內存有限的移动终端(例如寻呼机、移动电话)的应用时,显得尤其实用。 因为它是建立在操作系统之上的,使得应用的丌发无须考虑太多特殊的硬件配置类型或操作 系统。因此,开发商也无须为不同的终端建立特殊的应用,制造商也只需要简单地使它们的 操作平台可以攴持JM便可, 3:Java企业版:JEE( Java enterprise edition) JE( ava Enterprise edition)是·种利用Java平台来简化企业解决方案的开发、部 著和管理相关的复杂问题的体系结构。JE技术的基础就是核心Java平台或Java平台的标 准版,JEE不仅巩固了标淮版屮的诈多优点,例如“编写一次、随处运行”的特性、方便存 取数据库的 JDBC API、 CORBA技术以及能够在 Internet应用中保护数据的安全模式等等, 同时还提供了对BJB( Enterprise java beans)、 Java Servlets aPi、JSP( Java Server pages) 以及ⅫML技术的全面攴持。其最终目的就是成为一个能够使企业开发者大幅缩短投放市场时 间的体系结构。 JEE体系结构提供屮间层集成框架来满足无需太多费用而又需要高可用性、高可靠性以 及可扩展性的应用的需求。通过提供统的开发平台,J降低了开发多层应用的费用和复 杂性,同时提供对现有应用稈序集成强有力支持,完全支持 Entcrprise java beans,有良 好的向导攴持打包和部署应用,添加了目录攴持,增强了安全机制,提高了性能 JE是对标准版进行功能扩展,提供一系列功能,用来解决进行企业应用开发中所面临 的复杂的问题。具体的我们会放到后面JFE的课去讲。 4:三个版本之间的关系 JE几乎完全包含JSE的功能,然后在JSE的基础上添加了很多新的功能。 JME主要是JSE的功能子集,然后冉加上一部分额外添加的功能 PDF文件使用" pdfFactory Pro"试用版本创建ww, fineprint,cn Java私塾跟我学系列JAⅥ篇网址:htp:/www.lavass.Cn电话:010-86835215 如下图所示 JEE JSC JME Java的API类库之中有一组所谓的核心类( Corellas,即java.*),在核心类之外还有 所谓的扩允类( xtended Class,即 Javax.*)。根据对这两种类的支持度,进而区分出几 种不同的Java版本。 我们必须以 Java Standard Edition(JSE)作为基准,这个版本做了所有Java标准规格 之中所定义的核心类,也文持所有的Java基木类。JSE定位在客户端序的应用上 从JSE往外延伸,其外面为 Java Entcrprise edition(JEE),此版本除了支持所有的 标准核心类外,而且还增加了许多文持全业内部使用的扩充类,攴持 Servlet/JSP的 Javax. servlet.*类、支持 Enterprise Java Bean的 javax.ejb.*类。当然,JE必定支 持所有的Java基本类。JE定位在服务器端( server-side)序的应用上。 从J5E向内看,是 Java micro edition(JME),它所支持的只有核心类的了集合,在JME CLDC的规格之中,只支持java.lang.*、java.io.*、以及java.uti1.*这些类。此版本 也增加了一些攴持“微小装置”的扩充类,如 Javax. microedition.io.*类。然而,此版 木并不支持所有的Java基木类,就标准的 JMECLDO,也就是能在 Palmos上执行的 KwM( KVirtualmachine)来说,它就不支持属于浮点数( float、 double)的Java基本类。JME 定位在嵌入式系统的应用上 最里层,还有一个Java的 Smart card版本,原本在Java的文件之中并没有这样定义 但是将亡画在JⅦ内部是很合理的。因为 SmartCard版本具攴持java.lang*这个核心类, 比起JM所支持的核心类更少,但它也有属」自凵的扩充类,如 Javacard.*、 javacard. 这些类 Smartcard版本只支持 Boolean与Bytc这两种Java基本类,此版本定位在 SmartCard 的应用上 四:闲话 ava 1:Java历史 在上世纪90年代初,sun公司有一个叫做 Green的项目,目的是为家用消费电子广品 开发一个分布式代码系统,这样就可以对家用电器进行控制,和它们进行信息交流。詹姆 斯·高斯林( James Gosling)等人基于C+开发一种新的语言0 ak ( java的前身)。0ak是 种用于网络的精巧而安全的语言。Sun公司曾依此投标个交互式电视项目,但结果是被SGl 打败,Sun打算抛弃0ak。随着可联网的发展,Sun看到了0ak在计算机网络上的广阔应用 前景,于是改造0ak,在1995年5月以“Java”的名称正式发布,从此Java走上繁荣之路 当然提到Jaa历史,不得不提的一个故事就是Java的命名。开始“Oak”的命名是以 项目小组办公室外的树而得名,但是Oak商标被其他公司注册了,必须另外取一个名字 传说有天,几位Java成员组的会员正在讨论给这个新的语言取什么名字,当时他们止在 咖啡馆喝着Java(爪哇)咖啡,有一个人灵机一动说就叫Java怎杵,得到了其他人的赞同, 于是,Java这个名字就这样传开了。当然对于传说,了解一下就好了,不必过于认真 2:Java大事记 PDF文件使用" pdfFactory Pro"试用版本创建ww, fineprint,cn Java私塾跟我学系列JAⅥ篇网址:htp:/www.lavass.Cn电话:010-86835215 作为学习Java的人士,对Java历史上发生的大事件有一个了解是应该的。 JDK〔 Java Software Develop kit):Java软件开发工具包。JIK是Java的核心,包 括了Java运行坏境,一系刎Java开发工具和Java基础的类库。目前主流的JDK是Sun公 同发布的JDK,除了Sun之外,还有很多公司和组织都开发了自己的JD,例如IBM公司开 发的JD,BA公司的 Jrocket,还有GN组织丌发的JDK等等。 时间 事件 1995年5月23日 Java语言诞生 1996年1月 第个 JDK-JDK1.0诞生 1997年2月18日 JDK1.1发布 1998年12月8日 Java2企业平台J2EE发布 1999年6月 Sun发布JaⅤa三个版本:标准版J2SF,企业 版J2EE,微型版JME 2004年9月30日 Javase50发布 2006年12月 Java se60发布 3:Java特点 简单地说,Java具有如下特点:简单的、面向对象、平台无关、多线程、分布式、安全、 晑性能、可靠的、解释型、自动垃圾回收等特点。 这里只解释一下平台无关和分布式,其余的在后面会逐步接触到 3:平台无关 所谓平台无关指的是:用Java写的序不用修改就可在不同的软硬件平台上运行。这 烊就能实现同样的序既可以在 Windows下运行,到了Unix或者 Linux环境不用修改就直 接可以运行了。Java主要靠Java虚拟机(JⅧM)实现平台无关性 平台无关性就是一次编写,到处运行: Write Once, Run Anywhere 32:分布式 分布式指的是:软件由很多个可以独立执行的模块组成,这些模块被分布在多台计算机 上,可以同时运行,对外看起来还是个整体。也就是说,分布式能够把多台计算机集合起 来就像一台计算机一样,从而提供更好的性能 4:Java标准组织—Cp JCP( Java Community process)是一个开放的国际组织,成立于1995年,主要职能 是发展和更新Java技术规范、参考实现(RⅠ)、技术兼容包(TCK)。Java技术和JCP两者 的原创者都是SN计算机公司。组织成员可以提交JSR( Java Specification Requests), 通过讨论、认可、审核以后,将进入到下一版本的规范里面。 也就是说JCP是目前Java技术发展事实上的控制者和领导者。 五:Java如何做到让机器理解我们想要做的东西 用·个图来描述这个过会比较谷易理解: PDF文件使用" pdfFactory Pro"试用版本创建ww, fineprint,cn Java私塾跟我学系列JAⅥ篇网址:htp:/www.lavass.Cn电话:010-86835215 Runtime Compile Class loader Byte code Verifier Javac Hello.java Netwo Hello class Interpreter code Runtime generator/ Hardware 1:编写代码 首先把我们想要计算机做的事情,通过Java表达出来,写成Java文件,这个过就是 编写代码的过。如上图所示的 Hello java文件。 2:编译 写完Jaa代码后,机器并不认识我们写的Java代码,需要进行编译成为字节码,编译 后的文件叫做clas文件。如上图所示的 Hello, class文件。 3:类装载 Classloader 类裝载的功能是为执行序寻找和装载所需要的类 Classloader能够加强代似的安全性,主要方式是:把本机上的类和內络资源类相分离, 在调入类的时候进行检查,因而可以限制任何“特洛伊木马”的应用。 4:字节码(byte-code)校验 功能是对 class文件的代码进行校验,保证代码的安全。 Java软件代码在实际运行之前要经过几次测试。JWM将代码输入一个字节码校验器以 测试代码段格式并进行规则检査一一检査伪造指针、违反对象访问权限或试图改变对象类型 的非法代码。 注意-—所有源于网络的类文件都要经过字节码校验器 字节码校验器对序代码进冇四遍校验,这可以保证代码符合JⅧM规范并∏不破坏系统 的完整性。如果校验器在完成四遍校验后未返回出错信息,则下列各点可被保证 类符合JWⅦM规范的类文件格式 无访问限制异常 代码木引起操作数栈上溢或下溢 所有操作代码的参数类型将总是正确的 无非法数据转换发生,如将整数转换为对象引用 对象域访问是合法的 5:解释( Interpreter) 可是机器也不能认识clas文件,还需要被解释器进行解释,机器才能最终理解我们所 要表达的东西 PDF文件使用" pdfFactory Pro"试用版本创建ww, fineprint,cn Java私塾跟我学系列JAⅥ篇网址:htp:/www.lavass.Cn电话:010-86835215 6:运行 最后由运行环境屮的 Runtime对代码进行运行,真正实现我们想要机器完成的工作 7:说明 由上面的讲述,大家看到,Java通过个编译阶段和个运行阶段,来让机器最终坦解 我们想要它完成的工作,并按照我们的要求进行运行 在这两个阶段屮,需要我们去完成的就是编译阶段的工作,也就是说:我们需要把我们 想要札器完成的工作用Jaa语言表达出来,写成Java源文件,然后把源文件进行编译,形 成 class文件,最后就可以在JaⅦa运行环境中运行了。运行阶段的工作由Java平台自身提供 我们不需要做什么上作。 六:Java技术三大特性 1:虚拟机 Java虚拟机JM( Java Virtual Machine)在Java里面具有非常重要的地位,约 相当于前面学到的Java运行环境,虚拟机的基本功能如下: (1):通过 Classloader寻找和装载 class文件 (2):解释字节码成为指令并执行,提供 class文件的运行环境 (3):进行运行期间垃圾回收 4):提供与硬件交互的平台 Java虚拟杋是在真实札器中用软件模拟实现的—种想象机器。Jaa虚拟札代码被存储 在.clas文件中;每个文件都包含最多一个 public类。Java虚拟机规范为不同的硬件平台 提供了·种编译Java技术代码的规氾,该规范使Java软件独立于平台,因为编译是针对作 为虚拟机的“一般机器”而做。这个“一般机器”可用软件模拟并运行于各种现存的计算机 系统,也可用硬件米实现ε编译器在获取Java应用序的源代码后,将其生成字节码,它是 为J硎M生成的一种机器码指令。每个Java解释器,不管它是Java技术廾发工具,还是可运行 applets的Wcb浏览器,都可执行JVM。 JWM为下列各项做出了定义 指令集(相当于中央处理器[CP]) 寄存器 类文件格式 栈 垃圾收集堆 存储区 JⅧM的代码格式由紧缩冇效的字节码构成。由J硎M字节码编写的序必须保持适当的类 型约東。大部分类型检査是在编译时完成。任何从属的Java技术解释器必须能够运行仼何 含有类文件的序,这些类文件应符合Java虚拟机规范中所指定的类文件格式 1.1:虚拟机是Java平合无关的保障 正是因为有虚拟机这个中间层,Java才能够实现与平台无关。虚拟机就好比是一个Java 运行的基本平台,所有的Java序都运行在虚拟机上,如下图所示: PDF文件使用" pdfFactory Pro"试用版本创建ww, fineprint,cn Java私塾跟我学系列JAⅥ篇网址:htp:/www.lavass.Cn电话:010-86835215 Java序(*java文件) Javac编译 ava类文件(*cass文什) 平台无 被装载进入虚拟机 Java虚拟机 平台相关 解释执行字节码文件 Linux Windows 2:垃圾回收 2.1:什么是垃圾 在序运行的过中,存在被分配了的内存块不再被需要的情况,那么这些内存块对 序来讲就是垃圾。 产生了垃圾,自然就需要清理这些垃圾,更为重要的是需要把这些垃圾所占用的内存资 源,回收回来,加以再利用,从而节省资源,提高系统性能。 2.2:垃圾回收 不再需要的凵分配内存应取消分配(释放内存) 在其它语言中,取消分配是序员的责仟 Java语言提供了种系统级线程以跟踪内存分配 垃圾攻集 可检查和释放不再需要的内存 可自动完成上述工作 可在JM实现周期中,产生意想不到的变化 许多编语言都允许在序运行时动态分配内存,分配内存的过由于语言句法不同而 有所变化,但总是要将指针返回到内存的起始位置,当分配内存不再需要时(內存指针已溢 出范围),序或运行环境应释放内存 在C,C艹+或其它语言中,序员负责释放内存。有吋,这是件很困难的事情。因为 你并不总是事先知道内存应在何时被释放。当在系统中没有能够被分配的内有时,可导致 序瘫痪,这种序被称作具有内存漏洞 java语言解除∫序员释放内存的贲仼。它可提供一种系统级线程以跟踪每一次 内存的分配情況。在Java虚拟机的空闲周期,垃圾收集线程检查并释敚那些可被释放的内 存。垃圾收集在Java技术序的生命周期中自动进行,它解除了释放内存的要求,这样能 够有效避免内存漏洞和内存泄露(内冇泄露就是序运行期间,所占用的内存一直往上涨, 很容易造成系统资源耗尽而降低性能或崩溃) PDF文件使用" pdfFactory Pro"试用版本创建ww, fineprint,cn Java私塾跟我学系列JAⅥ篇网址:htp:/www.lavass.Cn电话:010-86835215 2.3:提示 (1):在Java里面,垃圾回收是一个自动的系统行为,序员不能控制垃圾回收的功 能和行为。比如垃圾回收什么时候开始,什么时侯结束,还有到底哪些资源需要回收等,都 是序员不能控制的 (2):有些跟垃圾回收相关的方法,比如: System. gc(,记住“点,调用这些方法, 仅仅是在通知垃圾回收序,全于垃圾回收序运个运行,仆么时候运行,都是尢法控制的。 (3):序员可以通过设置对象为nul(后面会讲到)来标示某个对象不再被需要了, 这只是表示这个对象可以被回收了,并不是马上被回收 3:代码安全 Java如何保证编写的代仍是安全可靠的呢? (1):第一关:编写的代码首先要被编译成为 class文件,如果代码写得有问题,编译期间 就会发现,然后提示有编译错误,无法编译通过。 (2):第二关:通过编译关后,在类装载的时候,还会进行类装载检查,把本机上的类和网 络资源类相分离,在调入类的时候进行检査,因而可以限制仁何“特洛伊木马”的应用 (3):第三关:类装载后,在运行前,还会进行字节校验,以判断你的序是安全的。 (4):第四关:如果你的序在网终上运行,还有沙箱( Sand box)的保护,什么是沙箱呢? 就是如果你的序没有汏得授权,只能在沙箱限定的范围内运行,是不能够访问本地資源的, 从而保证安全性。 如下图所示: Runtime Compile Java class Loader匿载检查 Hello world. java Network Byte code verifier 节码校验 Javac 编译检耷 Hello World class Interpreter Runtime Hardware 学习到这甲,大家应该对Java有了一定的了解了。现在是否想要看看Java序究竟什 么样子呢?是不是想要体会一下如何开发Java序呢?下面我们先米看看如何构建JSE的 环境,这是进行Java序开发的第一步, PDF文件使用" pdfFactory Pro"试用版本创建ww, fineprint,cn
第8章 多线程 第1页 本章概述 本章的学习目标 主要内容 Java序设计案例教-第8章-多线程全文共36页,当前为第1页。 本章概述 前面我们所开发的序大多是单线程的,即一个序只有一条从头到尾的执行路线。然而,现实世界中的很多事务都是有多种途径同时运作的,例如:服务器可能需要同时处理多个客户机的请求,这就需要有多个线程同时在工作。多线程使得系统资源并不是由某个执行体独占,而是由多个执行单元共同拥有,轮换使用。正确使用多线程可以消除系统的瓶颈问题,提高整个应用系统的性能。本章将详细介绍Java语言的多线程技术。通过本章的学习,读者应该理解线程和进的区别,掌握Java多线程技术,了解线程的同步和线程间通信等内容。 第2页 Java序设计案例教-第8章-多线程全文共36页,当前为第2页。 本章的学习目标 了解进线程的基本概念和区别 掌握创建线程的两种方法 掌握线程同步的概念和方法 了解线程的优先级 掌握线程间通信的方法 第3页 Java序设计案例教-第8章-多线程全文共36页,当前为第3页。 第4页 主要内容 8.1 Java线程模型 8.2 创建线程 8.3 同步与线程间通信 8.4 获取线程状态 8.5 本章小结 8.6 思考和练习 Java序设计案例教-第8章-多线程全文共36页,当前为第4页。 8.1 Java线程模型 Java多线程(multithreaded programming)提供了内置支持。多线程序包含同时运行的两个或多个部分。这种序的每一部分被称为一个线程,并且每个线程定义了单独的执行路径。因此,多线程是特殊形式的多任务处理。 第5页 Java序设计案例教-第8章-多线程全文共36页,当前为第5页。 进本质上是正在执行的序。在UNIX操作系统中,每个应用序的执行都在操作系统内核中登记一个进标志,操作系统根据分配的标志对应用序的执行进行调度和系统资源分配。每个进都有自己的内存单元,进之间是互相独立的,一个进一般不允许访问其他进的内存空间,因此,进通信非常困难。 基于进的多任务处理就是允许计算机同时运行两个或更多个序的特性。例如,基于进的多任务处理可以在运行Java编译器的同时使用文本编辑器或浏览网站。在基于进的多任务处理中,序是调度序能够调度的最小代码单元。 第6页 Java序设计案例教-第8章-多线程全文共36页,当前为第6页。 线程 线程是比进更小的执行单位。如果将进概念一分为二,则进中的系统资源,可以看成是一个静态的对象;而序代码的执行位置,可以看成一个动态对象,这个动态的部分就是线程。进在执行过中拥有独立的内存单元,而多个线程共享内存,线程之间的通信比较容易解决,从而极大地提高了序的运行效率。 在基于线程的多任务环境中,最小的可调度代码单元是线程,这意味着单个序可以同时执行两个或更多个任务。例如,文本编辑器可以在打印的同时格式化文本,只要这两个动作是通过两个独立的线程执行即可。 第7页 Java序设计案例教-第8章-多线程全文共36页,当前为第7页。 进线程的区别 进线程的区别可以总结为如下几点: 一个序至少有一个进,一个进至少有一个线程线程是进的一个实体,是CPU调度和分派的基本单位,它是比进更小的能独立运行的基本单位。 线程在执行过中与进也是有区别的,每个独立的线程有一个序运行的入口、顺序执行序列和序的出口;但是线程不能够独立执行,必须依存在应用序中,由应用序提供多个线程的执行控制。 一个线程可以创建和撤销另一个线程,同一进中的多个线程之间可以并发执行。 第8页 Java序设计案例教-第8章-多线程全文共36页,当前为第8页。 Java中的线程 Java运行时系统在许多方面依赖于线程,并且所有类库在设计时都考虑了多线程。事实上,Java通过利用线程使得整个环境能够异步执行。 在过去几年,多核系统已经变得很普遍了。当然,单核系统仍然在广泛使用。Java多线程系统在这两种类型的系统中都可以工作。在单核系统中,并发执行的线程共享CPU,每个线程得到一片CPU时钟周期。所以,在单核系统中,两个或更多个线程不是真正同时运行的,但是空闲时间被利用了。然而,在多核系统中,两个或多个线程可能是真正同步执行的。 第9页 Java序设计案例教-第8章-多线程全文共36页,当前为第9页。 线程的状态 线程有多种状态:线程可以处于运行(running)状态,只要获得CPU时间就准备运行。运行的线程可以被挂起(suspended),这会临时停止线程的活动。挂起的线程可以被恢复(resumed),从而允许线程从停止处恢复执行。当等待
线程间通信有多种方式可以实现。其中一种方式是使用共享变量,在不同的线程之间共享数据。通过读写共享变量来进行线程间的通信。这种方式需要注意线程安全性和同步机制,以避免数据竞争和不一致的问题。 另一种方式是使用线程同步机制,例如使用锁机制来实现线程间的通信。锁可以用来保护共享资源,确保在一个线程访问共享资源的同时,其他线程不能同时访问该资源。通过获取锁和释放锁的方式来实现线程间的同步和通信。 还有一种方式是使用线程间的阻塞和唤醒机制。通过在线程中使用等待和通知的方式来实现线程间的通信。当一个线程需要等待某些条件满足时,它可以调用等待方法进入等待状态,而其他线程在某些条件满足时可以调用通知方法来唤醒等待的线程。这种方式适用于线程间存在依赖关系的情况下。 此外,还可以使用信号量、管道、消息队列等方式来实现线程间的通信。每种方式都有其适用的场景和特点,根据具体的需求选择合适的方式进行线程间的通信。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [Java线程间的通信方式详解](https://download.csdn.net/download/weixin_38649315/12797496)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* *3* [线程间的通信方法](https://blog.csdn.net/liubing8609/article/details/81700284)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值