zoukankan      html  css  js  c++  java
  • Java 多线程程序设计

    课程  Java面向对象程序设计 

    一、实验目的

    掌握多线程程序设计

    二、实验环境

    1微型计算机一台 

    2WINDOWS操作系统,Java SDK,Eclipse开发环境

    三、实验内容 

    1、Java有两种实现多线程的方式:通过Runnable接口、通过Thread直接实现,请掌握这两种实现方式,并编写示例程序。

    2、多线程是并发执行的,交替占有cpu执行,请编写示例程序,并观察输出结果。

    3、编写程序实现生产者消费者问题代码,采用线程同步机制来解决多线程共享冲突问题。

    四、实验步骤和结果

      1、Java有两种实现多线程的方式:通过Runnable接口、通过Thread直接实现,请掌握这两种实现方式,并编写示例程序。

     (1)通过Runnable接口实现多线程:

          定义实现java.lang.Runnable接口的类,Runnable接口中只有一个run()方法,用来定义线程运行体。代码(MyRunner.java)如下:

    package CreateThread;
    import java.util.TreeMap;
    
    public class MyRunner implements Runnable{  //实现Runnable接口
         public void run(){
             for (int i = 0; i <20; i++) {  //要在线程中执行的代码
                System.out.println("MyRunner:"+i);
            }
         }
        public static void main(String[] args) {
            Thread thread1=new Thread(new MyRunner());
            thread1.start();
        }
    }

    程序执行结果截图如下:

    (2)通过Thread实现多线程:将类定义为Thread类的子类,并重写run()方法。代码(MyThread.java)如下

    package CreateThread;
    public class MyThread extends Thread {
        public void run(){
            for (int i = 0; i < 10; i++) {
                System.out.println("MyThread:"+i);
            }
        }
        public static void main(String[] args) {
            Thread thread2=new MyThread();
            thread2.start();
        }
    }

     2、多线程是并发执行的,交替占有cpu执行,编写示例程序如下,主线程先执行,然后启动两个新线程,但这两个新线程并没有立刻得到执行,而是统一由JVM根据时间片来调度,调度到哪个线程,就由哪个线程执行片刻。并且这两个新线程应该是交替显示结果,如果没有交替显示,可能是机器性能较好,在单位时间片内已经完成了循环运算,我们可以将循环次数更改为2000或更高值再行测试。多运行几次,每次的输出结果都不可能相同,说明JVM调度线程的执行顺序是随机的。

    测试代码(ConcurrentExecutionThread.java)如下所示:

    package CreateThread;
    public class ConcurrentExecutionThread {
        public static void main(String[] args) {
            System.out.println("主线程开始执行");
            Thread thread1=new Thread(new MyRunner());
            thread1.start();
            System.out.println("启动一个新线程(thread1)...");
            
            Thread thread2=new MyThread();
            thread2.start();
            System.out.println("启动另一个新线程(thread2)...");
            System.out.println("主线程执行完毕");
        }
    }

    程序运行结果为:

     

    3、编写程序实现生产者消费者问题代码,采用线程同步机制来解决多线程共享冲突问题。

    (1)编写产品类

         生产者要生产产品,消费者要消费产品,所以产品要提供一个含有标识的id属性,另外要在生产或消费时打印产品的详细内容,所以需要重写toString()方法,产品类(Products.java)代码如下:

    package SynchronizedThread;
    //编写产品类
    public class Products {
         int id;
        public Products() {
            super();
        }
        public Products(int id) {
            super();
            this.id = id;
        }
        public String toString() {
            return "Products [id=" + id + "]";
        }
    }

      (2)编写店员类(Clerk.java)

      店员一次只能持有10份产品,如果生产者生产的产品多于10份,则会让当前的正在此对象上操作的线程等待。一个线程访问addProduct方法时,它已经拿到这个锁了,当遇到产品大于10份时,它会阻塞。而且,只有锁定对象后才可以用wait方法,否则会出错。并且,notify与wait一般是一一对应的。

    package SynchronizedThread;
    //编写店员类
    public class Clerk {
        int index=0;//默认为0个产品
        Products[] pro=new Products[10];
        //生产者生产出来的产品交给店员
        public synchronized void addProduct(Products pd){
            while (index==pro.length) {
                try {
                    this.wait();//产品已满,请稍后再生产
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            this.notify();//通知等待区的消费者可以取产品了
            pro[index]=pd;
            index++;
        }
        //消费者从店员处取产品
        public synchronized Products getProduct(){
            while (index==0) {
                try {
                    this.wait();  //缺货,请稍后再取。
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            this.notify();//通知等待区的生产者可以生产产品了
            index--;
            return pro[index];
        }
    }

     (3)编写生产者线程类(Producer.java)

        生产者与店员有关系,所以店员类被当做属性引入进来,通过构造器完成初始化店员类对象的任务。为了凸显效果,每生产一个产品后让线程睡眠一会儿。

    package SynchronizedThread;
    //编写生产者线程类
    public class Producer implements Runnable { //生产者线程要执行的任务
        private Clerk clerk;
        public Producer() {
            super();
        }
        public Producer(Clerk clerk) {
            super();
            this.clerk = clerk;
        }
        public void run() {
            System.out.println("生产者开始生产产品");
            for (int i = 0; i <15; i++) {//注意此处的循环次数一定要大于pro数组的长度(10)
                Products pd=new Products(i);
                clerk.addProduct(pd);//生产产品
                System.out.println("生产了:"+pd);
                try {  //睡眠时间用随机产生的值来设置
                    Thread.sleep((int)(Math.random()*10)*100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    (4)编写消费者线程类(Consumer.java)

       消费者与店员也有关系,所以店员类被当做属性引入进来,同样通过构造器完成初始化店员类对象的任务。

    package SynchronizedThread;
    //编写消费者线程类
    public class Consumer implements Runnable {//消费者线程要执行的任务
        private Clerk clerk;
        public Consumer() {
            super();
        }
        public Consumer(Clerk clerk) {
            super();
            this.clerk = clerk;
        }
        public void run() {
            System.out.println("消费者开始取走产品");
            for (int i = 0; i <15; i++) {//注意此处的循环次数一定要大于pro数组的长度(10)
            //取产品
            Products pd=clerk.getProduct();
            System.out.println("消费了:"+pd);
            try {           //睡眠时间用随机产生的值来设置
                Thread.sleep((int)(Math.random()*10)*100);
            } catch (InterruptedException e) {
                    e.printStackTrace();
            }
            }
        } 
    }

     (5)编写生产者消费者问题的测试类( ProducerAndConsumerTest.java)

         创建生产者和消费者线程,然后分别调度线程。

    package SynchronizedThread;
    //生产者消费者问题测试
    public class ProducerAndConsumerTest {
        public static void main(String[] args) {
            Clerk clerk=new Clerk();
            Thread producerThread=new Thread(new Producer(clerk));//创建生产者线程
            Thread consumerThread=new Thread(new Consumer(clerk));//创建消费者线程
            producerThread.start();
            consumerThread.start();
        }
    }

      (6)运行程序,在控制台得到的输出结果如下所示:

    五、实验总结

      1.本次实验按时按量完成。

      2.线程和进程的区别:

       在操作系统中能同时运行多个任务(程序)叫做多进程;在同一应用程序中多条执行路径并发执行叫做多线程。

    (1)每个进程都有独立的代码和数据空间(进程上下文),进程间的切换开销大。

    (2)同一进程内的多个线程共享相同的代码和数据空间,每个线程有独立的运行栈和程序计数器(PC),线程间的切换开销小。

    通常,在以下情况中可能要使用到多线程:

    (1)程序需要同时执行两个或多个任务。

    (2)程序需要实现一些需要等待的任务时,如用户输入、文件读写操作、网络操作、搜索等。

    (3)需要一些后台运行的程序时。

      3.线程同步的方法:为了共享区域的安全,可以通过关键字synchronized来加保护伞,以保证数据的安全。synchronized主要应用于同步代码块和同步方法中。

    (1) 同步方法:synchronized放在方法声明中,表示整个方法为同步方法。

    (2) 同步代码块:把线程体内执行的方法中会操作到共享数据的语句封装在{}之内,然后用synchronized放在某个对象前面修饰这个代码块。

    如果一个线程调用了synchronized修饰的方法,它就能够保证该方法在执行完毕前不会被另一个线程打断,这种运行机制叫作同步线程机制。

      4.在生产者线程类与消费者线程类中,设置让线程睡眠的时间如果是一样的,运行结果中会出现“生产一个消费一个,生产与消费时成对出现的“的这个不符合现实的现象。这时需要修改线程的睡眠时间,把睡眠时间用随机产生的值来设置。这样之后,再次运行程序,可以看到有时候生产了多个产品后,消费者才开始消费。

     

     

  • 相关阅读:
    今天开始用 VSU 2010
    Visual Studio 2010 模型设计工具 基本应用
    Asp.Net访问Oracle 数据库 执行SQL语句和调用存储过程
    Enterprise Library 4.1 Security Block 快速使用图文笔记
    解决“System.Data.OracleClient 需要 Oracle 客户端软件 8.1.7 或更高版本。”(图)
    一个Oracle存储过程示例
    Enterprise Library 4.1 Application Settings 快速使用图文笔记
    Oracle 10g for Windows 简体中文版的安装过程
    Oracle 11g for Windows 简体中文版的安装过程
    Oracle 9i 数据库 创建数据库 Net 配置 创建表 SQL查询 创建存储过程 (图)
  • 原文地址:https://www.cnblogs.com/shenxiaolin/p/4938733.html
Copyright © 2011-2022 走看看