`
ccii
  • 浏览: 54789 次
  • 性别: Icon_minigender_1
  • 来自: 成都
社区版块
存档分类
最新评论
阅读更多
一、多线程简介
  对于Java而言,可以在一个程序中并发地启动多个线程,让这些线程在多处理器上同时运行。在单处理器系统中,多个线程共享CPU时间称为时间共享,而操作系统负责调度及分配资源给它们,即使是单处理器,多线程程序的运行速度也比单线程程序更快。
  当程序作为一个应用程序运行时,Java解释器为main方法启动一个线程。当程序作为一个applet运行时,Web浏览器启动一个线程来运行applet。同时我们可以在程序中创建附加的线程以执行并发任务。

二、创建任务和线程
1. 实现方法一
  首先,我们为任务定义一个类,任务类必须实现Runnable接口,它只包含一个run方法,需要实现这个方法来告诉系统线程将如何运行。任务必须在线程中执行。Thread类包括创建线程的构造方法以及控制线程的很多有用的方法。
  示例如下:
package test;
public class Test {
    public static void main(String[] args) {
        //创建任务
        PrintString print = new PrintString("jiang");
        //创建任务的线程
        Thread thread = new Thread(print);
        //调用start告诉java虚拟机线程已准备就绪
        thread.start(); 
    }
}

//定义任务类,实现Runnable接口,重载run方法
class PrintString implements Runnable{
    private String strToPrint;
    public PrintString(String str){
        this.strToPrint = str;
    }
    public void run() {
        System.out.println(this.strToPrint);
    }
}

  在调用start告诉java虚拟机线程已准备就绪后,Java虚拟机通过调用任务的run()方法执行任务。

2. 实现方法二
  因为Thread类实现了Runnable接口,故可以定义一个Thread类的扩展类,并且实现run方法。它将任务和运行任务的机制混在了一起,故不推荐使用这种方法。
package test;
public class Test {
    public static void main(String[] args) {
        TestThread tt = new TestThread("qin");
        tt.start();
    }
}
//直接定义Thread类的扩展类,实现run方法
class TestThread extends Thread{
    private String strToPrint;
    public TestThread(String str){
        this.strToPrint = str;
    }
    public void run() {
        System.out.println(this.strToPrint);
    }
}


三、Thread类
  Thread类包含为任务而创建的线程的构造方法,以及控制线程的方法。
    Thread() //创建一个空线程
    Thread(Runnable task) //为指定任务创建一个线程
    public void start() //启动线程使任务的run()被JVM调用
    public boolean isAlive() //测试线程当前是否正在运行,创建和结束状态时返回false,就绪、阻塞、运行状态时返回true。
    public int getPriority() //得到线程的优先级,默认线程继承生成它的线程的优先级
    public void setPriority(int p) //设置线程的优先级p(范围从1到10)
    public void join() //使此线程等待另一个线程的结束,在另一个线程里面调用
    public void sleep(long mi) //使线程睡眠指定的毫秒数
    public void yield() //使线程暂停并允许执行其他线程,为其他线程临时让出SPU时间
    public void interrupt() //中断线程,就绪或运行状态时给他设置一个中断标志,阻塞状态时它将被唤醒进入就绪状态并抛出InterruptedException异常。
  Thread类还包含方法stop()、suspend()、resume(),由于普遍认为这些方法具有内在有不安全因素,所以不提倡使用这些方法。为替代stop()的使用可以通过给Thread变量赋值null来表明它被停止。
  Thread类有int型常量MIN_PRIORITY、NORM_PRIORITY、MAX_PRIORITY,分别代表1,5,10。Java虚拟机总是选择当前优先级最高的可运行线程。如果所有可运行线程具有相同的优先级,那将会用循环队列给它们分配相同的CPU份额。但我在测试时发现并没有绝对的按优先级来调度,测试如下:
public class TestRunnable1 implements Runnable {
  @Override
  public void run() {
    for (int i = 1; i <= 1000; i++) {
      System.out.println("A : " + i);
    }
  }
}

public class TestRunnable2 implements Runnable {
  @Override
    public void run() {
      for (int i = 1; i <= 1000; i++) {
        System.out.println("B : " + i);
      }
    }
}

public class TestThead {
  public static void main(String[] args) {
    Thread t1 = new Thread(new TestRunnable1());
    Thread t2 = new Thread(new TestRunnable2());
    t1.setPriority(6);
    t2.setPriority(Thread.MAX_PRIORITY);
    System.out.println("A Priority : " + t1.getPriority());
    System.out.println("B Priority : " + t2.getPriority());
    t1.start();
    t2.start();
  }
}

  虽然绝大部分t2都会在t1前面输出,但t2输 出中总会插几条t1的输出。

四、线程池
  线程池是管理并发执行任务个数的理想方法。所以,如果需要为一个任务创建一个线程,就使用Thread类。如果需要为多个任务创建线程,最好使用线程池。
1. Executor接口
  Java提供Executor接口来执行线程池中的任务,执行方法如下:
    public void execute(Runnable r)

2. ExecutorService接口
  ExecutorService是Executor的子接口,用来管理和控制任务,如下:
    public void shutdown() //关闭执行器,但允许完成执行器中的任务,但不能接受新任务
    public List<Runnable> shutdownNow() //关闭执行器,返回未完成任务的清单
    public boolean isShutdown() //如果执行器已经被关闭则返回true
    public boolean isTerminated() //如果线程池中所有的任务都被终止,则返回true。

3. Executors工具类
  Java提供了Executors工具类来创建Executor对象,如下:
    //1. 创建一个线程池,该线程池可并发执行的线程数固定不变。它可以被重用以执行另一个任务。如果某个线程因意外结束,线程池会补充一个新线程。
    public static ExecutorService newFixedThreadPool(int n);
    public static ExecutorService newFixedThreadPool(int n, ThreadFactory tf);

    //2. 创建一个线程池,它可按需创建新线程,线程池大小完全依赖于操作系统或者JVM能够创建的最大线程数,但当前面创建的线程可用时,则重用它们。
    public static ExecutorService newCachedThreadPool();
    public static ExecutorService newCachedThreadPool(ThreadFactory tf);

    //3. 创建一个线程池,它只有一个线程在工作,相当于单线程串行执行所有任务。如果该因异常结束,那么会有一个新建一个线程来代替它。此线程池保证所有任务按提交顺序执行。
    public static ExecutorService newSingleThreadExecutor();
    public static ExecutorService newSingleThreadExecutor(ThreadFactory tf);

    //4. 创建一个线程池,此线程池支持定时以及周期性执行任务的需求。
    public static ScheduledExecutorService newScheduledThreadPool(int n);
    public static ScheduledExecutorService newScheduledThreadPool(int n, ThreadFactory tf);

    //5. 创建一个线程池,单线程串行执行,支持定时以及周期性执行任务的需求。
    public static ScheduledExecutorService newSingleThreadScheduledExecutor();
    public static ScheduledExecutorService newSingleThreadScheduledExecutor(ThreadFactory tf);

4. 示例
public class Test {
    public static void main(String[] args) {
        //创建一个最大线程数为3的线程执行器
        ExecutorService executor1 = Executors.newFixedThreadPool(3);
        //为每个等待的任务创建一个新线程,所有的任务都并发的执行
        ExecutorService executor2 = Executors.newCachedThreadPool();
        executor1.submit(new PrintString("wang"));
        executor2.submit(new PrintString("mao"));
        executor. 
    }
}
//定义任务类,实现Runnable接口,重载run方法
class PrintString implements Runnable{
    private String strToPrint;
    public PrintString(String str){
        this.strToPrint = str;
    }
    public void run() {
        System.out.println(this.strToPrint);
    }
}


五、线程同步
  共享资源在被多个线程同时访问时,可能遭到破坏。为避免竞争状态,应该防止多个线程同时进入程序的某一特定部分(临界区)。
1. synchronized
  我们可以使用synchronized关键字来同步方法或语句块,以便一次只在一个线程可以访问。如下:
//测试同步方法
public synchronized void printStri(){
  for(int i=0; i<100;i++){
    System.out.println(i);
  }
}

//测试同步块,同步语句不仅可以对this对象加锁,而且可用于对任务对象加锁。
public void printStri(){
  synchronized(this){
    for(int i=0; i<100;i++){
      System.out.println(i);
    }
  }
}


2. 利用加锁同步
  一个锁是一个Lock接口的实例,它定义了加锁和释放锁的方法。操作如下:
    public void lock() //加锁
    public void unlock() //释放锁
    public Condition newCondition() //返回到绑定到Lock实例的新的Condition实例

  ReentrantLock类是Lock接口的一个具体实现,它创建一个相互排斥的锁,如下:
    ReentrantLock() //等价于ReentrantLock(false)
    ReentrantLock(boolean f) //创建一个具有公平策略的锁,true时等待时间最长的线程将获得锁。false时没有特定的顺序。
  实例如下:
private static class PrintStr{
  //创建一个锁
  private static Lock lock = new ReentrantLock();
  //测试同步方法
  public void printStr(){
    //加锁
    lock.lock();
    try{
      for(int i=0; i<100;i++){
        System.out.println(i);
      }
    } catch(Exception e){
      e.printStackTrace();
    }finally {
      //解锁
      lock.unlock();
    }
  }
}


六、线程间协作
  通过保证在临界区上多个线程的相互排斥,线程同步完全可以避免竞争状态的发生,但是有些时候还需要线程之间的协作。使用条件可以便于线程间的通信,条件是通过调用Lock对象的newCondition()方法而创建的对象。一旦创建了条件,就可以使用await()、signal()、signlAll()方法来实现线程之间的相互通信。Condition具体操作如下:
    public void await() //当前线程等待直到发生某个条件,同普通对象的wait()方法
    public void signal() //唤醒一个等待线程,同普通对象的notify()方法
    public  Condition signalAll() //唤醒所有等待的线程,同普通对象的nofityAll()方法
  示例:
package test;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class TestCondition {
  private static Account account = new Account();
  public static void main(String[] args) {
    ExecutorService executorService = Executors.newFixedThreadPool(2);
    executorService.execute(new DepositTask());
    executorService.execute(new WithdrawTask());
    executorService.shutdown();
  }

  /**
   * 充值任务类
    */
  public static class DepositTask implements Runnable {
    @Override
    public void run() {
      try {
        account.deposit((int) (Math.random() * 10) + 1);
        Thread.sleep(1000);
      } catch (InterruptedException e) {
        // TODO: handle exception
      }
    }
  }

  /**
   * 取款任务类
   */
  public static class WithdrawTask implements Runnable {
    @Override
    public void run() {
      while (true) {
        account.withdraw((int) (Math.random() * 10) + 1);
      }
    }
  }

  /**
   * 账户类
    */
  public static class Account {
    private static Lock lock = new ReentrantLock();
    private static Condition newDeposit = lock.newCondition();
    private int balance = 0; // 账户金额

    public int getBalance() {
      return balance;
    }

    public void withdraw(int amount) {
      lock.lock();
      try {
        while (balance < amount) {
          System.out.println("余额不足,等待充值");
          newDeposit.await();
        }
        balance -= amount;
        System.out.println("取款:" + amount + "余额:" + balance);
      } catch (InterruptedException e) {
        // TODO: handle exception
      } finally {
        lock.unlock();
      }
    }

    public void deposit(int amount) {
      lock.lock();
      try {
        while (balance < 10) {
          balance += amount;
          System.out.println("充值:" + amount + "余额:" + balance);
          newDeposit.signalAll();
        }
      } finally {
        lock.unlock();
      }
    }
  }
}

  警告:为了使用条件,必须首先获取锁。一旦线程调用条件上的await(),线程就进入等待状态,等待恢复的信号。如果忘记对状态调用signal()或者signalAll(),那么线程就永远的等待下去。
  线程通信是使用对象的内置监视器编程实现的。

七、阻塞队列
  阻塞队列是一个在试图向满队列添加元素或从空队列中删除元素时会导致线程阻塞。
1. BlockingQueue接口
  BlockingQueue接口扩展自java.util.Queue,并且提供了同步的put和take方法向队列头添加元素和队列尾删除元素。

2. ArrayBlockingQueue类
  ArrayBlockingQueue类扩展了BlockingQueue接口,它使用数组实现阻塞队列,必须指定一个容量或者可选的公平性来构造它。

3. LinkedBlockingQueue类
  LinkedBlockingQueue类也扩展了BlockingQueue接口,它使用链表实现阻塞队列,可以创建受限的或不受限的LinkedBlockingQueue。不受限表示put元素时永远不会阻塞。

4. PriorityBlockingQueue类
  PriorityBlockingQueue类还是扩展的BlockingQueue接口,它是优先队列,可以创建受限的或不受限的优先队列。不受限表示put元素时永远不会阻塞。

八、信号量
  信号量是用来限制共享资源的线程数。在访问资源之前,线程必须从信号量获取许可(如果许可不可用就等待)。在访问完资源之后,这个线程必须将许可返回给信号量。为了创建信号量,必须使用可选择的公平策略来确定许可的数量。Semaphore信号量类使用如下:
    Semaphore(int n) //创建一个带指定数目许可的信号量,公平策略为false。
    Semaphore(int n, boolean fair) //创建一个带指定数目许可和公平策略的信号量。
    public void acquire() //获取信号量的许可,如果无许可可用,线程就被锁住直到有可用许可为止。
    public void release() //释放一个许可
  只有一个许可的信号量可以用来模拟一个相互排斥的锁。将release()方法放到finally中。

九、避免死锁
  有时两个或多个线程需要在几个共享对象上获取锁,可能会导致死锁,也就是说,每个线程已经锁定一个对象,而且正在等待一个已经被锁定的另一个对象。
  使用一种名为资源排序的简单技术可以轻易地避免死锁的发生。

十、线程的状态
  线程可以是以下五种状态之一:
    1)新建:新建一个线程时。
    2)就绪:调用线程的start()方法启动线程后、CPU时间用完、调用线程的yield()方法、休眠时间到。
    3)运行:获得CPU时间开始执行。
    4)阻塞:调用了join()、sleep()、wait()方法。
    5)结束:执行完run()方法这个线程就被结束。

十一、同步集合
  Java集合框架中的类不是线程安全的,也就是说,如果它们同时被多个线程访问和更新,它们的内容可能被破坏。可以通过锁定集合或者同步集合保护集合中的数据。
  Collections类提供六个静态方法将集合转成同步版本,在它里面的所有访问和更新原来的集合c的方法都被同步,使用这些方法创建的集合称为同步包装类。如下:
    public Collection synchronizedCollection(Collection c) //返回同步集合
    public List synchronizedList(List l) //返回同步线性表
    public Map synchronizedMap(Map m) //返回同步图
    public Set synchronizedSet(Set s) //返回同步规则集
    public SortedMap synchronizedSortedMap(SortedMap s) //返回同步有序图
    public SortedSet synchronizedSortedSet(SortedSet s) //返回同步有序规则集
  在Vector、Stack、Hashtable中的方法已经被同步,它们都是java中出现的旧类,应该用ArrayList代替Vector,LinkedList代替Stack,Map代替Hashtable。如果需要同步,就使用同步包装类。需要注意的是这些包装类具有快速失败的特性。
2
1
分享到:
评论

相关推荐

    Java多线程设计模式上传文件

    Java多线程设计模式上传文件Java多线程设计模式上传文件Java多线程设计模式上传文件Java多线程设计模式上传文件Java多线程设计模式上传文件Java多线程设计模式上传文件Java多线程设计模式上传文件Java多线程设计模式...

    java多线程读取文件

    Java多线程读大文件 java多线程写文件:多线程往队列中写入数据

    java多线程ppt

    java多线程PPT 多线程基本概念 创建线程的方式 线程的挂起与唤醒 多线程问题

    java 多线程操作数据库

    一个java 多线程操作数据库应用程序!!!

    java多线程经典案例

    java多线程经典案例,线程同步、线程通信、线程阻塞等经典案例

    Java多线程编程技术

    《Java多线程编程核心技术》建议猿友们读两遍,因为其写得没有那么抽象,第一遍有些概念不是很理解,可以先跳过并记录起来,第一遍阅读的目的主要是了解整个架构。第二遍再慢慢品味,并贯穿全部是指点来思考,并将...

    Java多线程编程实战指南(核心篇)

    Java多线程编程实战指南(核心篇) 高清pdf带目录 随着现代处理器的生产工艺从提升处理器主频频率转向多核化,即在一块芯片上集成多个处理器内核(Core),多核处理器(Multicore Processor)离我们越来越近了――如今...

    Java多线程知识点总结

    该文档总结了Java多线程相关的知识点,分享给大家,简单易懂!

    java多线程的讲解和实战

    详细的讲解了java多线程的原理,并配有代码进行实战,适合java初学者和想对多线程有进一步了解的人。

    java多线程通信图解

    一张图方便理解和掌握java 多线程之间通信的实质 java 多线程 其实就是每个线程都拥有自己的内存空间,多线程之间的通信,比例A线程修改了主内存(main方法的线程)变量,需要把A线程修改的结果同步到主线程中,...

    java多线程处理数据库数据

    java多线程处理数据库数据,使用并发包,无框架,可批量处数据库数据,进行增删改。。等等操作。

    java多线程,对多线程,线程池进行封装,方便使用

    java多线程,对多线程,线程池进行封装,方便使用

    Java多线程编程经验

    现在的操作系统是多任务操作系统。多线程是实现多任务的一种方式。 线程是指进程中的一个执行流程,一个进程中可以运行多个线程。...本文档提供Java多线程编程经验,方便广大Java爱好者研究学习Java多线程

    java多线程处理大数据

    java多线程处理大数据,可根据配置的线程数,任务去调度处理

    java多线程并发

    java多线程并发的在新窗口

    Java多线程机制(讲述java里面与多线程有关的函数)

    Java多线程机制 9.1 Java中的线程 9.2 Thread的子类创建线程 9.3 使用Runable接口 9.4 线程的常用方法 9.5 GUI线程 9.6 线程同步 9.7 在同步方法中使用wait()、notify 和notifyAll()方法 9.8 挂起、恢复和终止线程 ...

    java多线程核心技术

    资深Java专家10年经验总结,全程案例式讲解,首本全面介绍Java多线程编程技术的专著 结合大量实例,全面讲解Java多线程编程中的并发访问、线程间通信、锁等最难突破的核心技术与应用实践 Java多线程无处不在,如...

    java多线程实现大批量数据导入源码

    java多线程实现大批量数据切分成指定份数的数据,然后多线程处理入库或者导出,线程的个数和每份数据的数量都可以控制

    java多线程查询数据库

    java多线程并发查询数据库,使用线程池控制分页,并发查询。

    java多线程模拟队列实现排队叫号

    java多线程模拟队列实现排队叫号,多线程模拟排队叫号取号 java多线程模拟队列实现排队叫号,多线程模拟排队叫号取号

Global site tag (gtag.js) - Google Analytics