操作系统实践作业

本文详细介绍了操作系统中的进程与线程,包括它们的定义、特性及区别。此外,文章探讨了Java中线程的创建,通过继承Thread类和实现Runnable接口两种方式,并提供了示例代码。还讨论了线程同步的重要性,展示了synchronized关键字和同步块的使用。最后,通过生产者消费者问题的案例,阐述了多线程同步的实践应用。
摘要由CSDN通过智能技术生成

课程总结

 

1.1进程与线程的介绍与差别:

进程的介绍:

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

进程有三个状态:就绪状态、执行状态、等待状态【或称阻塞状态】;进程由父进程建立,系统中所有的进程形成一种进程树的层次体系;挂起命令可由进程自己和其他进程发出,但是解除挂起命令只能由其他进程发出。

线程的介绍:

线程:线程是进程中的一个实体,作为系统调度和分派的基本单位。Linux下的线程看作轻量级进程。

进程的特征:

1.动态性:进程的实质是程序的一次执行过程,进程是动态产生,动态消亡的。

2.并发性:任何进程都可以同其他进程一起并发执行。

3.独立性:进程是一个能独立运行的基本单位,同时也是系统分配资源和调度的独立单位。

4.异步性:由于进程间的相互制约,使进程具有执行的间断性,即进程按各自独立的、不可预知的速度向前推进。

 

线程和进程的区别:

1.线程是进程内的一个相对独立的可执行的单元。若把进程称为任务的话,那么线程则是应用中的一个子任务的执行。

2.由于线程是被调度的基本单元,而进程不是调度单元。所以,每个进程在创建时,至少需要同时为该进程创建一个线程。即进程中至少要有一个或一个以上的线程,否则该进程无法被调度执行。

3.进程是被分给并拥有资源的基本单元。同一进程内的多个线程共享该进程的资源,但线程并不拥有资源,只是使用他们。

4.线程是操作系统中基本调度单元,因此线程中应包含有调度所需要的必要信息,且在生命周期中有状态的变化。

5.由于共享资源【包括数据和文件】,所以线程间需要通信和同步机制,且需要时线程可以创建其他线程,但线程间不存在父子关系。

1.2 Java中的Thread和Runnable类

Java中线程的创建有两种方式:

1、 通过继承Thread类,重写Thread的run()方法,将线程运行的逻辑放在其中。

2、 通过实现Runnable接口,实例化Thread类。

Runnable 接口只有一个方法run(),我们声明自己的类实现 Runnable 接口并提供这一方法,将我们的线程代码写入其中,就完成了这一部分的任务。但是 Runnable 接口并没有任何对线程的支持,我们还必须创建 Thread 类的实例,这一点通过 Thread 类的构造函数public Thread(Runnable target);来实现。

3、继承Thread实现的模式是定义多个线程,各自完成各自的任务.

4、实现Runnable实现的模式是定义多个线程,实现一个任务。

 

Thread类与Runnable接口的关系

public interfaceRunnable {

public abstractvoid run();

}

 

public classThread implements Runnable {

/* What will berun. */

private Runnabletarget;

/**

* Causes thisthread to begin execution; the Java Virtual Machine

* calls the<code>run</code> method of this thread.

*/

publicsynchronized void start() {......}

@Override

public void run(){

if (target !=null) {

target.run();

}

}

}

Thread类与Runnable接口都位于java.lang包中。从上面我们可以看出,Runnable接口中只定义了run()方法,Thread类实现了Runnable 接口并重写了run()方法。当调用Thread 类的start()方法时,实际上Java虚拟机就去调用Thread类的run()方法,而Thread 类的run()方法中最终调用的是Runnable类型对象的run()方法。

1.3三种创建线程的方法

代码一:

package T1;

/**

*javadoc 将文档嵌入到程序中

* @author Administrator

*

*/

class MyR implements Runnable{

private String msg;

public MyR(String msg)

{

this.msg=msg;

}

//线程入口

@Override

publicvoid run() {

while(true) {

try {

Thread.sleep(1000);//每隔一秒调用一次

System.out.println(msg);

} catch (InterruptedException e) {

e.printStackTrace();

break;

}

}

}

}

publicclass test_1 {

 

publicstaticvoid main(String[] args) {

//创建线程

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

thread1.start();

Thread thread2 = new Thread(new MyR("66666"));

thread2.start();

    }

}

 

代码二:

package T1;

import java.text.BreakIterator;

publicclass TestThread2 {

publicstaticvoid main(String[] args) {

    TestThread2 testThread2 =new TestThread2();

//匿名类

Runnable runnable = new Runnable()

@Override

publicvoid run() {

while(true) {

try {

Thread.sleep(1000);

System.out.println("hehe");

} catch(InterruptedException e) {

e.printStackTrace();

break;

}

}

}

       };

 

Thread thread = new Thread(runnable);

thread.start();

    }

}

 

 

代码三:

package T1;

import javax.xml.stream.events.StartDocument;

publicclass TestThread3 {

publicstaticvoid main(String[] args) {

new Thread (new Runnable() {

@Override

publicvoid run() {

while(true) {

try {

Thread.sleep(1000);

System.out.println("haha");

} catch(InterruptedException e) {

e.printStackTrace();

break;

}

    }

           }

    }).start();

//lamda表达式 java 1.8+

new Thread(()->{ System.out.println("abc");

}).start();

}

}

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

2.1 同步的概念和必要性

同步:

java允许多线程并发控制,当多个线程同时操作一个可共享的资源变量时(如数据的增删改查),将会导致数据不准确,相互之间产生冲突,因此加入同步锁以避免在该线程没有完成操作之前,被其他线程的调用,从而保证了该变量的唯一性和准确性。

/**

* 同步

*

* @author XIEHEJUN

*

*/

public class SynchronizedThread {

class Bank {

 

private int account = 100;

public int getAccount() {

return account;

}

/**

* 同步方法

*/

public synchronized void save(int money) {

account += money;

}

/**

* 同步代码块

*

* @param money

*/

public void save1(int money) {

synchronized (this) {

account += money;

}

}

}

class NewThread implements Runnable {

private Bank bank;

public NewThread(Bank bank) {

this.bank = bank;

}

@Override

public void run() {

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

//bank.save1(10);

bank.save(10);

System.out.println(i + "账户余额为:" + bank.getAccount());

}

}

}

/**

*

*/

public void useThread() {

Bank bank = new Bank();

NewThread new_thread = new NewThread(bank);

System.out.println("线程1");

Thread thread1 = new Thread(new_thread);

thread1.start();

System.out.println("线程2");

Thread thread2 = new Thread(new_thread);

thread2.start();

public static void main(String[] args) {

SynchronizedThread st = new SynchronizedThread();

st.useThread();

 

2.2synchronize关键字和同步块

synchronized关键字修饰的方法。

由于java的每个对象都有一个内置锁,当用此关键字修饰方法时, 内置锁会保护整个方法。在调用该方法前,需要获得内置锁,否则就处于阻塞状态。

例如:

publicsynchronized void save(){}

同步代码块

即有synchronized关键字修饰的语句块。

被该关键字修饰的语句块会自动被加上内置锁,从而实现同步

 

例如:

synchronized(object){

}

2.3代码实例

//

public classSynchronizedThread {

 

class Bank {

private int account = 100;

public int getAccount() {

return account;

}

public synchronized void save(intmoney) {

account += money;

}

/public void save1(int money) {

synchronized (this) {

account += money;

}

}

}

class NewThread implements Runnable{

private Bank bank;

public NewThread(Bank bank) {

this.bank = bank;

}

@Override

public void run() {

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

// bank.save1(10);

bank.save(10);

System.out.println(i + "账户余额为:" + bank.getAccount());

}

}

}

3 生产者消费者问题

3.1 问题表述

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

3.2 实现思路

1)每个生产者和消费者对有界缓冲区进行操作后,即时显示有界缓冲区的当前全部内容、当前指针位置和生产者/消费者线程的自定义标识符。

2)为每个生产者/消费者产生一个线程,设计正确的同步算法每个生产者和消费者对有界缓冲区进行操作后,即时显示有界缓冲区的当前全部内容、当前指针位置和生产者/消费者线程的自定义标识符。

3)生产者和消费者都有30个;

4)多个生产者或多个消费者之间须共享对缓冲区进行操作的函数代码。

3.3 Java实现该问题的代码

生产者进程:
repeat
produce an item in nextp
wait(empty);
wait(mutex);
buffer(in):=nextp;
in:=(in+1)mod n;
signal(mutex);
signal(full);
until false;
消费者进程:
repeat
wait(full);
wait(mutex);
nextc:=buffer(out);
out:=(out+1)mod n;
signal(mutex);
signal(empty);
consumethe item in nextc
wait(S)
操作:

S=S-1;
若S>=0,则进程继续运行。
若S<0,则该进程被阻塞,并将它插入该信号量的等待队列中。
Signal(S)操作:
S=S+1;
若S>0,则进程继续执行;
若S<=0,则从信号量等待队列中移处第一个进程,使其变为就绪状态,然后再返回原进程继续执行。

当生产者能力超出消费者能力:

package T1;

import java.util.concurrent.ThreadLocalRandom;

publicclass Testpc {

static Que queue = new Que(5);

publicstaticvoid main(String[] args) {

//创建三个生产者

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

finalintindex = i;

new Thread(()->{

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

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

queue.EnQueue(data);

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

sleep();

}).start();

}

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

finalintindex=i;

new Thread(()->{

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

intdata = queue.DeQueue();

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

}).start();

}

}

publicstaticvoid sleep() {

intt =(int)(Math.random()*10);

try {

Thread.sleep(t);

} catch (Exception e) {

e.printStackTrace();

}

}

}

customer thread 0 want to EnQueue

customer thread 0 EnQueue -1 Success

produce thread 2 want to EnQueue 459

produce thread 1 want to EnQueue 750

produce thread 0 want to EnQueue 946

produce thread 0 EnQueue 946 Success

customer thread 1 want to EnQueue

customer thread 2 want to EnQueue

customer thread 1 EnQueue 459Success

produce thread 1 EnQueue 750 Success

produce thread 2 EnQueue 459 Success

customer thread 2 EnQueue 750Success

当消费者能力超过生产者:

package T1;

import java.util.concurrent.ThreadLocalRandom;

publicclass Testpc {

static Que queue = new Que(5);

publicstaticvoid main(String[] args) {

//创建三个生产者

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

finalintindex = i;

new Thread(()->{

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

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

queue.EnQueue(data);

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

sleep();

}).start();

}

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

finalintindex=i;

new Thread(()->{

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

intdata = queue.DeQueue();

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

}).start();

}

}

publicstaticvoid sleep() {

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

try {

Thread.sleep(t);

} catch (Exception e) {

e.printStackTrace();

}

}

produce thread 0 want to EnQueue 5

produce thread 0 EnQueue 5 Success

customer thread 1 want to EnQueue

customer thread 1 EnQueue 5 Success

produce thread 2 want to EnQueue 7

produce thread 2 EnQueue 7 Success

customer thread 2 want to EnQueue

customer thread 2 EnQueue 7 Success

customer thread 0 want to EnQueue

customer thread 0 EnQueue -1 Success

produce thread 1 want to EnQueue 5

produce thread 1 EnQueue 5 Success

 

4总结

本次实践让我知道了什么是线程和进程,一个进程可以有多个线程,知道了什么是javadoc,它是一个可以将文本嵌入到程序中三种创建线程的方法:普通的创建方法,匿名类和lamda表达式。消费者与生产者问题的解决方法,建立队列Queue和producer,创建三个生产者和消费者,通过运行结果观察生产者能力大于消费者和消费者能力大于生产者的现象。知道了什么是信号量,什么是互斥量。

互斥量:

互斥对象和临界区对象非常相似,只是其允许在进程间使用,而临界区只限制与同一进程的各个线程之间使用,

但是更节省资源,更有效率。

信号量:

 信号量就是具有原子性的计数器,就相当于一把锁,在每个进程要访问临界资源时,必须要向信号量拿个锁,它才能进去临界资源这个房间,并锁上门,不让其他进程进来,此时信号量执行P()操作,锁的数目减少了一个,所以计数器减1,;当它访问完成时,它出来,将锁还给信号量,执行V()操作,计数器加1;然后是下面的进程继续。这也体现了各个进程访问临时资源是互斥的


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值