笔记:多线程

多线程程序在较低的层次上扩展了多任务的概念:一个程序同时执行多个任务,通常每个任务称为一个线程(thread),他是线程控制的简称,可以同时运行一个以上线程的程序称为多线程程序(multithreaded);多线程和多进程有哪些区别呢,本质的区别在于每个进程拥有自己的一整套变量,而线程则是共享数据,Java中启动一个线程的代码如下:

//
线程任务的具体实现接口

????public interface Runnable {

public abstract
void
run();

????}

//
启用新线程执行任务

????Runnable runnable = new
MyRunnable();

????Thread newThread = new
Thread(runnable);

?
?

????newThread.start();

  • 中断线程

    当线程的run方法执行方法体中最后一条语句后,并经由执行return语句返回时,或者出现了在方法中没有捕获的异常时,线程将终止;还有一种可以强制线程终止的方法,interrupt
    方法可以用来请求终止线程,示例如下:

    while(Thread.currentThread().isInterrupted
    ==
    false && more work to do){

    ????do more work

    }

    如果线程被阻塞,就无法检测中断状态,如果在一个阻塞的线程(sleep或wait)上调用interrupt方法时,阻塞调用将会抛出InterruptedException异常,因此标准的线程run方法应具有如下形式:

    public
    void
    run(){

    ????try{

    ????????...

    ????????while(Thread.currentThread().isInterrupted()
    ==
    false && more work to do){

    ????????????// do more work

    ????????}

    ????????
    ?

    ????????// 返回结果

    ????}

    ????catch(InterruptedException e){

    ????????// 线程被阻塞,需要中断线程返回

    ????}

    ????finally{

    ????????// 释放资源

    ????}

    }

    如果在每次工作迭代之后都调用sleep方法(或者其他可中断方法),isInterrupted检测将没有任何用处,如果在中断状态被置为时调用sleep方法,他不会休眠,相反他将清除这个状态,并抛出
    InterruptedExcpetion。

  • 线程状态
  • 新创建(New)

    当使用New操作符创建一个新线程时,该线程还没有开始运行,这时他的状态是新创建(New)。

  • 可运行(Runnable)

    一旦调用start方法,线程将处于可运行(Runnable)状态,一个可运行的线程可能正在运行也可能没有运行,这取决与操作系统给线程提供运行的时间。

  • 被阻塞(Blocked)、等待(Waiting)和计时等待(Timed
    Waiting)

    当线程处于被阻塞或等待状态时,线程暂时不活动,不运行任何代码且消耗最小的资源,知道线程调度器重新激活他。

    • 被阻赛(Blocked):当一个线程试图获取一个内部的对象锁(不是 java.util.concurrent库的锁),而该锁被其他线程持有,则该线程进入阻塞状态,当所有线程释放该锁,并且线程调度器允许本线程持有该锁时,该线程将变成非阻塞状态。
    • 等待(Waiting):当线程等待另一个线程通知调度器一个条件时,该线程将进入等待状态,例如,在调用 Object.wait 方法或者Thread.join方法,或者等待 java.util.concurrent库的Lock或Condition时,就会出现这个情况。
    • 计时等待(Timed Waiting):有几个方法具有超时参数,调用他们导致线程进入计时等待(Timed Waiting),这一状态将一直保持到超时期满或者接收到适当通知,带有超时参数的方法有 Thread.sleep、Object.wait、Thread.join、Lock.tryLock以及 Condition.await方法。
  • 被终止(Terminated)

    当线程run方法正常退出而自然死亡;因为一个没有捕获的异常终止了run方法而意外死亡,此时线程状态为被终止。

  • 线程优先级

    在Java程序中,每个线程有一个优先级,默认情况下,一个线程继承他的父线程优先级,可以用
    setPriority方法提高或降低任何一个线程优先级,可以设置为MIN_PRIORITY(Thread类中定义为1)与MAX_PRIORITY(Thread类中定义为10)之间的任何值,NORM_PRIORITY(默认优先级)被定义为5。每当线程调度器有机会选择新线程时,他首先选择具有高优先级的线程,但是线程优先级时依赖与系统的,Java的线程优先级被映射到宿主主机平台的优先级上,优先级个人数也许更多,也许更少。

  • 守护线程

    守护线程的唯一用途时为其他线程提供服务,如果只剩下守护线程,虚拟机都退出了,因此,守护线程应该永远不去访问固有资源,如文件、数据库,因为他任何时候甚至在一个操作的中间发生中断,可以通过调用Thread实例的
    setDaemon(true) 来将线程转换为守护线程,必须在线程启动之前调用。

  • 未捕获异常处理器

    线程的run方法不能抛出任何被检测的异常,但是,不被检测的异常会导致线程终止,在这种情况下,线程就死亡了,不需要任何catch子句来处理可以被传播的异常,就在线程死亡之前,异常被传播到一个用于未捕获异常的处理器,该处理器必须实现
    Thread.UncaughtExceptionHandler接口的类,该接口只有一个方法定义:

    ??public interface UncaughtExceptionHandler {

    ????????void
    uncaughtException(Thread t, Throwable e);

    ??}

    可以使用
    setUncaughtExceptionHandler方法未任何线程安装一个处理器,也可以使用Thread类的静态方法
    setDefaultUncaughtExceptionHandler
    为所有线程安装一个默认处理器。

  • 同步锁对象

    有两种机制防止代码块受并发访问的干扰,Java语言提供一个synchronized
    关键字达到这目的,并且在
    Java
    SE5.0
    引入了
    ReentrantLock类(重入锁),synchronized关键字自动提供一个锁以及相关条件,使用
    ReentrantLock类的代码示例如下:

    package org.drsoft.mybatisExamples.example_thread;

    ?
    ?

    import java.util.concurrent.locks.Lock;

    import java.util.concurrent.locks.ReentrantLock;

    ?
    ?

    public class Bank {

    ????private Lock bankLock = new
    ReentrantLock();

    ?
    ?

    ????public
    void
    transfer(int form, int to, int amount) throws InterruptedException {

    ????????bankLock.lock();

    ????????try {

    ????????????System.out.println("ThreadID=" + Thread.currentThread().getId() + "\tForm=" + form + "\tTo=" + to+ "\tAmount=" + amount);

    ????????????Thread.sleep((int) (10 * Math.random()));

    ????????} finally {

    ????????????bankLock.unlock();

    ????????}

    ????}

    }

  • 条件锁对象

    通常,线程进入临界区,却发现在某一条件满足之后才能执行,要使用一个条件对象来管理哪些已经获得了一个锁,当是却不能做有用工作的线程,一个锁对象可以有一个或者多个相关的条件对象,可以使用
    newCondition方法获得一个条件,可以调用条件的
    await方法阻塞线程,直到另一个线程的调用同一个条件上的
    signalAll方法重新激活因为这个条件等待的所有线程,示例代码如下:

    package org.drsoft.mybatisExamples.example_thread;

    ?
    ?

    import java.util.concurrent.locks.Condition;

    import java.util.concurrent.locks.Lock;

    import java.util.concurrent.locks.ReentrantLock;

    ?
    ?

    public class Bank {

    ????private Lock bankLock = new
    ReentrantLock();

    ????private Condition toCondition;

    ?
    ?

    ????public
    Bank() {

    ????????toCondition = bankLock.newCondition();

    ????}

    ?
    ?

    ????public
    void
    transfer(int form, int to, int amount) throws InterruptedException {

    ????????bankLock.lock();

    ????????try {

    ????????????if (to == 2) {

    ????????????????System.out.println("ThreadID=" + Thread.currentThread().getId() + "\tawait");

    ????????????????toCondition.await();

    ????????????}

    ????????????System.out.println("ThreadID=" + Thread.currentThread().getId() + "\tForm=" + form + "\tTo=" + to+ "\tAmount=" + amount);

    ????????????toCondition.signalAll();

    ????????????Thread.sleep((int) (10 * Math.random()));

    ????????} finally {

    ????????????bankLock.unlock();

    ????????}

    ????}

    }

  • synchronized关键字

    使用
    synchronized
    关键字声明在方法中,将保护整个方法,也就是说,要调用该方法,线程必须获得内部得对象锁,代码示例如下:

    public synchronized
    void
    method(){

    ????//method body

    }

    等价与如下代码:

    public
    void
    method(){

    ????this.intrinsicLock.lock();

    ????try{

    ???????????// method body

    ????}

    ????finally{

    ?????????this.intrinsicLock.unlock();

    ????}

    ?}

    内部锁只有一个相关条件,wait方法添加一个线程到等待集,notifyAll或notify方法解除等待线程得阻塞状态,可以使用
    synchronized关键字实现前面得代码:

    package org.drsoft.mybatisExamples.example_thread;

    ?
    ?

    public class Bank {

    ?
    ?

    ????public synchronized
    void
    transfer(int form, int to, int amount) throws InterruptedException {

    ????????if (to == 2) {

    ????????????System.out.println("ThreadID=" + Thread.currentThread().getId() + "\tawait");

    ????????????wait();

    ????????}

    ????????System.out.println(

    ????????????????"ThreadID=" + Thread.currentThread().getId() + "\tForm=" + form + "\tTo=" + to +

    "\tAmount=" + amount);

    ????????notifyAll();

    ????????Thread.sleep((int) (10 * Math.random()));

    ????}

    }

    将静态方法声明为
    synchronized也是合法的,如果调用这种方法,该方法获得相关的类对象的内部锁,因此,没有其他线程可以调用同一个类的这个或任何其他的同步静态方法。

  • 同步阻塞

    每一个Java对象有一个锁,线程可以通过调用同步方法获得锁,还有另一种机制可以获得锁,通过进入一个同步阻塞,当线程进入如下形式的阻塞:

    synchronized(obj){

    ????//critical section

    }

    有时候程序员使用一个对象的锁来实现额外的原子操作,实际上称为客户端锁定。

  • volatile关键字

    如果向一个变量写入值,而这个变量接下来可能会被另一个线程读取,或者,从一个变量读值,而这个变量可能是之前被另一个线程写入的,此时必须使用
    volatile关键字标识,volatile关键字为实例域的同步访问提供了一种免锁机制,如果声明一个域为
    volatile,那么编译器和虚拟机就知道这该域是可能被另一个线程并发更新的,注意:volatile关键字不能提供原子性操作。

  • final变量

    还有一种方法可以安全的访问一个共享域,即这个域声明为
    final时,例如如下代码:

    final Map<String,Double> accountMap = new HashMap<String,Double>();

    如果不声明为
    final,就不能保证其他线程看到的是实例化的值,有可能看到的只是
    null,而不是新构造的HashMap。

  • 原子性

    java.util.concurrent.atomic包中有很多类使用了高效的机器指令来保证其他操作的原子性,例如,AtomicInteger类,提供了方法
    incrementAndGet

    decrementAndGet方法,他们分别以原子方式将一个整数自增或自减,另外这个包中还提供了
    AtomicBoolean、AtomicLong和AtomicReference等相关类。

  • 线程局部变量

    有时候可能避免共享变量,使用ThreadLocal辅助类为各个线程提供各自的实例,示例代码如下:

    package org.drsoft.mybatisExamples.example_thread;

    ?
    ?

    import java.text.SimpleDateFormat;

    import java.util.Date;

    ?
    ?

    public class ThreadLocalExample {

    ????public static final ThreadLocal<SimpleDateFormat> dataFormat = new ThreadLocal<SimpleDateFormat>(){

    ????????protected SimpleDateFormat initialValue(){

    ????????????return new
    SimpleDateFormat("yyyy-MM-dd");

    ????????}

    ????};

    ????
    ?

    ????public static
    void
    main(String[] args) {

    ????????String dateStamp = dataFormat.get().format(new Date());

    ????????System.out.println("dateStamp="+dateStamp);

    ????}

    }

    在一个指定的线程首次调用
    get
    方法时,会调用initialValue方法进行初始化,在此之后,get方法会返回属于当前线程的哪个实例。

  • 读写锁(ReentrantReadWriteLock)

    如果很多线程从一个数据结构读取数据,而很少的线程修改其中的数据的话,读写锁时十分有用的,在这种情况下,允许读数据线程访问共享时合适的,写线程依然必须时互斥访问的,下面是使用读写锁的必要步骤:

    • 构造一个ReentrantReadWriteLock对象:

      private ReentrantReadWriteLock
      rwl = new
      ReentrantReadWriteLock();

    • 抽取读锁和写锁:

      private Lock readLock = rwl.readLock();

      private Lock writeLock = rwl.writeLock();

    • 对所有的获取方法加读锁:

      ????????????public
      double
      getTotalBalance(){

      ????????????????realLock.lock();

      ????????????????try{

      ????????????????// 读取数据代码

      ????????????????}

      ????????????????finally{realLock.unlock();}

      ????????????}

    • 对所有修改方法加写锁:

      ????????????public
      void
      transfer(…){

      ????????????????writeLock.lock();

      ????????????????try{

      ????????????????// 写入数据代码

      ????????????????}

      ????????????????finally{writeLock.unlock();}

      ????????????}

  • Callable

    Future

    前面创建线程对象时,使用的是
    Runnable接口,该接口封装异步运行的任务,是没有参数和返回类型的异步返回;Callable 和 Runnable类似,但Callable是有返回值,该接口只有一个方法 call,接口定义如下:

    public interface Callable<V> {

    ????????V call() throws Exception;

    }

    其类型参数是call方法的返回值的类型;Futrue保持异步计算的结果,可以启动一个计算,将Futrue对象交给线程,Futrue
    对象的所有者在结果计算好之后就可以获取他,Futrue接口定义如下:

    public interface Future<V> {

    ????????boolean
    cancel(boolean mayInterruptIfRunning);

    ????????boolean
    isCancelled();

    ????????boolean
    isDone();

    ????????V get() throws InterruptedException, ExecutionException;

    ????????V get(long timeout, TimeUnit unit)

    ????????????????throws InterruptedException, ExecutionException, TimeoutException;

    }

    get方法的调用被阻塞,可以设置一个超时时间,如果计算完成则会获取计算结果,如果在超时时间到达后,则会抛出 TimeoutException 异常,如果调用方法被中断,则会抛出 InterruptedException 异常,调用实例如下:

    ????????LogCallable callable = new
    LogCallable("高优先级线程");

    ????????FutureTask<Integer> futureTask = new FutureTask<Integer>(callable);

    ????????Thread newMaxPriorityThread = new
    Thread(futureTask);

    ????????newMaxPriorityThread.setPriority(Thread.MAX_PRIORITY);

    ????????newMaxPriorityThread.setDaemon(false);

    ????????newMaxPriorityThread.start();

    ?
    ?

    ????????int val = futureTask.get();

    ????????System.out.println("Thread value is " + val);

    ?
    ?

    ?
    ?

?
?

?
?

?
?

?
?

?
?

?
?

?
?

时间: 2024-08-06 20:07:40

笔记:多线程的相关文章

IOS学习笔记 -- 多线程

多线程1.多线程的原理 1>.同一时间,CPU只能处理1条线程,只有1条线程在工作(执行) 2>.多线程并发(同时)执行,其实是CPU快速地在多条线程之间调度(切换) 3>.如果CPU调度线程的时间足够快,就造成了多线程并发执行的假象如果线程非常非常多,会发生: 1>.CPU会在N多线程之间调度,CPU会累死,消耗大量的CPU资源 2>.每条线程被调度执行的频次会降低(线程的执行效率降低) 2.多线程的优缺点 1>.多线程的优点 能适当提高程序的执行效率 能适当提高资源

Linux程序设计学习笔记----多线程编程线程同步机制之互斥量(锁)与读写锁

互斥锁通信机制 基本原理 互斥锁以排他方式防止共享数据被并发访问,互斥锁是一个二元变量,状态为开(0)和关(1),将某个共享资源与某个互斥锁逻辑上绑定之后,对该资源的访问操作如下: (1)在访问该资源之前需要首先申请互斥锁,如果锁处于开状态,则申请得到锁并立即上锁(关),防止其他进程访问资源,如果锁处于关,则默认阻塞等待. (2)只有锁定该互斥锁的进程才能释放该互斥锁. 互斥量类型声明为pthread_mutex_t数据类型,在<bits/pthreadtypes.h>中有具体的定义. 互斥量

java笔记--多线程基础

多线程技术 在java中实现多线程技术有两种方式: 1.继承Thread类: 2.实现Runnable接口 这两种方法都需要重写run()方法:通常将一个新线程要运行的代码放在run()方法中(这是创建没有返回值线程的方法)由于java只支持单继承,当类已经继承有其他类时,只能选择实现Runnable接口在启动线程时需要使用Thread类的start()方法,而不是直接使用run()方法: 如: public static void function() { for (int i = 0; i

[开发笔记]-多线程异步操作如何访问HttpContext?

如何获取文件绝对路径? 在定时器回调或者Cache的移除通知中,有时确实需要访问文件,然而对于开发人员来说, 他们并不知道网站会被部署在哪个目录下,因此不可能写出绝对路径, 他们只知道相对于网站根目录的相对路径,为了定位文件路径,只能调用HttpContext.Current.Request.MapPath或者 HttpContext.Current.Server.MapPath来获取文件的绝对路径. 如果HttpContext.Current返回了null,那该如何如何访问文件? 其实方法并非

Linux程序设计学习笔记----多线程编程之线程同步之条件变量

转载请注明出处:http://blog.csdn.net/suool/article/details/38582521. 基本概念与原理 互斥锁能够解决资源的互斥访问,但是在某些情况下,互斥并不能解决问题,比如两个线程需 要互斥的处理各自的操作,但是一个线程的操作仅仅存在一种条件成立的情况下执行,一旦错过不可再重现,由于线程间相互争夺cpu资源,因此在条件成立的时候,该线程不一定争夺到cpu而错过,导致永远得不到执行..... 因此需要某个机制来解决此问题,更重要的是,线程仅仅只有一种情况需要执

JAVA学习笔记 -- 多线程之共享资源

在多线程程序运行过程中,可能会涉及到两个或者多个线程试图同时访问同一个资源.为了防止这种情况的发生,必须在线程使用共享资源时给资源"上锁",以阻挡其它线程的访问.而这种机制也常常被称为互斥量,本文主要介绍它的两种方式synchronized和Lock . 1.synchronized 当任务要执行被synchronized关键字保护的代码片段的时候,它会检查锁是否可用,然后获取锁,执行代码,释放锁.synchronized也有两种用法: A.synchronized方法 import

Linux程序设计学习笔记----多线程编程基础概念与基本操作

转载请注明出处,http://blog.csdn.net/suool/article/details/38542543,谢谢. 基本概念 线程和进程的对比 用户空间资源对比 每个进程在创建的时候都申请了新的内存空间以存储代码段\数据段\BSS段\堆\栈空间,并且这些的空间的初始化值是父进程空间的,父子进程在创建后不能互访资源. 而每个新创建的线程则仅仅申请了自己的栈,空间,与同进程的其他线程共享该进程的其他数据空间包括代码段\数据段\BSS段\堆以及打开的库,mmap映射的文件与共享的空间,使得

11.1-全栈Java笔记:多线程技术的基本概念

多线程是JAVA语言的重要特性,大量应用于网络编程.应用于服务器端程序的开发.以及最常见的UI界面底层原理.操作系统底层原理都大量使用了多线程. 我们可以流畅的点击软件或者游戏中的各种按钮,其实,底层就是多线程的应用.UI界面的主线程绘制界面,如果有一个耗时的操作发生则启动新的线程,完全不影响主线程的工作.当这个线程工作完毕后,再更新到主界面上. 我们可以上百人.上千人.上万人同时访问某个网站,其实,网站服务器也是基于多线程的原理.如果没有多线程,服务器处理速度会极大降低. 多线程应用于计算机的

java学习笔记 多线程

进程是程序的一次动态执行过程,需要经历代码加载,代码执行,执行完毕的一个完整过程. 多进程操作系统能同时运行多个进程(程序),由于cpu具有备份时机制,所以每个进程都能循环获得自己的cpu时间片.由于cpu执行速度非常快,使得所有程序好像是在"同时运行"一样. 线程是比进程更小的单位,线程是在进程基础上进一步的划分.多线程是指一个进程在执行过程中可以产生多个更小的程序单元,这些更小的程序单元称之为线程.(java在运行中至少会启动两个线程:1,main线程 2,垃圾回收线程) 线程的实

Java基础笔记-多线程

线程: 方式一:继承Thread类并且复写run方法. 格式: class MyThread extends Thread { public void run() { 线程中要运行的代码. } } 其步骤为: 1.定义类继承Thread类. 2.复习run方法.---->目的是:用于存储线程中要运行的代码,将自定义的代码存储在run方法中,让线程运行. 3.调用线程的start方法 注意:不是调用run方法. 线程的名字:一般线程有默认的名字,形式为:Thread-编号,编号是从0开始标记的.