线程与进程的使用

什么是进程与线程

当我们在手机上打开淘宝App,想购买点东西的时候,这个时候我们其实就启动了一个进程。当我们启动一个程序的时候,我们就需要为这个程序分配存储资源以及运算资源。当这个程序运行的时候,我们就叫它为进程。当我们打开一个电脑的运行管理器的时候,我们会发现很多程序在管理器上运行着,而每一个程序代表着一个进程。也可以说一个程序只有一个进程在工作。

而一个进程又是由许多个线程组成的,线程是进程的单位。一个进程可以有很多个线程,线程的意义是什么呢? 在我们生活中,如果我们想吃到香喷喷的米饭,我们先要种水稻,培育水稻,等到水稻成熟了我们在收割水稻,将收割好的水稻去壳,烹饪,我们就能得到一碗米饭了。这个过程是有序的,哪一步没有完成,我们都没办法进行下一项活动。而我们在吃米饭的时候还想配上一碗好吃的红烧肉,这该怎么办呢?我们等米饭上桌了在开始养猪?这样会让我们吃到一顿米饭配红烧肉的这顿饭时间消耗的过于长,我们想缩短时间,一个好办法就是,在种水稻的同时,我们也可以养猪。养猪和种水稻在顺序上是没有依赖关系的。当水稻种好,等待生长的时候,我们就不用对水稻做什么操作了,这段时间我们可以饲养我们的猪。这样就使得我们的工作时间分配的更合理,更高效。我们同时做了种水稻这件事,又做了养猪这件事。序的某些操作之间是没有先后顺序的,我们可以将这些没有先后顺序联系的操作,拿出来组成不同的线程,在程序里面一起的工作,当一个线程遇到需要等待的情况,其它的进程也可以继续工作,而不是堵在哪里,就像在高速公路上,不会只有一条道路,当有一台车占用这个车道出现故障的时候,走在其它车道的车辆不会因为这辆停下来而不行驶了,出现交通堵塞,他们还是照常的向前开,不受影响,不浪费宝贵的计算资源。提高程序运行效率。同时这样可以多个功能一起的实现。而有些时候我们需要几个功能一起工作才能满足我们的需求,就相当于看电影的时候要眼耳并用。我们也需要这种并行化的方式来实现眼耳共用。

举个例子

以银行从网上取钱的例子来解释一下,多线程的作用。我们每一个人在网上取钱,每个人都可以看成是一个线程,同时有很多人在网上同时的存钱和取钱。我们可以想象一下,如果不是多线程的化,排队去在上网取钱的话,那么排多久才能轮到你呢?以中国人口数量的话,发起取钱的申请,然后一个一个的取款,等排到我们可以取钱的时候,要等几个月都有可能。所以我们要设置一个多线程的方法实现多人共同取钱。
首先,我们要建立一个小金库。金库里有200份的钱,每份是100元,我们规定每次只能取走一份。代码如下:

public class store {
	private int [] count = new int[200];
	private int i = 0;
	String name ;
	public store(String name) {
		System.out.println("建立store");
		this.name = name;
		for(int i = 0; i<200; i++) {
			count[i] = 100;
		}
	}
	public void get_money(String user) {
		count[i] = count[i] - 100;
		i++;
		System.out.println("第i次  " + i + " user is "+user);
	}
	public int find(int i) {
		return count[i];
	}
}

接着我们想要建立多个线程。首先我们要定义一个取钱的线程类。这个线程类需要继承java.lang.Thread 这个类,并且要改写run的这个方法,代码如下:

import java.lang.Thread;

public class Thread_get_money extends Thread {
	public store st ;
	public String user ;
	public Thread_get_money(store st,String user) {
		this.st = st;
		this.user = user;
	}
	public void run () {
		for(int i = 0; i < 99; i++) {
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			st.get_money(user);
		}
	}
}

这里面我们在run这个方法中定义了取钱的方法,一个线程重复的对这个金库取钱99次后,结束这个线程。注意,在使用线程的时候我们还定义了一个线程的休眠,就是在执行休眠这部分时候线程在这里等待一会。因为计算机执行的速度都是以毫米为单位的,人还无法感知的时候,可能这个程序就执行完了,为了让我们人类对程序的一些操作来得及感知并且做出回应,进程中就有了休眠这个存在,让进程等一等。Thread.sleep 就是让进程休眠。使用这段休眠代码的时候要和try,catch一起使用。不然程序会报错。

最后我们模拟一下取钱的这个过程:

import java.lang.Thread;

public class test {
	public static void main (String[] args) {
		store count = new store("bank") ;
		Thread_get_money th1 = new Thread_get_money(count,"th1");
		Thread_get_money th2 = new Thread_get_money(count,"th2");
		th1.start();
		th2.start();
		try {
			Thread.sleep(20000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		for(int i = 0; i < 200;i++) {
		System.out.println(count.find(i));
		}
	}
}

这里我们定义了两个线程th1和th2用来一起去取钱。count 就是取钱的金库。最后我们打印了一些取过钱后金库内部的数据。
一部分输出结果:
0
0
0
-100
100
0
0
0
我们发现这中间居然出现了负数。这是由于,进程1和进程2对在金库中取钱的时候,金库对多个进程来说是共享的数据。当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没有执行完,另一个线程参与进来执行。导致共享数据的错误。即多个线程执行的不确定性引起执行结果的不稳定。我们要怎么办呢?我们需要在一个人对金库操作的时候,另一个人不让进。这时候我们需要加一个线程锁,当一个线程占用这个金库的时候,这个金库就锁住了,等这个进程用完了金库之后,金库开放给另一个进程。具体的解决方案有:
1)同步代码块:
synchronized(对象){
同步代码块;
}
这样多条语句操作共享数据时,只能让一个进程执行,其它的不可以执行。对象如同锁,持有锁的线程可以在同步中执行没有持有锁的线程即使获得CPU的执行器,也无法执行。

同步前提:
1.多线程的代码块才需要同步,即run里的代码才需要加同步。对共享数据加同步
2.必须是多个线程使用同一个锁,才需要同步

好处:提高多线程的安全问题
弊端:多个线程需要判断锁,较为消耗资源
这样我们的代码就修改如下:

public class Thread_get_money extends Thread {
	public store st ;
	public String user ;
	private Object obj;
	public Thread_get_money(store st,String user , Object obj) {
		this.st = st;
		this.user = user;
		this.obj = obj;
	}
	public void run () {
		for(int i = 0; i < 99; i++) {
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			synchronized(obj){
				st.get_money(user);
			}
		}
	}
}

我们将同步代码块加到run这个方法中,将需要提取共享数据的代码放入到同步代码块
synchronized(对象){
同步代码块;
}
中。这样在使用共享数据的时候就会先查询一下共享的数据有没有被其它线程语句所调用。如果被其它线程语句所执行,那就等其它语句执行完,释放了共享数据后再执行本线程中提取共享数据的语句。

2)lock对象的方法:
我们同样可以用lock方法构造一个对象,当这个对象在某一个线程中启动的时候,其它线程中同一个对象就会阻塞不会在执行,只有当调用对象的这个线程将释放以后,其它线程的同一个对象才会继续获得执行的权限,来继续执行。
将上一个代码用lock 方法表示的时候代码如下:

import java.lang.Thread;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Thread_get_money extends Thread {
	public store st ;
	public String user ;
	private Lock lock;
	public Thread_get_money(store st,String user , Lock lock) {
		this.st = st;
		this.user = user;
		this.lock = lock;
	}
	public void run () {
		for(int i = 0; i < 99; i++) {
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			try{
				lock.lock();
				st.get_money(user);
			}
			finally{
			lock.unlock();
			}
		}
	}
}
public class test {
	public static void main (String[] args) {
		Lock lock = new ReentrantLock();
		store count = new store("bank") ;
		Thread_get_money th1 = new Thread_get_money(count,"th1" ,lock);
		Thread_get_money th2 = new Thread_get_money(count,"th2" ,lock);
		th1.start();
		th2.start();
		try {
			Thread.sleep(20000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		for(int i = 0; i < 200;i++) {
		System.out.println(count.find(i));
		}
	}
}

Lock lock = new ReentrantLocak();是生成一个名字为lock的锁对象,然后将这个对象传入到th1,th2这两个进程中。当其中一个线程的执行到lock.lock这段语句的时候,这个线程就获得了这个锁,在这个线程没有释放这个锁,也就是没有执行到lock.unlock这条语句的时候,其它线程在执行lock.lock这条语句时,因为锁被其它进程锁住了,所以就停止了,只有别的进程释放锁后,停止的进程得到锁后,进程才能继续运行。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值