操作系统实践报告

本文探讨了操作系统的线程概念,详细讲解了Java中创建线程的三种方式,包括Thread类和Runnable接口的使用。接着,介绍了线程同步的重要性,详细阐述了`synchronized`关键字和同步块的应用,并通过生产者消费者问题展示了线程同步的实战。最后,通过具体的Java代码示例,总结了多线程编程中的关键知识点,强调了良好的代码设计规划和编程规范的重要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1 线程的创建与启动

1.1 进程与线程

1.1.1什么是线程

答:线程是独立运行,调度的基本单位。

1.1.2进程与线程的区别

答:进程与线程是相互联系又是有区别的

(1)一个进程包含多个线程,所以说它们之间是相互联系的

(2)调度性:在传统操作系统中,进程是独立运行,调度,资源分配的基本单位,而在线程操作系统中,线程是调度,独立运行的基本单位,而进程是资源分配的基本单位。

(3)并发性:进程和线程都可以并发执行

(4)资源性;进程不管在传统os还是线程的os中都是资源分配的基本单位,线程不能实现资源的分配

1.2 Java中的Thread和Runnable类

1.2.1 Java中创建多线程

答:一个Thread类的对象对应一个线程,创建一个Runnable接口的对象并使用Thread对象启动它,Thread t= new Thread (obj)(obj代表Runnable的对象),用start()函数启动线程。

1.3 三种创建线程的办法

1.3.1实验方法一:

package org.yang;

/**

 * Runnable的实现类,是线程执行的主体。

 * run函数是入口

 *

 */

class MyR implementsRunnable{

   private String msg;

   public MyR(String msg) {

      this.msg = msg;

   }

   //线程的入口

   @Override

   publicvoid run() {

      while(true) {

         try {          

            System.out.println(msg);

            Thread.sleep(1000);

         }catch(InterruptedException e) {

            e.printStackTrace();

            break;

         }

      }

   }

}

publicclassTestThread {

publicstaticvoidmain(String[] args){

      //创建了线程

      Threadthread1= newThread(newMyR("hello"));

      thread1.start(); //启动了线程

      Threadthread2= newThread(newMyR("wuwu"));

      thread2.start(); //启动了线程

   }

}

1.3.2实验方法二

package org.yang;

publicclassTestThread2 {

publicstaticvoidmain(String[] args){

      TestThread2testThread2=newTestThread2();

      //匿名信  匿名类   引用就是指针

      Runnablerunnable= newRunnable() {

         @Override

         publicvoid run() {

            while(true) {

                try {          

                   System.out.println("haha");

                   Thread.sleep(1000);

                }catch(InterruptedException e) {

                   e.printStackTrace();

                   break;

                }

            }

         }

      };

      Threadthread= newThread(runnable);

      thread.start();

   }

}

1.3.3实验方法三

package org.yang;

publicclassTestThread3 {

publicstaticvoidmain(String[] args){

      new Thread(new Runnable() {

         @Override

         publicvoid run() {

            System.out.println("haha");

         }

      }).start();

      // lamda 表达式 java 1.8+

      new Thread(()->{

         System.out.println("haha");      

      }).start();

   }

}

2 线程简单同步(同步块)

2.1 同步的概念和必要性

2.1.1同步的定义

答:两个进程使用同一资源时,一个进程必须停下来等待另一个进程完成后才能使用这一资源。

2.1.2必要性

答:保证进程之间互斥的使用资源,没有同步机制会导致资源不合理利用。

2.2 synchronize关键字和同步块

2.2.1synchronize关键字:在多线程环境下的应用synchronized 关键字,它包括两种用法:synchronized 方法和 synchr onized synchronized方法。synchronized 方法控制对类成员变量的访问:每个类实例对应一把锁,每个 synchronized 方法都必须获得调用该方法的类实例的锁方能执行,否则所属线程阻塞,方法一旦执行,就独占该锁,直到从该方法返回时才将锁释放,此后被阻塞的线程方能获得该锁,重新进入可执行状态。

2.2.2同步块:同步块用来标记方法或者代码块是同步的。Java同步块用来避免竞争。

2.3 实例

关键字实验:

package org.yang;

import java.util.ArrayList;

publicclassTestSync {

   staticint  c = 0;

   static Object lock = new Object(); //(1) 随便建立了一个变量,作为锁变量

   publicstaticvoid main(String[] args) {

      Thread[]threads= newThread[1000];

      for(inti=0;i<1000;i++) {

         finalintindex = i//(4) 建立了一个final变量,放半在lamba中使用

         threads[i] = new Thread(()->{

         synchronized (lock) { //(2) 创建一个同步块,需要一个锁。

      System.out.println("thread "+index+"enter");//5)输出

                inta  = c//获取c的值

                a++; //将值加一

                try {//模拟复杂处理过程

                   Thread.sleep((long) (Math.random()*10));

                }catch(InterruptedException e) {

                   // TODO Auto-generated catch block

                   e.printStackTrace();

                }

                c=a; //存回去

                System.out.println("thread "+index+"leave");//(6)输出

            }//3这是块的终结

         });

         threads[i].start(); //线程开始

      }

      for(inti=0;i<1000;i++) {

         try {

            threads[i].join(); //等待 thread i 完成

         }catch(InterruptedException e) {

            // TODO Auto-generated catch block

            e.printStackTrace();

         }

      }//循环后,所有的线程都完成了

      System.out.print("c="+c); //输出c的结果

   }

}

 

3 生产者消费者问题

3.1 问题表述

生产者消费者问题(Producer-consumer problem),也称有限缓冲问题(Bounded-buffer problem),是一个多线程同步问题的经典案例。该问题描述了两个共享固定大小缓冲区的线程--即所谓的"生产者"和"消费者"--在实际运行时会发生的问题。生产者的主要作用是生成一定量的数据放到缓冲区中,然后重复此过程。与此同时,消费者也在缓冲区消耗这些数据。

该问题的关键就是要保证生产者不会在缓冲区满时加入数据,消费者也不会在缓冲区中空时消耗数据。

3.2 实现思路

要解决该问题,就必须让生产者在缓冲区满时休眠(要么干脆就放弃数据),等到下次消费者消耗缓冲区中的数据的时候,生产者才能被唤醒,开始往缓冲区添加数据。同样,也可以让消费者在缓冲区空时进入休眠,等到生产者往缓冲区添加数据之后,再唤醒消费者。通常采用进程间通信的方法解决该问题,常用的方法有信号灯法等。如果解决方法不够完善,则容易出现死锁的情况。出现死锁时,两个线程都会陷入休眠,等待对方唤醒自己。该问题也能被推广到多个生产者和消费者的情形。

3.3 Java实现该问题的代码

3.3.1 Queue队列代码:

packageorg.yang;

importjava.util.LinkedList;

importjava.util.concurrent.locks.Condition;

importjava.util.concurrent.locks.Lock;

importjava.util.concurrent.locks.ReentrantLock;

 

publicclass Queue { //队列

  

   private Lock lock =new ReentrantLook();  //锁

   private Condition fullC;  //信号量

   private Condition emptyC;  //信号量

  

   private int size;

   public Queue(int size) {

      this.size = size;

      //(2)为信号量附初值

      fullC = lock.newCondition();

      emptyC = lock.newCondition();

   }

   LinkedList<Integer> list = newLinkedList<Integer>();

   /**

    * 入队

    *@return

    */

   public boolean EnQueue(int data) {

      lock.lock();//上锁

      while(list.size()>=size) {

         fullC.await();

      } catch (InterruptedException e) {

         lock.unlock();

         return false;

      }

   }

      list.addLast(data);

      emptyC.signalAll();

      lock.unlock();

      return true;

   }

   /**

    * 出队

    *@return

    */

   public int DeQueue() {

      lock.lock();//线上所

      while(list.size() == 0) {

         try {

            emptyC.await();

         }catch (InterruptedException e) {

            lock.unlock();

            return -1;//失败返回

         }

      }

      int r = list.removeFirst();//获取队列头部

      fullC.signalAll();//唤醒所有生产者

      lock.unlock();//解锁

      return r;

   }

        

   public boolean isFull() {

      return list.size()>=size;

   }

   public boolean isEmpty() {

      return list.size()==0;

   }

}

 

3.3.2 producer实验:

package org.yang;

import java.util.concurrent.locks.Condition;

import java.util.concurrent.locks.Lock;

/**

 * 生产者

 */

publicclassProducer implementsRunnable {

   private Queue q;

   private Condition isFull; //信号量如果满了则等待

   private Condition isEmpty; //信号量如果空了则等待

   private Lock lock;

   privateintindex; //生产者的编号

   public Producer(intindex,Queue q,Lock lock,Condition isFull,Condition isEmpty) {

      this.index = index;

      this.q= q;

      this.isFull = isFull;

      this.isEmpty = isEmpty;

      this.lock = lock;

   }

   @Override

   publicvoid run() {

      lock.lock();

      if(q.isFull()) {

         try {

            isFull.await(); //如果队列为慢,则等待

         }catch(InterruptedException e) {

            return;

         }       

      }

      //生产并入队

      inta = (int) (Math.random()*1000);

      q.EnQueue(a);

      //生产完后

      isEmpty.signalAll();//把消费者唤醒。

      lock.unlock();

   }

}

3.4 测试

3.4.1实验TestPC:

package org.yang;

publicclassTestPC {

   static Queue queue = new Queue(5);

   publicstaticvoid main(String[] args) {

      //创建三个生产者

      for(inti=0;i<3;i++) {

         finalintindex = i;

         new Thread(()->{

            intdate =(int)(Math.random()*1000);

            System.out.printf("thread %d want to EnQueue %d\n",index,date);

            queue.EnQueue(date);

            System.out.printf("thread %d EnQueue %d Success\n",index,date);

            sleep();//随机休息一段时间

         }

      }).start();

   }

   //创建消费者

   for(inti = 0;i<3;i++) {

      finalintindex = i;

      new Thread(()->{

         while(true) {

            System.out.printf("customer thread %d want to DnQueue %d\n",index);

            intdata = queue.DeQueue();

            System.out.printf("customer thread %d Finish DnQueue %dSuccess\n",index,date);

            sleep2();//随机休息一段时间

         }

      }).start();

   }

      }

   }

   //sleep随机时间

   publicstaticvoid sleep() {

      int t = (int)(Math.random()*100);

      try {

         Thread.sleep(t);

      }catch(InterruptedException e) {

         e.printStackTrace();

      }

   }

3.4.2 当生产能力超出消费能力时的表现

publicstaticvoidsleep() {

      int t = (int)(Math.random()*100);

      try {

         Thread.sleep1(t);

      }catch(InterruptedException e) {

         e.printStackTrace();

      }

   }

 

3.4.3 当生产能力弱于消费能力时的表现

System.out.printf("customer thread %d want to DnQueue %d\n",index);

            intdata = queue.DeQueue();

            System.out.printf("customer thread %d Finish DnQueue %dSuccess\n",index,date);

            sleep2();//随机休息一段时间

         }

      }).start();

   }

      }

4 总结

      整个程序的设计过程中出现了很多问题,比如在设计过程中该设计几个类,各个类该完成什 么样的功能,如何实现各个功能,一个类如何调用另一类中的变量、如何处理事件等等许多问题,在代码设计过程中,对JAVA 面向的思想有了更深入的了解,程序中用到了很多操作系统的思想, 操作,信号量的互斥实现,线程的概念,对这些概念理解的更加透彻。

      实验过程的同时认识到了自己的不足,在以后的学习中应更加努力。在开始写代码前,一定要有个整体的规划,如设计几个类,每个类完成什么样的功能,应该设计几个变量,几个方法,各个方法能完成的功能等等。在写代码时,遵循 Java 编程规范。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值