博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Java 多线程程序设计
阅读量:7080 次
发布时间:2019-06-28

本文共 5928 字,大约阅读时间需要 19 分钟。

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

 

 

 

转载地址:http://uppml.baihongyu.com/

你可能感兴趣的文章
windows 安装服务
查看>>
MySQL常用简单小命令
查看>>
ERROR: child process failed, exited with error number 100 mongodb报错
查看>>
epoll 使用小结
查看>>
c#调用存储过程实现登录界面
查看>>
测试类。。。重写篇
查看>>
二进制
查看>>
入侵式与非入侵式JavaScript
查看>>
ny47 过河问题
查看>>
神奇高效的Linux命令行
查看>>
阿里云老后台
查看>>
mikadonic-文件访问控制设置(深层次的权限控制setfacl)
查看>>
这是标题,用来测试博客皮肤标题
查看>>
AJax详解
查看>>
从一段时间段中获取所有日期
查看>>
Java中如何设置表格处于不可编辑状态
查看>>
Java JTable视图窗口滚动并定位到某一行
查看>>
课堂练习
查看>>
HTML学习成果 制作一个空白简历
查看>>
使用mybatis自带工具,自动生成表对应domain、mapper.xml以及dao
查看>>