您的位置:首页 > 编程语言 > Java开发

java基础——进程和线程

2017-10-20 11:26 239 查看
1.并行和并发

在学习线程之前,先理解一下并行和并发的概念
并发(concurrency)的实质是一个物理CPU(也可以多个物理CPU) 在若干道程序之间多路复用,并发性是对有限物理资源强制行使多用户共享以提高效率。
并行性(parallelism)指两个或两个以上事件或活动在同一时刻发生。在多道程序环境下,并行性使多个程序同一时刻可在不同CPU上同时执行。



并发,是在同一个cpu上同时(不是真正的同时,而是看来是同时,因为cpu要在多个程序间切换)运行多个程序。



并行,是每个cpu运行一个程序。

------------------------------------------------------
【进程和线程】
进程是指一个内存中运行的应用程序。每个应用程序都有自己的一块内存空间(内存空间是用来存放数据的),一个应用程序可以同时启用多个进程。比如在Windows系统中,一个exe文件就是一个进程(进程之间的通信很不方便)。
线程是指进程中一个执行任务(控制单元),一个进程可以同时并发运行多个线程,如:多线程下载软件。
多任务系统,该系统可以运行多个进程,一个进程也可以执行多个任务,一个进程可以包含多个线程。
一个进程至少有一个线程,为了提高效率,可以在一个进程中开启多个执行任务,即多线程。

多进程:操作系统中同时运行的多个程序。
多线程:在同一个进程中同时运行的多个任务。

Windows环境下的任务管理器:在操作系统系统中允许多个任务,每一个任务就是一个进程,每一个进程也可以同时执行多个任务,每一个任务就是线程。
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
进程与线程的区别:
<
10bb7
span style="font-size:13px;color:rgb(57,57,57);">进程:有独立的内存空间,进程中的数据存放空间(堆空间和栈空间)是独立的,至少有一个线程。
线程:堆空间是共享的,栈空间是独立的,线程消耗的资源也比进程小,相互之间可以影响,又称为轻型进程或进程元。
线程的先后执行取决于CPU调度器(JVM来调度)。
多线程并发性可看作是多个线程在瞬间抢CPU资源,谁抢到资源谁就运行,这也造就了多线程的随机性。
Java程序的进程里至少包含主线程和垃圾回收线程(后台线程)
---------------------------------------------------------------------------------------------------------------------------------------------------------
多线程的优势
【多线程优势】
工作方式:多任务、并发的。
①进程之间不能共享内存,而线程之间共享内存(堆内存)则很简单。
②系统创建进程时需要为该进程重新分配系统资源,创建线程则代价小很多,因此实现多任务并发时,多线程效率更高。
③Java语言本身内置多线程功能的支持,而不是单纯的作为底层系统的调度方式,从而简化了多线程编程。

多线程下载:可以理解为一个线程就是一个文件的下载通道,多线程也就是同时开起好几个下载通道。当服务器提供下载服务时,使用下载者是共享宽带的,在优先级相同的情况下,总服务器会对总下载线程进行平均分配。(线程多的话,下载快;现流行的下载软件都支持多线程)
多线程是为了同步完成多项任务,不是为了提供程序运行效率,而是通过提高资源使用效率来提高系统的效率。(所以买电脑的时候,也应该看看CPU的线程数)

宽带带宽:是以为(bit)计算,而下载速度是以字节(Byte)计算,1字节(Byte)等于8位(bit),所以1024kb/s是代表上网带宽为1024千位(1M),而下载速度需要1024千位/秒(1024kb/s)带宽除以8,得出128千字节/秒(128KB/s)。

--------------------------------------------------------------------------------------------------------------------------------------------------------
创建进程操作
【Java操作进程】
在Java代码中如何去运行一个进程。(后面会讲获取进程中的数据(IO))。
①Runtime类的exec()方法
②ProcessBuilder的start()方法
command参数包含程序及其参数的字符串数组。
//java中如何开启一个进程:运行一个记事本程序
public class ProcessDemo {
public static void main(String[] args) throws Exception {
//方式1 使用 runtime类的exec方法
Runtime  runtime = Runtime.getRuntime();
runtime.exec("notepad");
//方式2:processbuilder
ProcessBuilder pb= new ProcessBuilder("notepad");
pb.start();
}
}


【创建和启动线程】(传统有两种方式)
①继承Thread类
②实现Runnable接口
线程类(java.lang.Thread):Thread类和Thread的子类才能称之为线程类。
别忘了主线程(main方法运行,表示主线程)。

①继承Thread类:
步骤:
1.定义一个类A继承于java.lang.Thread类
2.在A类中覆盖Thread类的run方法
3.在run方法中编写需要执行的操作(run方法里的代码是线程执行体
4.在main方法(线程)中,创建线程对象,并启动线程。
创建线程类的格式:A类 a=new A类();
调用线程对象的start方法:a.start(); //启动一个线程
注:千万不要调用run方法,如果调用run方法等于是对象调用方法,依然还是只有一个线程,并没有开启新的线程。
//播放音乐的线程类
class MusicTread extends java.lang.Thread{
public void run() {
for(int i = 0; i < 50; i++ )
{
System.out.println("播放音乐"+ i);
}
}
}
//方式1:继承thread类
public class ThreadDemo {
public static void main(String[] args) {
//运行游戏
for(int i = 0; i < 50; i++ )
{
System.out.println("运行游戏"+ i);
if(i == 10)
{
MusicTread mic = new MusicTread();
mic.start();//启动线程,不能用run方法

}
}
}
}

步骤:②实现Runnable接口:
1.定义一个类A实现java.lang.Runnable接口,注意A类不是线程类
2.在A类中覆盖Runnable接口中的run方法
3.在run方法中编写需要执行的操作(run方法里的代码是线程执行体
4.在main方法(线程)中,创建线程对象,并启动线程。
创建线程类对象:Thread t=new Thread(new A());
调用线程对象的start方法:t.start(); //启动一个线程
Thread构造器,需要Runnable对象/Runnable实现类的对象



class Game implements Runnable {
public void run() {
for (int i = 0; i < 50; i++) {
playgame();
}
}

private void playgame() {
System.out.println("playgame...");
}
}

public class ThreadCreate {
public static void main(String[] args) throws Exception {
// 通过实现runnable接口方式
Game gm = new Game();
Thread gmtd = new Thread(gm, "playgames,hh..");
gmtd.start();
System.out.println(gmtd.currentThread().getName());

}
}


注意:子线程一旦创建成功,拥有和主线程一样的级别,主线程执行完成,子线程仍然可以继续执行

runnable类
currentThread()返回当前线程对象的引用

并行和并发
------------------------------------------------------------------------------------------------
【继承方式和实现方式的区别】
继承方式:
1)Java中类是单继承的,如果继承了Thread,该类就不能再有其他的直接父类了。
2)从操作上分析,继承方式更简单,获取线程名字也简单(操作上,更简单)。
3)从多线程共享同一个资源上分析,继承方式不能做到
实现方式:
1)Java中类可以实现接口,此时还可以继承其他类,并且还可以实现其他接口(设计上更优雅)。
2)从操作上分析,实现方式稍微复杂点,获取线程名字也比较复杂,要使用Thread.currentThread()来获取当前线程的引用。
3)从多线程共享同一个资源上分析,实现方式能做到(可以共享同一个资源)。

------------------------------------------------------
【线程不安全的问题】
当多线程并发访问同一资源对象的时候,可能出现线程不安全的问题。
让问题更明显(并不是使用该方法之后才出现问题):Thread.sleep(10); //当前线程睡10毫秒,使当前线程休息,让其他线程去抢资源。经常用来模拟网络延迟。
注:在线程的run方法上不能使用throws来声明抛出异常,只能在方法中使用try-catch来处理异常。
原因:子类覆盖父类方法的原则,子类不能抛出新的异常。在Runnable接口中的run方法,都没有声明抛出异常。

原子操作:不分割,必须保证同步进行。
1.先展示自己拿到受伤苹果的编号。
2.在吃掉苹果,意味着苹果的总数少一个。

要解决上述多线程并发访问同一个资源的安全性问题:
解决方案:
①同步代码块
②同步方法
③锁机制(Lock)
----------------------------------------------------------------

【同步代码块】
语法:
synchronized(同步锁)
{
需要同步操作的代码
}
【同步锁】
为了保证每个线程都能正常执行原子操作,Java引入了线程同步机制。
同步监听对象/同步锁/同步监听器/互斥锁:对象的同步锁只是一个概念,可以想象为在对象上标记了一个锁。
Java程序运行使用任何对象作为同步监听对象,但是一般的,我们把当前并发访问的共同资源作为同步监听对象(如:this,该对象属于多线程共享的资源)。
注意:在任何时候,最多允许一个线程拥有同步锁,谁拿到锁就进入代码块,其他线程只能在外等着。

【同步方法】
使用synchronized修饰的方法。
对应的同步锁:对于非static方法,同步锁就是this;对于static方法,使用当前方法所在类的字节码对象。(Apple2.class)
不要使用synchronized修饰run方法,修饰之后,某一个线程就执行完了所有功能,好比是多个线程出现串行。解决办法:
把需要同步执行代码放在一个新方法中用synchronized修饰,在run中调用新方法即可。
--------------------------------------------------------------------------------------
【synchronized】
好处:保证了多线程并发访问的同步操作,避免线程的安全性问题。
缺点:使用synchronized的方法/代码块的性能要低一些。

StringBuilder和StringBuffer的区别:
StringBuilder:不安全、性能高(无synchronized修饰)
StringBuffer:安全、性能低(有synchronized修饰)
ArrayList和Vector的区别:
ArrayList(无synchronized修饰)
Vector(有synchronized修饰)
HashMap和Hashtable的区别:
-------------------------------------------------------------------------------------------------------------------------------------------
同步锁(Lock):
lock机制提供了比synchronized代码块和synchronized方法更为广泛的锁操作,同步代码块/同步方法具有的功能lock都有,除此之外更强大,更体现面向对象.
在JDK7 API中可以看到如下的说明:



public class SingletonDemo{
class  Apple implements Runnable {
private int num = 50;//苹果总数
private final Lock lock = new ReentrantLock();//创建一个锁对象
public void run() {
for (int i=0;i<50;i++){
eat();
}
}
private void eat() {
//进入方法立马加锁
lock.lock();
try{
if(num>0){
System.out.println(Thread.currentThread().getName()+"吃了的编号"+num);
Thread.sleep(100);
num--;
}
}catch(InterruptedException e){
e.printStackTrace();
}finally{
lock.unlock();
}
}
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: