操作系统编程与实践
1 线程的创建与启动
1.1 进程与线程
主要描述进程线程概念和差别。
1. 什么是线程
线程是作为独立调度和分配的基本单位,因而进程是能独立运行的基本单位
2. 线程和进程的关系
一个进程包含多个线程
3. 线程与进程的比较
1. 调度的基本单位:线程切换代价低于进程
2. 并发性:一个进程中的多个线程之间亦可并发执行,不同进程中的线程也能并发执行。
3. 拥有资源:进程可以拥有资源,线程不拥有资源
4. 独立性:在同一进程的不同线程之间的独立性要比不同进程之间的独立性低得多
5. 系统开销:在进程切换时,涉及到进程上下文的切换,而线程的代价也远低于进程。
6. 支持多处理系统:对于传统的进程,即单线程进程,不管有多少处理机,该进程只能运行在一个处理机上。
但对于多线程,就可以将一个进程中的多个线程分配到多个处理机上,使他们执行,这无疑将加速进程的完成。
1.2 Java中的Thread和Runnable
1. 通过继承Thread类,重写Thread的run()方法,将线程运行的逻辑放在其中
2. 通过实现Runnable接口,实例化Thread类
1.3 三种创建线程的办法
package org;创建一个包
/**
* Runnable的实现类,是线程执行的主体。
* Run函数是入口
*
*/
//javadoc 自动生成文档
class MyR implementsRunnable{//创建一个类实现接口的进入
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) {
// TODO Auto-generated catch block
e.printStackTrace();
break;
}
}
}
}
publicclassTestThread {
publicstaticvoid main(String[] args) {
Threadthread1=new Thread(new MyR("hello"));
thread1.start();
Thread thread2=new Thread(new MyR("wuwu"));
thread2.start();
}
}
实验二
package org;
publicclassTestThread2 {
publicstaticvoid main(String[] args) {
TestThread2 testThread2=new TestThread2();
//匿名信匿名类引用指针
Runnable runnable=new Runnable(){
publicvoidrun() {
while(true){
try {
System.out.println("haha");
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
break;
}
}
}
};
Thread thread=new Thread(runnable);//创建Thread类的一个对象,将runnable的值传给他
thread.start();
}
}
实验三
package org;
publicclassTextThread3 {
publicstaticvoid main(String[] args) {
new Thread(new Runnable() {
publicvoid run() {
while(true){
try{
System.out.println("haha");
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
break;
}
}
}
}).start();
new Thread(()->{
System.out.println("haha");
}).start();
也可以用lamda表达式写 9-12行等价
}
}
2 线程简单同步(同步块)
2.1 同步的概念和必要性
为什么要同步,可以举例说明
1. 同步:即当有一个线程在对内存进行操作时,其他线程都不可以对这个内存地址进行操作,
直到该线程完成操作,其他线程才能对该内存地址进行操作,而其他线程又处于等待状态,
目前实现线程同步的方法有很多,临界区对象就是其中一种。
2.不用同步有什么危害
资源会不合理共用,导致系统崩溃
3.同步的好处:
互斥的使用资源
例子:两个同学在同一时刻买一张火车票,要是他们之间不是同步的。会出现两个人都抢到这张票的情况,导致系统崩溃。如果引用就不会出现上述情况。
- 1
- 2
2.2 synchronize关键字和同步块
Java 同步块(synchronized block)用来标记方法或者代码块是同步的。Java 同步块用来避免竞争。
对于同步块,synchornized获取的是参数中的对象的锁:
Java 中的同步块用 synchronized 标记。同步块在 Java 中是同步在某个对象上。所有同步在一个对象上的同步块在同时只能被一个线程进入并执行操作。所有其他等待进入该同步块的线程将被阻塞,直到执行该同步块中的线程退出。
2.3 实例
package org;
import java.util.ArrayList;
publicclassTextthread4 {
staticintc=0;
publicstaticvoid main(String[] args) {
Thread[]threads=new Thread[1000];
for(inti=0;i<1000;i++) {
threads[i]=new Thread(()->{
inta=c;//获取c的值
a++;//将值+1
try {//模拟复杂处理过程
Thread.sleep((long)(Math.random()*1000));
}catch(InterruptedException e) {
e.printStackTrace();
}
c=a;//存回去
});
threads[i].start();//线程开始
}
for(inti=0;i<1000;i++) {
try {
threads[i].join();//等待thread i的完成
}catch (InterruptedException e) {
e.printStackTrace();
}
}//循环后,所有用的线程都完成了
System.out.print("c="+c);//输出c的结果
}
}
实验二
加锁
package org;
import java.util.ArrayList;
import com.sun.media.jfxmedia.events.NewFrameEvent;
publicclassTextthread4 {
staticintc=0;
static Object lock=new Object();//随便创立一个变量,作为锁变量
publicstaticvoid main(String[] args) {
Thread[]threads=new Thread[1000];
for(inti=0;i<1000;i++) {
finalintindex=i;//建立一个final变量,放在lamba中使用
threads[i]=new Thread(()->{
synchronized(lock) {//创建一个同步块,需要一个锁
System.out.println("thread"+index+"enter");//(5)输出
inta=c;//获取c的值
a++;//将值+1
try {//模拟复杂处理过程
Thread.sleep((long)(Math.random()*10));
}catch(InterruptedException e) {
e.printStackTrace();
}
c=a;//存回去
System.out.println("thread"+index+"leave");//输出
}//(3)这是块的终结
});
threads[i].start();//线程开始
}
for(inti=0;i<1000;i++) {
try {
threads[i].join();//等待thread i的完成
}catch (InterruptedException e) {
e.printStackTrace();
}
}//循环后,所有用的线程都完成了
System.out.print("c="+c);//输出c的结果
}
3 生产者消费者问题
3.1 问题表述
3.2 实现思路
用自然语言描述这个问题的解决思路
要解决该问题,就必须让生产者在缓冲区满时休眠(要么干脆就放弃数据),等到下次消费者消耗缓冲区中的数据的时候,生产者才能被唤醒,开始往缓冲区添加数据。同样,也可以让消费者在缓冲区空时进入休眠,等到生产者往缓冲区添加数据之后,再唤醒消费者。通常采用进程间通信的方法解决该问题,常用的方法有信号灯法等。如果解决方法不够完善,则容易出现死锁的情况。出现死锁时,两个线程都会陷入休眠,等待对方唤醒自己。
3.3 Java实现该问题的代码
package org;
import java.util.LinkedList;
publicclassQueue { //队列
privateintsize;
public Queue(intsize) {
this.size = size;
}
LinkedList<Integer>list= newLinkedList<Integer>();
/**
* 入队
* @return
*/
publicboolean EnQueue(intdata) {
if(list.size()>=size) returnfalse;
list.addLast(data);
returntrue;
}
/**
* 出队
* @return
*/
publicint DeQueue() {
if(list.size() == 0) return -1; //返回-1表示失败
returnlist.removeFirst();
}
}
增加
package org;
import java.util.LinkedList;
import javax.print.attribute.standard.RequestingUserName;
publicclassQueue { //队列
privateintsize;
public Queue(intsize) {
this.size = size;
}
LinkedList<Integer>list= newLinkedList<Integer>();
/**
* 入队
* @return
*/
publicboolean EnQueue(intdata) {
if(list.size()>=size) returnfalse;
list.addLast(data);
returntrue;
}
/**
* 出队
* @return
*/
publicint DeQueue() {
if(list.size() == 0) return -1; //返回-1表示失败
returnlist.removeFirst();
}
publicboolean isFull() {
returnlist.size()>=size;
}
publicboolean isEmpty() {
returnlist.size()==0;
}
}
修改
package org;
import java.util.LinkedList;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
publicclassQueue { //队列
private Lock lock=new ReentrantLock();
private Condition fullC;//信号量
private Condition emptyC;//信号量
privateintsize;
public Queue(intsize) {
this.size = size;//为信号量赋初值
fullC=lock.newCondition();
emptyC=lock.newCondition();
}
LinkedList<Integer>list= newLinkedList<Integer>();
/**
* 入队
* @return
*/
publicboolean EnQueue(intdata) {
lock.lock();
while(list.size()>=size) {
try {
fullC.await();
}catch (InterruptedException e) {
lock.unlock();
returnfalse;
}
}
list.addLast(data);
returntrue;
}
/**
* 出队
* @return
*/
publicint DeQueue() {
lock.lock();
while(list.size() == 0) {
try {
emptyC.await();
}catch(InterruptedExceptione) {
lock.unlock();
return -1;
}
}
intr=list.removeFirst();
fullC.signalAll();
lock.unlock();
returnr;
}
publicboolean isFull() {
returnlist.size()>=size;
}
publicboolean isEmpty() {
returnlist.size()==0;
}
public Object size() {
returnlist.size();
}
}
package org;
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();
while(q.isFull()) {
try {
isFull.await(); //如果队列为慢,则等待
}catch(InterruptedException e) {
return;
}
}
//生产并入队
inta = (int) (Math.random()*1000);
q.EnQueue(a);
//生产完后
isEmpty.signalAll();//把消费者唤醒。
lock.unlock();
}
}
3.4 测试
package org;
publicclassTestPC {
static Queue queue=new Queue(5);
publicstaticvoid main(String[] args) {//创建三个生产者
for(inti=0;i<3;i++) {
finalintindex=i;
new Thread(()-> {
while(true) {
intdata=(int)(Math.random()*1000);
System.out.printf("producer thread %d want to EnQueue %d\n",index,data);
queue.EnQueue(data);
System.out.printf("producer thread %d EnQueue %d Sucsess\n",index,data);
sleep();//随机休息一段时间
}
}).start();
}
//创建消费者
for(inti=0;i<3;i++) {
finalintindex=i;
new Thread(()-> {
while(true) {
System.out.printf("customer thread %d want to DeQueue\n",index);
intdata=queue.DeQueue();
System.out.printf("customer thread %d DeQueue %d,size=%d\n Sucsess\n",index,data,queue.size());
sleep2();//随机休息一段时间
}
}).start();
}
}
//sleep随机时间
publicstaticvoid sleep() {
intt=(int)(Math.random()*100);
try {
Thread.sleep(t);
}catch(InterruptedException e) {
e.printStackTrace();
}
}
//sleep随机时间
publicstaticvoid sleep2() {
intt=(int)(Math.random()*1000);
try {
Thread.sleep(t);
}catch(InterruptedException e) {
e.printStackTrace();
}
}
}
3.4.1 当生产能力超出消费能力时的表现
通过sleep函数产生随机数的频率来判断,当他的值小的时候说明生产者能力超出消费者能力
publicstaticvoidsleep() {
intt=(int)(Math.random()*100);
try {
Thread.sleep(t);
}catch(InterruptedException e) {
e.printStackTrace();
}
}
3.4.2 当生产能力弱于消费能力时的表现
通过sleep函数产生随机数的频率来判断,当他的值大的时候说明生产者能力弱于消费者能力
publicstaticvoidsleep2() {
intt=(int)(Math.random()*1000);
try {
Thread.sleep(t);
}catch(InterruptedException e) {
e.printStackTrace();
}
}
总结
通过课程的学习我学会了如何用Java创建进程,通过Thread和Runnable类,用了三种不同的方法。更深一步的学习到了线程的概念和进程的区别。通过第二个实验我学习到了同步的概念和同步的好处及不使用同步的危害,以及synchronize关键字和同步块的关系。第三个实验学会到了生产者消费者问题,用信号量机制解决这个问题。通过实验学习到了用sleep函数判断生产能力超出弱于消费能力的表现。在实验过程中遇到了很多问题,通过老师和同学的帮助,解决了这些问题,顺利的完成了实验,从中我学到,面对困应当迎难而上。
上一篇: 路径规划算法
下一篇: 【Linux系统编程】线程基本操作
推荐阅读
-
详解Python编程中包的概念与管理
-
详解JavaScript编程中的window与window.screen对象
-
Android编程实现下载时主界面与详细界面一致更新的方法
-
Java编程线程间通信与信号量代码示例
-
人工智能赋能健康医疗:理论与实践、现实与挑战
-
Android编程使用WebView实现与Javascript交互的方法【相互调用参数、传值】
-
Android编程创建与解析xml的常用方法详解
-
Python进阶编程 类与类的关系
-
Linux编程 14 文件权限(用户列表passwd,用户控制shadow,useradd模板与useradd命令参数介绍)
-
Android编程中关于单线程模型的理解与分析