线程,进程,单线程,多线程,附有并发例子

本文详细阐述了进程和线程的概念、关系与区别,并对比了两者的优缺点。此外,还介绍了单线程与多线程的区别,多线程的应用场景及注意事项,包括如何使用Java的synchronized关键字来实现线程同步。

本篇博客是根据自己的理解对这块知识做一个总结,如果有不妥的地方请指出,定会改正。

1、线程和进程

1.1、线程和进程定义

进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位.

线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源.

1.2、线程和进程关系

一个线程可以创建和撤销另一个线程;同一个进程中的多个线程之间可以并发执行.

相对进程而言,线程是一个更加接近于执行体的概念,它可以与同进程中的其他线程共享数据,但拥有自己的栈空间,拥有独立的执行序列。

1.3、线程和进程区别

进程和线程的主要差别在于它们是不同的操作系统资源管理方式。进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程只是一个进程中的不同执行路径。线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间,一个线程死掉就等于整个进程死掉,所以多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差一些。但对于一些要求同时进行并且又要共享某些变量的并发操作,只能用线程,不能用进程。

1) 简而言之,一个程序至少有一个进程,一个进程至少有一个线程.

2) 线程的划分尺度小于进程,使得多线程程序的并发性高。

3) 另外,进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序的运行效率。

4) 线程在执行过程中与进程还是有区别的。每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口。但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。

5) 从逻辑角度来看,多线程的意义在于一个应用程序中,有多个执行部分可以同时执行。但操作系统并没有将多个线程看做多个独立的应用,来实现进程的调度和管理以及资源分配。这就是进程和线程的重要区别。

1.4、线程和进程优缺点

线程和进程在使用上各有优缺点:线程执行开销小,但不利于资源的管理和保护;而进程正相反。同时,线程适合于在SMP机器上运行,而进程则可以跨机器迁移。

2、单线程和多线程

单线程和多线程部分主要讲多线程的特点和使用场景以及怎么去写多线程程序。

2.1、单线程多线程定义

单线程在程序执行时,所走的程序路径按照连续顺序排下来,前面的必须处理好,后面的才会执行。
多线程是指从软件或者硬件上实现多个线程并发执行的技术。具有多线程能力的计算机因有硬件支持而能够在同一时间执行多于一个线程,进而提升整体处理性能。

2.2、单线程和多线程的特点

根据单线程和多线程的定义很容易理解,多线程一次可以执行多个并发程序,而单线程一次只能执行一个进程,
从硬件上讲,增加cup的核数可以一次执行多条进程。
从软件上讲,实现多线程可以一次执行多条进程。

2.3、多线程的使用场景

根据多线程的定义和特点不难看出多线程的应用场景,我从提高工作效率和监控消息两个方面去说多线程。

2.31、多线程提高程序的执行效率

比如说有一堆数据需要用程序去处理,那么多线程一次可能会处理n条(n==线程数),单线程一次只会处理一条。而多线程的cpu利用率要比单线程的利用率高。

2.32、多线程监控消息

比如你要做一个多人聊天软件,那么对整个软件来讲用户之间是多对多的关系,对一个用户来讲是一对多的关系。关键问题来了,要实时监控和某一个用户进行聊天的用户之间的消息,就要用多线程。也就是说聊天用户两者之间必须要有一个线程在时刻监控聊天信息。

2.4、多线程要注意的地方

多线程有很多优点,但是多线程的缺点也需要各位注意,在执行同一个信息量的时候有可能出错,导致最后的数据有偏差,这时候应该合理的利用“锁”Java提供了 synchronized关键字,极其方便使用当然你也可以使用lock。这里推荐使用synchronized
关于synchronized的用法
(1)方法声明时使用,放在范围操作符(public等)之后,返回类型声明(void等)之前.即一次只能有一个线程进入该方法,其他线程要想在此时调用该方法,只能排队等候,当前线程(就是在synchronized方法内部的线程)执行完该方法后,别的线程才能进入.

例如:

      public synchronized void synMethod() {

        //方法体

      }

(2)对某一代码块使用,synchronized后跟括号,括号里是变量,这样,一次只有一个线程进入该代码块.例如:

public int synMethod(int a1){

        synchronized(a1) {

          //一次只能有一个线程进入

        }

      }

(3)synchronized后面括号里是一对象,此时,线程获得的是对象锁.例如:

 public void run() {

    synchronized (this) {

      System.out.println(Thread.currentThread().getName());

    }

  }

对于3,如果线程进入,则得到对象锁,那么别的线程在该类所有对象上的任何操作都不能进行.在对象级使用锁通常是一种比较粗糙的方法。为什么要将整个对象都上锁,而不允许其他线程短暂地使用对象中其他同步方法来访问共享资源?如果一个对象拥有多个资源,就不需要只为了让一个线程使用其中一部分资源,就将所有线程都锁在外面。由于每个对象都有锁,可以如下所示使用虚拟对象来上锁:

(4)synchronized后面括号里是类.例如

class ArrayWithLockOrder{

  private static long num_locks = 0;

  private long lock_order;

  private int[] arr;

  public ArrayWithLockOrder(int[] a)

  {

    arr = a;

    synchronized(ArrayWithLockOrder.class) {//-----------------------------------------这里

      num_locks++;             // 锁数加 1。

      lock_order = num_locks;  // 为此对象实例设置唯一的 lock_order。

    }

  }

  public long lockOrder()

  {

    return lock_order;

  }

  public int[] array()

  {

    return arr;

  }

}

class SomeClass implements Runnable

{

  public int sumArrays(ArrayWithLockOrder a1,

                       ArrayWithLockOrder a2)

  {

    int value = 0;

    ArrayWithLockOrder first = a1;       // 保留数组引用的一个

    ArrayWithLockOrder last = a2;        // 本地副本。

    int size = a1.array().length;

    if (size == a2.array().length)

    {

      if (a1.lockOrder() > a2.lockOrder())  // 确定并设置对象的锁定

      {                                     // 顺序。

        first = a2;

        last = a1;

      }

      synchronized(first) {              // 按正确的顺序锁定对象。

        synchronized(last) {

          int[] arr1 = a1.array();

          int[] arr2 = a2.array();

          for (int i=0; i<size; i++)

            value += arr1[i] + arr2[i];

        }

      }

    }

    return value;

  }

  public void run() {

    //...

  }

}

对于4,如果线程进入,则线程在该类中所有操作不能进行,包括静态变量和静态方法,实际上,对于含有静态方法和静态变量的代码块的同步,我们通常用4来加锁.

以上4种之间的关系:
锁是和对象相关联的,每个对象有一把锁,为了执行synchronized语句,线程必须能够获得synchronized语句中表达式指定的对象的锁,一个对象只有一把锁,被一个线程获得之后它就不再拥有这把锁,线程在执行完synchronized语句后,将获得锁交还给对象。
在方法前面加上synchronized修饰符即可以将一个方法声明为同步化方法。同步化方法在执行之前获得一个锁。如果这是一个类方法,那么获得的锁是和声明方法的类相关的Class类对象的锁。如果这是一个实例方法,那么此锁是this对象的锁。

下面附上一个关于多线程的银行转账的例子:

Bank类:
类中包含
初始化Bank方法
转账方法transfer方法
得到总共资金getTotalBalance方法
查看转用户数量getAccountSize方法

public class Bank {

    private final double[] accounts;  
    private int i = 0;
    private int zhichu = 0;
    private int shouru = 0;

    public Bank(int n, double initialBalance) {  
        accounts = new double[n];  
        for (int i = 0; i < accounts.length; i++) {  
            accounts[i] = initialBalance;  
        }  
    }  

    public synchronized void transfer(int from, int to, int amount) {  
        if (accounts[from] < amount) {  
            return;  
        }  
        i++;
        System.out.println("第"+i+"笔");
        System.out.println(Thread.currentThread());
        System.out.println(from+"原来有"+accounts[from]);
        System.out.println(to+"原来有"+accounts[to]);
        accounts[from] -= amount;  
        zhichu = zhichu + amount;
        System.out.print(amount+" from "+from+" to "+to);
        System.out.println();
        System.out.println(from+"有"+accounts[from]);
        accounts[to] += amount;  
        shouru = shouru + amount;
        System.out.println(to+"有"+accounts[to]);
        System.out.println("total:" + getTotalBalance()); 
        System.out.println("总支出:"+zhichu);
        System.out.println("总收入:"+shouru);
        System.out.println("当前共有"+getAccountSize()+"个用户");
    }  

    public double getTotalBalance() {  
        double sum = 0d;  
        for (int i = 0; i < accounts.length; i++) {  
            sum += accounts[i];  
        }  
        return sum;  
    }  

    public int getAccountSize() {  
        return accounts.length;  
    }  
}

多线程类:

public class TransferRunnable implements Runnable{
    private Bank bank;  
    private int fromAccount = 0;  
    private double maxAmount = 0;  


    public TransferRunnable(Bank b, int fromAccount, double maxAmount) {  
        this.bank = b;  
        this.fromAccount = fromAccount;  
        this.maxAmount = maxAmount;  
    }  


    @Override  
    public void run() {
        int amount = (int)(maxAmount * Math.random());  
        int toAccount = (int) ((int) bank.getAccountSize() * Math.random());  
        bank.transfer(fromAccount, toAccount, amount);  
    }  
}

测试类:

public class Test {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        Bank bank = new Bank(100, 100);  
        for (int i = 0; i < 100; i++) {  
            TransferRunnable transferRunnable = new TransferRunnable(bank, i,  
                    100);  
            Thread thread = new Thread(transferRunnable);  
            thread.start();  
        }  
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值