操作系统编程实践课程总结

1 线程的创建与启动

1.1 进程与线程

进程:是指系统中能够独立运行并作为资源分配的基本单位,由一组机器指令、数据和堆栈组成的。是一个能够独立运行的活动实体。

线程:是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源。一个线程可以创建和撤消另一个线程,同一进程中的多个线程之间可以并发执行。线程有就绪、阻塞和运行三种基本状态。

区别:①因为进程拥有独立的堆栈空间和数据段,所以每当启动一个新的进程必须分配给它独立的地址空间,建立众多的数据表来维护它的代码段、堆栈段和数据段,这对于多进程来说十分“奢侈”,系统开销比较大,而线程不一样,线程拥有独立的堆栈空间,但是共享数据段,它们彼此之间使用相同的地址空间,共享大部分数据,比进程更节俭,开销比较小,切换速度也比进程快,效率高,但是正由于进程之间独立的特点,使得进程安全性比较高,也因为进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程只是一个进程中的不同执行路径。一个线程死掉就等于整个进程死掉。

②体现在通信机制上面,正因为进程之间互不干扰,相互独立,进程的通信机制相对很复杂,譬如管道,信号,消息队列,共享内存,套接字等通信机制,而线程由于共享数据段所以通信机制很方便。。

③属于同一个进程的所有线程共享该进程的所有资源,包括文件描述符。而不同过的进程相互独立。

④线程又称为轻量级进程,进程有进程控制块,线程有线程控制块;

⑤线程必定也只能属于一个进程,而进程可以拥有多个线程而且至少拥有一个线程;

⑥体现在程序结构上,举一个简明易懂的列子:当我们使用进程的时候,我们不自主的使用if else嵌套来判断pid,使得程序结构繁琐,但是当我们使用线程的时候,基本上可以甩掉它,当然程序内部执行功能单元需要使用的时候还是要使用,所以线程对程序结构的改善有很大帮助。

1.2 Java中的Thread和Runnable类

Thread类

Thread类实现了Runnable接口,在Thread类中,有一些比较关键的属性,比如name是表示Thread的名字,可以通过Thread类的构造器中的参数来指定线程名字,priority表示线程的优先级(最大值为10,最小值为1,默认值为5),daemon表示线程是否是守护线程,target表示要执行的任务。

下面是Thread类中常用的方法:

以下是关系到线程运行状态的几个方法:

1)start方法

start()用来启动一个线程,当调用start方法后,系统才会开启一个新的线程来执行用户定义的子任务,在这个过程中,会为相应的线程分配需要的资源。

2)run方法

run()方法是不需要用户来调用的,当通过start方法启动一个线程之后,当线程获得了CPU执行时间,便进入run方法体去执行具体的任务。注意,继承Thread类必须重写run方法,在run方法中定义具体要执行的任务。

3)sleep方法

sleep方法有两个重载版本:

sleep(long millis)    //参数为毫秒

sleep(long millis,int nanoseconds)    //第一参数为毫秒,第二个参数为纳秒

sleep相当于让线程睡眠,交出CPU,让CPU去执行其他的任务。

但是有一点要非常注意,sleep方法不会释放锁,也就是说如果当前线程持有对某个对象的锁,则即使调用sleep方法,其他线程也无法访问这个对象。

Runnable接口

通过继承Thread类实现多线程,但是这种方式有一定的局限性。因为在java中只支持单继承,一个类一旦继承了某个父类就无法再继承Thread类,比如学生类Student继承了person类,就无法再继承Thread类创建的线程。为了克服这种弊端,Thread类提供了另外一种构造方法Thread(Runnable target),其中Runnable是一个接口,它只有一个run()方法。当通过Thread(Runnable target)构造方法创建一个线程对象时,只需该方法传递一个实现了Runnable接口的实例对象,这样创建的线程将调用实现了Runnable接口中的run()方法作为运行代码,二不需要调用Thread类中的run()方法。

 

1.   package test;

2.  

3.   public class example {

4.        public static void main(String[] args){

5.           MyThread myThread=new MyThread();

6.           Threadthread=new Thread(myThread);

7.           thread.start();

8.           while(true)

9.           {

10.             System.out.println("Main方法在运行");

11.          }

12.        }

13.  }

14. 

15.  class MyThread implements Runnable{

16.     public void run(){

17.       while(true){

18.          System.out.println("MyThread类的run()方法在运行");

19.       }

20.     }

21.  }

区别:java不允许多继承,因此实现了Runnable接口的类可以再继承其他类。
Thread也可以资源共享啊,为什么呢,因为Thread本来就是实现了Runnable,包含Runnable的功能是很正常的啊!!至于两者的真正区别最主要的就是一个是继承,一个是实现;其他还有一些面向对象的思想,Runnable就相当于一个作业,而Thread才是真正的处理线程,我们需要的只是定义这个作业,然后将作业交给线程去处理,这样就达到了松耦合,也符合面向对象里面组合的使用,另外也节省了函数开销,继承Thread的同时,不仅拥有了作业的方法run(),还继承了其他所有的方法。综合来看,用Runnable比Thread好的多。

1.3 三种创建线程的办法

继承Thread类创建线程类

(1)定义Thread类的子类,并重写该类的run方法,该run方法的方法体就代表了线程要完成的任务。因此把run()方法称为执行体。

(2)创建Thread子类的实例,即创建了线程对象。

(3)调用线程对象的start()方法来启动该线程。

1.           package com.thread;

2.            

3.           public classFirstThreadTest extends Thread{

4.                 int i = 0;

5.                 //重写run方法,run方法的方法体就是现场执行体

6.                 public void run()

7.                 {

8.                       for(;i<100;i++){

9.                       System.out.println(getName()+"  "+i);

10.                    

11.                     }

12.               }

13.               public static void main(String[] args)

14.               {

15.                     for(int i = 0;i< 100;i++)

16.                     {

17.                          System.out.println(Thread.currentThread().getName()+"  : "+i);

18.                           if(i==20)

19.                           {

20.                                 new FirstThreadTest().start();

21.                                 new FirstThreadTest().start();

22.                           }

23.                     }

24.               }

25.          

26.         }



上述代码中Thread.currentThread()方法返回当前正在执行的线程对象。GetName()方法返回调用该方法的线程的名字。

二、通过Runnable接口创建线程类

(1)定义runnable接口的实现类,并重写该接口的run()方法,该run()方法的方法体同样是该线程的线程执行体。

(2)创建 Runnable实现类的实例,并依此实例作为Thread的target来创建Thread对象,该Thread对象才是真正的线程对象。

(3)调用线程对象的start()方法来启动该线程。

示例代码为:

1.           package com.thread;

2.            

3.           public classRunnableThreadTest implements Runnable

4.           {

5.            

6.                 private int i;

7.                 public void run()

8.                 {

9.                       for(i = 0;i <100;i++)

10.                     {

11.                          System.out.println(Thread.currentThread().getName()+""+i);

12.                     }

13.               }

14.               public static void main(String[] args)

15.               {

16.                     for(int i = 0;i < 100;i++)

17.                     {

18.                          System.out.println(Thread.currentThread().getName()+""+i);

19.                           if(i==20)

20.                           {

21.                                 RunnableThreadTest rtt = newRunnableThreadTest();

22.                                 new Thread(rtt,"新线程1").start();

23.                                 new Thread(rtt,"新线程2").start();

24.                           }

25.                     }

26.          

27.               }

28.          

29.         }



三、通过Callable和Future创建线程

(1)创建Callable接口的实现类,并实现call()方法,该call()方法将作为线程执行体,并且有返回值。

(2)创建Callable实现类的实例,使用FutureTask类来包装Callable对象,该FutureTask对象封装了该Callable对象的call()方法的返回值。

(3)使用FutureTask对象作为Thread对象的target创建并启动新线程。

(4)调用FutureTask对象的get()方法来获得子线程执行结束后的返回值

实例代码:

1.           package com.thread;

2.            

3.           importjava.util.concurrent.Callable;

4.           import java.util.concurrent.ExecutionException;

5.           importjava.util.concurrent.FutureTask;

6.            

7.           public classCallableThreadTest implements Callable<Integer>

8.           {

9.            

10.               public static void main(String[] args)

11.               {

12.                     CallableThreadTest ctt = new CallableThreadTest();

13.                     FutureTask<Integer> ft = newFutureTask<>(ctt);

14.                     for(int i = 0;i < 100;i++)

15.                     {

16.                          System.out.println(Thread.currentThread().getName()+"的循环变量i的值"+i);

17.                           if(i==20)

18.                           {

19.                                 new Thread(ft,"有返回值的线程").start();

20.                           }

21.                     }

22.                     try

23.                     {

24.                           System.out.println("子线程的返回值:"+ft.get());

25.                     } catch (InterruptedException e)

26.                     {

27.                           e.printStackTrace();

28.                     } catch (ExecutionException e)

29.                     {

30.                           e.printStackTrace();

31.                     }

32.          

33.               }

34.          

35.               @Override

36.               public Integer call() throws Exception

37.               {

38.                     int i = 0;

39.                     for(;i<100;i++)

40.                     {

41.                          System.out.println(Thread.currentThread().getName()+""+i);

42.                     }

43.                     return i;

44.               }

45.          

46.         }



二、创建线程的三种方式的对比

采用实现Runnable、Callable接口的方式创见多线程时,优势是:

线程类只是实现了Runnable接口或Callable接口,还可以继承其他类。

在这种方式下,多个线程可以共享同一个target对象,所以非常适合多个相同线程来处理同一份资源的情况,从而可以将CPU、代码和数据分开,形成清晰的模型,较好地体现了面向对象的思想。

劣势是:

编程稍微复杂,如果要访问当前线程,则必须使用Thread.currentThread()方法。

使用继承Thread类的方式创建多线程时优势是:

编写简单,如果需要访问当前线程,则无需使用Thread.currentThread()方法,直接使用this即可获得当前线程。

劣势是:

线程类已经继承了Thread类,所以不能再继承其他父类。

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

 2.1 同步的概念和必要性

概念:线程同步就是线程排队。同步就是排队。线程同步的目的就是避免线程"同步"执行。
必要性:不管是多线程还是多进程,涉及到共享相同的内存时,需要确保好同步问题。对线程来说,需要确保每个线程看到一致的数据视图。如果每个线程使用的变量都是其他线程不会读取和修改的,那么就不存在一致性问题,同样的,如果变量是只读的,多个线程同时读取该变量也不会有一致性问题。但是如果其中的某个线程去改变该变量,其他线程也能读取或者修改的时候,我们就需要对这些线程进行同步,确保他们访问变量的存储内容时不会访问到无效的值。当线程修改变量的时候,其他线程在读取这个变量时可能会看到一个不一致的值,在变量修改时间多于一个存储器访问周期的处理器结构中,当存储器读与写这两个周期交叉,不一致就会出现。

 

2.2 synchronize关键字和同步块

在进行多线程的开发中经常会遇到线程安全问题,那么我们应该如何避免呢.

使用synchronize关键字就可以很好的实现线程安全的程序.

在什么情况下会遇到非线程安全问题呢?比如:

线程A                          线程B

1.线程A在数据库中查询存票,发现票C可以卖出
2.线程A接受用户订票请求,准备出票.
3. 这时切换到了线程B执行
4. 线程B在数据库中查询存票,发现票C可以卖出                
5. 线程B将票卖了出去 
6.切换到线程A执行,线程A卖了一张已经卖出的票

这时就会出现非线程安全问题,原因是两个线程同时访问了一个共同的对象并修改了他.所以我们应该在某个线程正在执行一个不可分割的部分时,其它线程不能同时执行这一部分.

synchronize到底锁住了什么?

对于同步块,synchornized获取的是参数中的对象的锁:
 synchornized(obj){
   //...............
  }
  线程执行到这里时,首先要获取obj这个实例的锁,如果没有获取到,线程只能等待.如果多个线程执行到这里,只能有一个线程获取obj的锁,然后执行{}中的语句,所以,obj对象的作用范围不同,控制程序不同.

对于对象和方法,调用synchronize的方法一定是排队运行的.

2.3 实例

package zheng;

 

importcom.sun.media.jfxmedia.events.NewFrameEvent;

 

public class testhread {

 

static int c=0;

 

             staticObject lock = new Object();

 

             publicstatic void main(String[] args) {

 

                             Thread[]thread =new Thread[1000];             

 

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

 

                                           finalint index = i;

 

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

 

                                                          synchronized(lock){

 

                                                                           System.out.println("thread"+index+"enter");

 

                                                                         

 

                                                          inta = c;//获取c的值

 

                                                           a++;//将值加一

 

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

 

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

 

                                                          }

 

                                                          catch(InterruptedExceptione) {

 

                                                                          e.printStackTrace();

 

                                                          }

 

                                                          c=a;//存回去

 

                                                          System.out.println("thread"+index+"leave");

 

                                                          }

 

                                            });

 

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

 

                             }

 

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

 

                                           try{

 

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

 

                                           }catch(InterruptedExceptione) {

 

                                                          e.printStackTrace();

 

                                            }

 

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

 

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

 

 

 

             }

 

}

3 生产者消费者问题

3.1 问题表述

 问题描述:一群生产者进程在生产产品,并将这些产品提供给消费者去消费。为了使生产者进程与消费者进程能够并发进行,在两者之间设置一个具有n个缓冲区的缓冲池,生产者进程将产品放入一个缓冲区中;消费者可以从一个缓冲区取走产品去消费。尽管所有的生产者进程和消费者进程是以异方式运行,但它们必须保持同步:当一个缓冲区为空时不允许消费者去取走产品,当一个缓冲区满时也不允许生产者去存入产品。

3.2 实现思路

我们这里利用一个一个数组buffer来表示这个n个缓冲区的缓冲池,用输入指针和输出指针+1来表示在缓冲池中存入或取出一个产品。由于这里的缓冲池是循环缓冲的,故应把in和out表示成:in = ( in +1 ) % n (或把out表示为 out = ( out +1 ) % n )当( in +1) % n= out的时候说明缓冲池满,in = out 则说明缓冲池空。在这里还要引入一个整型的变量counter(初始值0),每当在缓冲区存入或取走一个产品时,counter +1或-1。那么问题的关键就是,把这个counter作为临界资源处理,即令生产者进程和消费者进程互斥的访问它。

3.3实现代码

packageorg.zheng; 


import java.util.LinkedList;


importjava.util.concurrent.locks.Condition; 


importjava.util.concurrent.locks.Lock; 


import java.util.concurrent.locks.ReentrantLock; 


public class Queue { //
队列


//(1)
建立一个锁,俩信号量 


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


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 = new LinkedList<Integer>();


/** 


 * 入队 


 * @return 


 */ 


public boolean EnQueue(int data) { 


lock.lock(); //上锁 


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


try { 


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.4 测试

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

当生产能力超出消费能力时,生产者线程生产物品时没有空缓冲区可用,生产者线程必须等待消费者线程释放出一个空缓冲区。

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

生产能力弱于消费能力时,消费者线程消费物品,如果没有满的缓冲区,那么消费者线程将被阻塞,直到新的物品被生产者线程生产出来。

4 总结

这次课程设计中,不仅培养了独立思考、动手操作的能力,在各种其它能 力上也都有了提高。更重要的是,在实验课上,我们学会了很多学习的方法。而这是以后 最实用的,真的是受益匪浅。要面对社会的挑战,只有不断的学习、实践,再学习、再实 践。这对于我们的将来也有很大的帮助。回顾起此课程设计,至今我仍感慨颇多,从理论 到实践,在这日子里,可以说得是苦多于甜,但是可以学到很多很多的东西,同时不仅 可以巩固了以前所学过的知识,而且学到了很多在书本上所没有学到过的知识。通过这次 课程设计使我懂得了理论与实际相结合是很重要的,只有理论知识是远远不够的,只有把 所学的理论知识与实践相结合起来,从理论中得出结论,才能真正为社会服务,从而提高 自己的实际动手能力和独立思考的能力。在设计的过程中遇到问题,可以说得是困难重 重,但可喜的是最终都得到了解决。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
系统根据B/S,即所谓的电脑浏览器/网络服务器方式,运用Java技术性,挑选MySQL作为后台系统。系统主要包含对客服聊天管理、字典表管理、公告信息管理、金融工具管理、金融工具收藏管理、金融工具银行卡管理、借款管理、理财产品管理、理财产品收藏管理、理财产品银行卡管理、理财银行卡信息管理、银行卡管理、存款管理、银行卡记录管理、取款管理、转账管理、用户管理、员工管理等功能模块。 文中重点介绍了银行管理的专业技术发展背景和发展状况,随后遵照软件传统式研发流程,最先挑选适用思维和语言软件开发平台,依据需求分析报告模块和设计数据库结构,再根据系统功能模块的设计制作系统功能模块图、流程表和E-R图。随后设计架构以及编写代码,并实现系统能模块。最终基本完成系统检测和功能测试。结果显示,该系统能够实现所需要的作用,工作状态没有明显缺陷。 系统登录功能是程序必不可少的功能,在登录页面必填的数据有两项,一项就是账号,另一项数据就是密码,当管理员正确填写并提交这二者数据之后,管理员就可以进入系统后台功能操作区。进入银行卡列表,管理员可以进行查看列表、模糊搜索以及相关维护等操作。用户进入系统可以查看公告和模糊搜索公告信息、也可以进行公告维护操作。理财产品管理页面,管理员可以进行查看列表、模糊搜索以及相关维护等操作。产品类型管理页面,此页面提供给管理员的功能有:新增产品类型,修改产品类型,删除产品类型。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值