Thread2

进程

http://blog.csdn.net/eckotan/article/details/46854507

几乎所有操作系统都支持==进程==的概念,所有运行中的任务通常对应这一条进程(Process)。当一个程序进入内存(存储正在运行的程序和数据)运行时,就变成了一个进程。

注意区分并发性(concurrency)和并行性(parallel)这两个概念:

  • 并行(parallel):指在同一时刻,有==多条==指令在==多个处理器==上==同时==执行

  • 并发(Concurrency): 指在同一时刻,只能有==一==条指令执行,但多个进程指令被快速轮换执行,使得在宏观上具有多个进程同时执行的效果.

  • 线程(Thread)==也被称作 ==轻量级进程(Lightweight Process)==。线程(Thread)是进程(Process)的执行单元。就像进程在操作系统中的地位一样,线程在程序中是独立的并发的执行流。当进程被初始化之后,主线程就被创建了。对于大多数的应用程序来说,通常仅要求有一个主线程,但我们也可以在该进程内创建多条顺序执行流,这些顺序执行流就是Thread,每条Thread也是互相独立的.
  • 线程的划分尺度小于进程,使得多线程程序的==并发性高==。进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大的提高了程序的效率。
  • 系统创建进程必须为该进程分配独立的内存空间,并分配大量相关资源,但创建一个线程则简单的得多.
  • java使用Thread类代表线程,所有的线程都必须是Thread类或其子类。

    每条线程的作用是:==完成一定的任务,实际上就是执行一段程序流。==*java使用run方法来封装这样一段程序流,run方法也被成为==线程执行体==*

    1. 定义Thread类的子类,重写该类run方法。==run方法的方法体代表了该线程需要完成的任务==(想要它做什么,就往run方法里面写什么)。

    2. 创建线程对象

    3. 用线程对象的start方法来启动线程。

    package thread.createThread;
    
    public class FirstThread extends Thread {
    
        private int i;
    
        public void run() {
            for (; i < 20; i++) {
                // shiyong getName()方法来返回当前线程的名字
                System.out.println(this.getName() + " " + i);
            }
        }
    
        public static void main(String[] args) {
    
            for (int i = 0; i < 20; i++) {
    
                // 使用Thread类的静态方法 currentThread() 来获取当前线程
                System.out.println(Thread.currentThread().getName() 
                    + " " + i);
    
                if (i == 10) {
                    new FirstThread().start();
                }
                if (i == 15) {
                    new FirstThread().start();
                }
    
            }
        }
    }
    • 上面的程序只显式的启动了两条线程,但实际上有==3条线程==,因为还有包含main方法的主线程。主线程的线程体不是有run方法确定的,而是由main方法的方法体来确定。

    • 上面还用到了线程类的两个方法:

      • Thread.currentThread():返回当前正在执行的线程对象

      • getName():返回调用该方法的线程的名字。

        程序可以通过setName(String name)方法来为线程设置名字。默认情况下下主线程的名字为main,用户启动的多条线程名字依次为Thread-0, Thread-1…

    • 上面程序Thread-0和Thread-1输出的i并不连续,这是因为i是实例属性,程序每次创建线程对象都需要创建一个FirstThread对象,Thread-0和Thread-1不能共享i。(但如果把i设成static就可以)

      使用继承Thread类的方法来创建线程,多条线程之间无法共享实例变量

  • 实现Runnable接口创建线程类

    步骤:

    1. 定义实现Runnable接口的类,重写run方法

      public class SecondThread implements Runnable
           
           
      • 1
    2. 创建Runnable实现类的对象,并以此作为Thread的target来创建Thread对象,这个Thread对象才是真正的线程对象。

      SecondThread st = new SecondThread();
      
      new Thread(st, "NewThread");
           
           
      • 1
      • 2
      • 3

      Runnable对象仅仅作为Thread对象的Target(在创建Thread对象时作为参数传进构造方法,Runnale实现类里包含的run方法仅仅作为线程执行体。==而实际的线程对象依然是Thread类的实例,只是该Thread线程负责执行其Target的run方法而已==

    3. 调用线程对象的start方法来启动该线程

    例:

    package thread.createThread;
    
    public class SecondThread implements Runnable {
    
        private int i;
    
        public void run() {
            for (; i < 20; i++) {
                System.out.println(Thread.currentThread().getName() 
                    + " " + i);
            }
        }
    
        public static void main(String[] args) {
    
            for (int i = 0; i < 20; i++) {
                System.out.println(Thread.currentThread().getName() 
                    + " " + i);
                SecondThread st = new SecondThread();
                if (i == 10) {
                    new Thread(st, "newThread1").start();
                }
                if (i == 15) {
                    new Thread(st, "newThread2").start();
                }
    
            }
        }
    
    }
    
       
       
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 这时两个线程的i变量是连续的,因为程序所创建的Runnable对象只是线程的target,而多条线程可以共享同一个target,也就是说可以共享同一个target的所有实例变量。
    synchronized后括号里的obj就是同步锁定器。上面代码的含义是:==线程开始执行同步代码块之前,必须先获得对同步锁定器的锁定。==
  • 使用同步监视器的目的是:防止两条线程对同一个共享资源进行并发访问,因此==通常用可能被并发访问的共享资源当同步监视器==(比如上面的Account对象)
  • 使用同步监视器的逻辑是:

    ==加锁 ——> 修改 ——> 修改完成 ——> 释放锁==
  • 同步方法就是使用synchronized关键字来修饰某个方法,该方法称为同步方法
  • 另一种线程同步的机制:它通过显式定义同步锁对象来实现同步,在这种机制下,同步锁应该使用Lock对象更适合。

    Lock提供了比synchronized方法和代码块更广泛的锁定操作,Lock对象实现允许更灵活的结构,可以具有差别很大的属性,并可以支持多个相关的Condition对象。

    Lock是控制多个线程对共享资源进行访问的工具。通常,锁提供了对共享资源的独占访问,每次==只能有一个线程==对Lock对象加锁,线程开始访问共享资源之前应先获得Lock对象。不过,某些锁可能允许对共享资源进行并发访问,比如ReadWriteLock

    在实现线程安全的控制中,通常用ReentrantLock,使用该Lock对象可以显式的加锁、释放锁
  • 线程组(ThreadGroup)

    • java使用ThreadGroup来表示线程组,它可以对一批线程进行分类管理,对线程组的控制相当于同时控制这一批线程。

    • 用户创建的所有线程都属于==指定线程组==。如果程序没有显式的制定线程属于哪个线程组,那么该线程属于默认线程组。默认情况下,子线程和创建它的父线程处于同一个线程组内:例如A创建了B并且没有指定B的线程组,那么B属于线程A所在的线程组

    • 一旦某个线程加入了制定线程组之后,该线程将一直处于该线程组,知道该线程死亡,线程运行中途==不能改变它所属的线程组==。因此Thread类并没与提供setThreadGroup这样一个方法来改变线程所属的线程组,但是提供了一个getThreadGroup方法来返回该线程所属的线程组。

    • Thread类提供下面几个构造器来设置新家的线程属于哪个线程组:

      • Thread(ThreadGroup group, Runnable target)

      • Thread(ThreadGroup group, Runnnale target, String name)

      • Thread(ThreadGroup group, String name)
    • ThreadGroup类的构造方法:

      • ThreadGroup(String name)

      • Thread(ThreadGroup parent, String name)

        以指定的名字和父线程组来创建一个新线程组

      上面两个构造方法创建线程组实例时都必须为其制定一个名字,也就是线程组总是有一个字符串名字,改名字可调用ThreadGroup的getName()得到,==但不允许改变线程组的名字!==

    • 常用方法:

      • int activeCount()

        返回此线程组中活动线程的数目

      • interrupt()

        中断此线程组的所有线程

      • isDaemon()

        判断该线程是否是后台线程组

      • setDaemon(boolean daemon)

        把该线程组设厂后台线程组。后天线程组的特征是:当后条线程组的最后一个线程执行结束或最后一个线程被销毁,那么后台线程组将自动销毁

      • setMaxPriority(int pri)

        设置线程组的最高优先级

  • Callable是Runnable接口的增强版,Callable也提供了一个call()方法作为线程执行体,但它比run方法更加强大牛逼:

    • call()方法可以有返回值

    • call()方法可以声明抛出异常

    类似于Runnable的用法,我们可以提供一个Callable对象作为Thread的target,而call()方法作为该线程的线程执行体。问题是:Callable接口并不是Runnable接口的子接口,而Thread的构造方法里形参的类型是Runnable,所以Callable对象不能直接作为Thread的target;而且call方法还有一个返回值——但call方法不是直接调用,而是作为线程执行体被调用的。为了解决这几个问题,java提供了Future接口来代表Callable接口里call方法的返回值,并且为Future接口提供了一个FutureTask实现类,这个类实现了Future接口,也实现了Runnable接口(Adapter模式),这样就可以作为Thread类的target了。

  • Thread Pool(线程池)

    系统启动一个新线程的成本是比较高的,因为它涉及与操作系统交互。在这种情况下,使用线程池可以很好的提高性能,尤其是当程序中需要创建大量生存期很短暂的线程时,更应该考虑使用线程池。

    ==线程池在系统启动时就创建了大量空闲的线程,程序将一个Runnable对象传给线程池,线程池就会启动一条线程来执行该对象的run方法,当run方法执行结束之后,该线程不会死亡,而是再次返回线程池中成为空闲状态,等待执行下一个Runnable对象的run方法。==

    使用线程池可以有效的控制系统中并发线程的数量,当系统中包含大量并发线程时会导致系统性能严重下降,甚至会导致JVM崩溃,而线程池的最大线程参数可以控制系统中并发线程不超过此数目。

    java提供了一个Executors工厂类来生产线程池,包含下面几个==静态工厂方法==来创建线程池:

    • newCachedThreadPool()

      创建一个具有缓存功能的线程池,系统根据需要创建线程,这线程会被缓存在线程池中

    • newFixedThreadPool(int nThreads)

      创建一个可重用的、具有固定线程数的线程池

    • newSingleThreadExecutor()

      创建一个只有单线程的线程池,相当于newFixedThreadPool(int nThreads)传入参数为1

    • newScheduledThreadPool(int corePoolSize)

      • 创建具有固定线程数的线程池,可以在指定延迟后执行线程任务。

      • corePoolSize指池中所保存的线程数,即使线程是空闲的也被保存在线程池里

    • newSingleThreadScheduledExecutor()

      创建只有固定线程的线程池,可以在指定延迟后执行线程任务。

    前三个方法返回一个ExecutorService对象,该对象代表一个线程池,可以执行Runnable或Callable对象所代表的线程。

    ExecutorService代表==尽快执行线程的线程池(只要池中有空闲线程就立即执行线程任务)==,程序只需要将一个Runnable或Callable对象提交给该线程池即可,该线程池就会尽快执行该任务(实现的细节对客户解耦)。

  • ThreadLocal,是ThreadLocalVariable的意思。功能是:==为每一个使用该变量的线程都提供一个变量值的副本,使每一个线程都可以独立的改变自己的副本,而不会和其他线程的副本发生冲突。==从每一个线程的角度来看,好像每一个线程都==完全拥有该变量==
  • 包装线程不安全的集合

    ArrayList、LinkedList、HashSet、HashMap等都是==线程不安全==的,也就是有可能当多个线程向这些集合放入一个元素时,可能会破坏集合数据的完整性。

    可以使用Collections类提供的静态方法来把这些集合包装成线程安全的集合:

    • <T> Collection<T> synchronizedCollection(Collection<T> c)

      返回指定Collection对应的线程安全的Collection

    • static <T> List<T> synchronizedList(List<T> list)

      返回指定List对应的线程安全的List

    • static <K, V> Map<K, V> synchronizedMap(Map<K, V> m)

      返回指定Map对应的线程安全的Map

    • static <T> Set<T> synchronizedSet(Set<T> s)

      返回指定Set对应的线程安全的set

    • static <K, V> SortedMap<K, V> synchronizedSortedMap(SortedMap<K, V> m)

    如果需要把某个集合包装成线程安全的集合,应该在创建之后立即包装!==

    例:

    HashMap m = Collections.sychronizedMap(new HashMap());



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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值