计算机操作系统编程实践

1 线程的创建与启动
1.1 进程与线程
主要描述进程线程概念和差别。

概念:

进程是并发执行的程序在执行过程中分配和管理资源的基本单位。进程有5种基本状态,分别是初始态、执行态、等待状态、就绪状态、终止状态。

线程是进程的一部分,如果一个进程没有线程则可以被看作单线程。

差别:

 进程的执行过程是线性的,尽管中间会发生中断或暂停,但该进程所拥有的资源只为该线状执行过程服务。一旦发生进程上下文切换,这些资源都是要被保护起来的。这是进程宏观上的执行过程。

线程的改变只代表了 CPU 执行过程的改变,而没有发生进程所拥有的资源变化了 CPU 之外,计算机内的软硬件资源的分配与线程无关

1.2 Java中的Thread和Runnable类

Thread类本质上是实现了Runnable接口的一个实例,代表一个线程的实例。启动线程的唯一方法就是通过Thread类的start()实例方法。start()方法是一个native方法,它将启动一个新线程,并执行run()方法。这种方式实现多线程很简单,创建名为MyR的类,作为Runnable接口的实现类并复写run()方法,用while循环控制输出,就可以启动新线程并执行自己定义的run()方法

1.3 三种创建线程的办法

1、继承Thread类创建线程类(1)定义Thread类的子类,并重写该类的run方法,该run方法的方法体就代表了线程要完成的任务。因此把run()方法称为执行体。(2)创建Thread子类的实例,即创建了线程对象。(3)调用线程对象的start()方法来启动该线程。

2、通过Runnable接口创建线程类(1)定义runnable接口的实现类,并重写该接口的run()方法,该run()方法的方法体同样是该线程的线程执行体。(2)创建 Runnable实现类的实例,并以此实例作为Thread的target来创建Thread对象,该Thread对象才是真正的线程对象。(3)调用线程对象的start()方法来启动该线程。

3、通过Callable和Future创建线程(1)创建Callable接口的实现类,并实现call()方法,该call()方法将作为线程执行体,并且有返回值(2)创建Callable实现类的实例,使用FutureTask类来包装Callable对象,该FutureTask对象封装了该Callable对象的call()方法的返回值。(FutureTask是一个包装器,它通过接受Callable来创建,它同时实现了Future和Runnable接口。)(3)使用FutureTask对象作为Thread对象的target创建并启动新线程。(4)调用FutureTask对象的get()方法来获得子线程执行结束后的返回值

 

代码1

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

 * run函数是入口

 *

 */

classMyR implements Runnable{

private String msg;

public MyR(String msg) {

     this.msg = msg;

}

//线程的入口

@Override

public void run() {

     while(true) {

        try {            

            System.out.println(msg);

            Thread.sleep(1000);

        } catch (InterruptedException e) {

            e.printStackTrace();

            break;

        }

     }

}

}

publicclass TestThread {

 

public static void main(String[] args) {

     //创建了线程

     Thread thread1 = new Thread(newMyR("hello"));

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

    

 

     Thread thread2 = new Thread(newMyR("wuwu"));

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

}

 

}

 

 

代码二

public class TestThread2 {

 

    publicstatic void main(String[] args) {

       TestThread2testThread2 =new TestThread2();

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

       Runnablerunnable = new Runnable() {

          

           @Override

           publicvoid run() {

              while(true){

                  try{            

                     System.out.println("haha");

                     Thread.sleep(1000);

                  }catch (InterruptedException e) {

                     e.printStackTrace();

                     break;

                  }

              }

           }

       };

      

    Threadthread = new Thread(runnable);

       thread.start();

    }

 }

代码三

publicclass TestThread3 {

 public static void main(String[] args) {

     new Thread(new Runnable() {

       

        @Override

        public void run() {

            System.out.println("haha");

        }

     }).start();

    

     // lamda 表达式 java 1.8+

     new Thread(()->{

        System.out.println("haha");       

     }).start();

}

}

2 简程简单同步(同步块)
2.1 同步的概念和必要性
 为什么要同步,可以举例说明

同步的概念:同步就是协同步调,按预定的先后次序进行运行。进程、线程同步,可理解为进程或线程A和B一块配合,A执行到一定程度时要依靠B的某个结果,于是停下来,示意B运行;B依言执行,再将结果给A;A再继续操作。

同步的必要性:同步可以保证在同一时刻,只有一个线程可以进入临界区执行代码,因为当我们有多个线程要同时访问一个变量或对象时,如果这些线程中既有读又有写操作时,就会导致变量值或对象的状态出现混乱,从而导致程序异常。

1、如果一个银行账户同时被两个线程操作,一个取100块,一个存钱100块。假设账户原本有0块,如果取钱线程和存钱线程同时发生,则会提示存钱成功,取钱不成功。

2、当同学们在网络教学平台选课时。若当时已经没有课,一人选课一人退课,则退课的同学能成功退课,选课的同学则无课可选。

2.2 synchronize关键字和同步块

关键字的作用域有两种:

(1)是某个对象实例内,synchronized aMethod(){}可以防止多个线程同时访问这个对象的synchronized方法

(2)是某个类的范围,synchronized static aStaticMethod{}防止多个线程同时访问这个类中的synchronized static 方法。

2.3 实例

TestSync.java

 

package org.yang;

 

import java.util.ArrayList;

 public class TestSync {

    staticint  c = 0;

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

    publicstatic void main(String[] args) {

       Thread[]threads = new Thread[1000];

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

           finalint index = 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 问题表述
有一个有限缓冲区和两个线程:生产者和消费者。他们分别把产品放入缓冲区和从缓冲区中拿走产品。当一个生产者在缓冲区满时必须等待,当一个消费者在缓冲区空时也必须等待

3.2 实现思路
用自然语言描述这个问题的解决思路

创建一个队列,用来当做生产者和消费者的缓冲区,这个缓冲区应该是线程内唯一的。创建两个线程,一个是生产者线程一个是消费者线程,当生产者生产了物品就放到这个缓冲区中,而消费者时时刻刻可以从这个缓冲区中去拿物品,只要是有就可以拿,生产者只要是缓冲区不满就可以往里面放。

 
3.3 Java实现该问题的代码

package ly;

 

importjavax.net.ssl.SSLException;

 

public classTestPC {

       static Queue queue=new Queue(5);

 

       public static void main(String[] args) {

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

                     final int index=i;

                     new Thread(()-> {

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

                            System.out.printf("thread%d want to EnQueue %d\n"+data);

                            queue.EnQueue(data);

                            System.out.printf("thread%d  EnQueue %d Success\n"+data);

                            sleep();

                     }).start();

              }

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

                            final int index=i;

                            new Thread(()-> {

                                   while(true) {

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

                                   int data=queue.DeQueue();

                                   System.out.printf("customerthread %d  DnQueue %dSuccess\n",index,data);

                                   sleep();

                                   }

                            }).start();

                     }

                    

              }

             

      

       public static void sleep() {

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

              try {

                     Thread.sleep(t);

              } catch (InterruptedException e) {

                     // TODO Auto-generatedcatch block

                     e.printStackTrace();

              }

      }

}

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

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

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

4 总结

通过今天慢慢一天的实践课程,让我对计算机操作系统有了一个不一样的认识。对路程和线程也有了比较深刻的认识。学会了用synchronized解决线程同步问题,认识到路程和线程的区别以及他们之间的联系,当一个线程访问object的一个synchronized(this)同步代码块时,其他线程对object中所有其它synchronized(this)同步代码块的访问将被阻塞。通过对生产者与消费者的问题,让我体会到同步对于生产者消费者的重要意义。虽然一天的学习比较辛苦,在实验过程中遇到了很多困难,但是我们迎难而上,克服了困难,挑战了自己。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值