并发和并行的区别
并行:指两个或多个时间在同一时刻发生;情调的是时间点
并发:指两个或多个时间在同一时间段内发生;强调的是时间点
在操作系统中,在多道程序环境下,并发性是指在一段时间内,有多个程序同时进行,但在单CPU系统下,每个时刻却仅能有一个程序执行(时间片),所以微观上这些程序只能是分时地交替执行。倘若计算机系统中有多个CPU,则这些可以并发执行的程序可被分配到多个处理机中,实现并行执行,即利用每个处理机来处理一个可并发执行的程序,这样多个程序就能同时执行,因为是微观的,所以大家在使用电脑的时候感觉就是多个程序是同时执行的。
单核处理器的计算机肯定是不能并行地处理多个任务的,只能是多个任务在单个CPU中并发运行,CPU调度。
同理线程也是一样,从宏观角度上理解线程是并行运行的,但是从微观角度上分析却是串行运行的,即一个线程一个线程地去运行,当系统只有一个CPU时,系统会以某种顺序执行多个线程,我们把这种情况称之为线程调度。
解决多个程序的并发操作,可以从进程或者线程入手。
进程是指一个内存中运行中的应用程序。每个进程都有自己独立的一块内存空间,一个应用程序可以同时启动多个进程。比如在windows系统中,一个运行的xx.exe就是一个进程。
那么此时我们可以用进程来处理同时玩游戏和听音乐的问题。我们可以设计两个进程,一个专门负责玩游戏,一个专门负责听音乐。但是,如果两个并发的动作如果需要共享数据,那么用进程就无法解决了,因为大多数操作系统都不需要一个进程访问其他进程的内存空间,也就是说进程之间的通信很不方便,这时候需要用线程去解决这个问题。
线程是指进程中的一个执行任务(控制单元),一个进程可以同时并发运行多个线程,如多线程下载软件。一个进程至少有一个线程,为了提高效率,可以在一个进程中开启多个执行任务,即多线程。
多进程:操作系统中同时运行多个程序
多线程:在同一个进程中同时运行的多个任务
多线程下载:可以理解为一个线程就是一个文件的下载通道,多线程也就是同时开起了好几个下载通道。当服务器提供下载服务的时候,使用下载者是共享带宽的,在优先级相同的情况下,总服务器会对总下载线程进行平均分配。也就是线程越多,下载得越快
总结:进程和线程的区别
进程:有独立的内存空间,进程中的数据存放空间(堆空间和栈空间)是独立的,至少有一个线程
线程:堆空间是共享的,栈空间是独立的,线程消耗的资源也比资源小,相互之间可以影响的,又称为轻型进程或进程元
因为一个进程中的多个线程是并发运行的,那么从微观角度上考虑也是先后顺序的,那么哪个进程执行完全取决于CPU调度器,程序员是控制不了的。我们可以把多线程并发性看做是多个线程在瞬间抢CPU资源,谁抢到资源谁就运行,这也早就了多线程的随机性。
多线程作为一种多任务、并发的工作方式,当然有其存在优势:
1.提高应用程序响应
2.使多cpu系统更加有效
操作系统会保证当线程数不大于CPU数目时,不同的线程运行于不同的cpu上。
3.改善程序结构
在java中调用其他进程
public class ProcessDemo{
public static void main(String []args)
{
Runtime runtime=Runtime.getRuntime();
runtime.exec(“noteped.exe);
}
}
创建线程的方式:
1.继承java.lang.Thread类,覆盖java.lang.Thread类中的run方法,并在该run方法中编写实现代码
2.实现java.lang.Runnable接口,覆盖/实现java.lang.Runnable接口中的run方法,并在该run方法中编写实现代码
下面用A、B、C共吃50个苹果的例子来说明线程的创建和使用。
继承Thread类
public class EatApples1{
class ExtendThread extends Thread{
public static int apples =50;
public ExtendThread(String name)
{
super(name);
}
public void run(){
for(int i=0;i<100;i++)
{
if(apples>0)
{
System.out.println(this.getName()+"当前拿到第"+apples--);
}
}
}
}
public static void main(String []args)
{
new ExtendThread("A").start();
new ExtendThread("B").start();
new ExtendThread("c").start();
}
}
使用实现的方式
public class EatApple2{
class ImpleRunnable implements java.lang.Runnable{
private int apples=50;
public void run(){
for(int i=0;i<50;i++)
{
if(apples>0)
{
System.out.println(Thread.currentThread.getName+"当前拿到第"+apples--);
}
}
}
}
public static void main(String []args)
{
ImpleRunnable runnable=new ImpleRunnable ();
new Thread(runnable,"A").start();
new Thread(runnable,"B").start();
new Thread(runnable,"C").start();
}
}
上述两种方式都基本实现了多线程的操作。
两者的区别:
1.因java的单继承特性,如果该类需要继承其他类,那么应该采用接口创建的方法。
2.继承方式中需要对变量使用static修饰,才能达到共享资源的目的,而实现方式只新建了一个对象,几个线程就共享同一份资源,因此,推荐使用实现方式。
上述两个方法虽然都完成了多线程的操作,但是同时也存在着线程安全问题。
**在Java中,解决多个线程并发访问同一个资源时出现的线程安全问题,解决方案有3种:**
1.同步代码块
2.同步方法
3.java5开始支持,锁机制。
方式1:同步代码块
public class EatApple2{
class ImpleRunnable implements java.lang.Runnable{
private int apples=50;
public void run(){
for(int i=0;i<50;i++)
{
synchronized(this){
if(apples>0)
{
System.out.println(Thread.currentThread.getName+"当前拿到第"+apples--);
}
}
}
}
}
public static void main(String []args)
{
ImpleRunnable runnable=new ImpleRunnable ();
new Thread(runnable,"A").start();
new Thread(runnable,"B").start();
new Thread(runnable,"C").start();
}
}
方式2:使用同步方法
public class EatApple2{
class ImpleRunnable implements java.lang.Runnable{
private int num=50;
public void run(){
for(int i=0;i<50;i++)
{
take();
}
}
synchronized public void take(){
if(apples>0)
{
System.out.println(Thread.currentThread.getName+”当前拿到第”+apples–);
}
}
}
public static void main(String []args)
{
ImpleRunnable runnable=new ImpleRunnable ();
new Thread(runnable,”A”).start();
new Thread(runnable,”B”).start();
new Thread(runnable,”C”).start();
}
}
同步方法的同步监听对象:
若同步方法是非static 方法,此时同步监听对象是:this
若同步方法是static 方法,此时同步监听对象是:当前方法所在类的字节码对象
方式3:使用锁机制
从java5开始提供的这种操作,更加面向对象
ReentrantLock:一个可重入的互斥锁Lock,它具有与使用synchronized方法和语句所访问的隐式监视锁相同的一些基本行为和语义,但功能更强大。
public class EatApple2{
class ImpleRunnable implements java.lang.Runnable{
private int apples=50;
private final Lock lock=new ReentrantLock();
public void run(){
for(int i=0;i<50;i++)
{
this.take();
}
}
public void take{
lock.lock();
try{
if(apples>0)
System.out.println(Thread.currentThread.getName()+"当前第"+apples--);
}finally{
lock.unlock();
}
}
}
public static void main(String []args)
{
ImpleRunnable runnable=new ImpleRunnable ();
new Thread(runnable,"A").start();
new Thread(runnable,"B").start();
new Thread(runnable,"C").start();
}
}