Java 多线程学习集合

相对于多进程,多线程的优势如下:

  1. 进程之间不能共享数据,线程可以。
  2. 系统需要为新创建的进程重新分配系统资源,而创建线程代价较小。
  3. java内置多线程功能,简化编程。

线程的创建和启动有三种方式:

继承Thread类创建线程类,步骤如下:

  1. 定义一个继承Thread类的子类,重写run()方法
  2. 创建Thread子类的实例。
  3. 调用start()方法启动线程
public class ThreadTest{
    public static void main(String[] args){
        FirstThread firstThread=new FirstThread();//创建实例
        firstThread.start();//开始线程
        System.out.println("main finish");
    }
}

class FirstThread extends Thread{
    @Override //重写Thread的run方法
    public void run(){
        System.out.println("extends Thread");
    }
}


运行结果:
main finish
extends Thread

实现Runnable接口创建线程类,步骤如下:

  1. 定义Runnable接口的实现类,重写接口的run()方法
  2. 创建Runnable实现类的实例,以此作为Thread的target对象,所以Thread对象才是真正的线程对象。
public class ThreadTest{
    public static void main(String[] args){
        Runnable firstRunnable=new SomeRunnable();
        Thread firstThread=new Thread(firstRunnable);//firstRunnable作为Thread的target对象
        firstThread.start();
        System.out.println("main finish");
    }
}

class SomeRunnable implements Runnable{//定义Runnable接口的实现类,重写run方法
    @Override
    public void run(){
        System.out.println("first Runnable");
    }
}

运行结果:
main finish
first Runnable

通过Callable和Future创建线程,步骤如下:

  1. 创建Callable接口的实现类,并实现call()方法(作为线程的执行体,有返回值)。
  2. 创建Callable实现类的实例,并使用FutureTask类来包装Callable对象,该FutureTask对象封装了该Callable对象的call()方法的返回值。
  3. 将FutureTask对象作为Thread对象的target,创建该Thread对象并启动新线程。
  4. 调用FutureTask对象的get()方法来获得子线程执行结束后的返回值。
public class ThreadTest{
    public static void main(String[] args){
        Callable<String> callable=new FirstCallable();//创建Callable实例
        FutureTask<String> task=new FutureTask<>(callable);//FutureTask包装Callable对象
        Thread thread=new Thread(task);
        try {
            thread.start();
            System.out.println("return: " + task.get());//get方法获得返回值
        }catch (InterruptedException e){
            e.printStackTrace();
        }catch (ExecutionException e){
            e.printStackTrace();
        }catch (Exception e){
            e.printStackTrace();
        }
        System.out.println("main finish");
    }
}

class FirstCallable implements Callable<String>{//Callable接口的实现类,实现call()方法
    @Override
    public String call(){
        System.out.println("first callable");
        return "sub-Thread finish";
    }
}

运行结果:
first callable
return: sub-Thread finish
main finish

线程三种创建方式的比较:

1、使用继承Thread类的方式创建多线程时:

优势:

编写简单,如果需要访问当前线程,则无需使用Thread.currentThread()方法,直接使用this即可获得当前线程。

劣势:

线程类已经继承了Thread类,所以不能再继承其他父类。

2、采用RUnnable和Callable接口的方式创建多线程时:

优势:

线程类只是实现了Runnable接口或Callable接口,还可以继承其他类。在这种方式下,多个线程可以共享同一个target对象,所以非常适合多个相同线程来处理同一份资源的情况,从而可以将CPU、代码和数据分开,形成清晰的模型,较好地体现了面向对象的思想。

劣势:

编程稍微复杂,如果要访问当前线程,则必须使用Thread.currentThread()方法。

3、Runnable和Callable的区别:

  1. Callable规定(重写)的方法是call(),Runnable规定(重写)的方法是run()。
  2.  Callable的任务执行后可返回值,而Runnable的任务是不能返回值的。
  3. call方法可以抛出异常,run方法不可以。
  4. 运行Callable任务可以拿到一个Future对象,表示异步计算的结果。它提供了检查计算是否完成的方法,以等待计算的完成,并检索计算的结果。通过Future对象可以了解任务执行情况,可取消任务的执行,还可获取执行结果。

线程的生命周期:

1、新建状态

用new关键字和Thread类或其子类建立的一个线程对象后,该线程对象就处于新生状态,有自己的内存空间。

2、就绪状态

调用start()方法进入就绪状态,处于就绪状态的线程具备的运行条件,但还没有分配CPU。处于线程就绪队列,尽管采用队列形式,实际上,应该称为可运行池,cpu调度不一定按照先进先出的顺序来调度,一旦获得cpu,线程就进入运行状态并自动调用自己的run()方法。

3、运行状态

运行状态最为复杂,可变为阻塞状态、就绪状态和死亡状态。获得cpu的调度,从就绪状态变为运行状态,执行run()方法中的任务,如果线程失去了cpu资源,从运行状态变为就绪状态,重新等待系统分配资源。对在运行状态的线程调用yield()方法,它就会让出cpu资源,再次变为就绪状态。

由运行状态变为阻塞状态的情况:

  • 调用sleep方法
  • 调用一个阻塞式IO方法,在该方法返回之前,该线程被阻塞。
  • 试图获得一个同步监视器,但该同步监视器正被其他线程所持有
  • 线程等待某个通知notify
  • 程序调用了线程的suspend方法将线程挂起(该方法容易导致死锁)

当run()方法执行完,或者被强制性终止,例如出现异常、或者调用了stop、destory方法等,就会从运行状态变为死亡状态。

4、阻塞状态

阻塞状态的线程不能进入就绪队列,只有当引起阻塞的原因消除时,线程就进入就绪状态,重新等待cpu调度。

5、死亡状态

线程一旦死亡就不能复生,如果一个死去的线程上调用start()方法,会抛出java.lang.IllegalThreadStateException异常。

线程管理

1、sleep  线程睡眠

让当前线程暂停一段时间,并进入阻塞状态。该方法时静态方法,它睡眠的始终是当前正在运行的线程,而不是调用它的线程对象,它只对正在运行状态的线程对象有效。

Java线程调度是Java多线程的核心,只有良好的调度,才能充分发挥系统的性能,提高程序的执行效率。但是不管程序员怎么编写调度,只能最大限度的影响线程执行的次序,而不能做到精准控制。因为使用sleep方法之后,线程是进入阻塞状态的,只有当睡眠的时间结束,才会重新进入到就绪状态,而就绪状态进入到运行状态,是由系统控制的,我们不可能精准的去干涉它,所以如果调用Thread.sleep(1000)使得线程睡眠1秒,可能结果会大于1秒。

public class ThreadTest{
    public static void main(String[] args){
        System.out.println(Thread.currentThread().getName());
        MyThread myThread=new MyThread();
        try {
            myThread.start();
            myThread.sleep(5000);////这里sleep的就是main线程,而非myThread线程 
        }catch (InterruptedException e){
            e.printStackTrace();
        }catch (Exception e){
            e.printStackTrace();
        }
        System.out.println("main finish");
    }
}

class MyThread extends Thread{
    @Override
    public void run(){
        try {
            for (int i = 0; i < 10; i++) {
                System.out.println(i);
                sleep(1000);
            }
        }catch (InterruptedException e){
            e.printStackTrace();
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

运行结果:
main
0
1
2
3
4
main finish
5
6
7
8
9

2、yield 线程让步

和sleep方法有点相似,也是Thread类提供的一个静态方法,可以让当前正在执行的线程暂停,让出cpu资源,与sleep方法不同的是,它不会使线程进入阻塞状态,而是进入就绪状态。可能出现这样的情况:当某个线程调用yield()方法之后,线程调度器又将其调度出来重新进入到运行状态。

实际上,当某个线程调用了yield()方法暂停之后,优先级与当前线程相同,或者优先级比当前线程更高的就绪状态的线程更有可能获得执行的机会,当然,只是有可能,因为我们不可能精确的干涉cpu调度线程。

public class ThreadTest{
    public static void main(String[] args){
        MyThread thread1=new MyThread("低级",Thread.MIN_PRIORITY);
        MyThread thread2=new MyThread("中级",Thread.NORM_PRIORITY);
        MyThread thread3=new MyThread("高级",Thread.MAX_PRIORITY);

        thread1.start();
        thread2.start();
        thread3.start();

        System.out.println("main finish");
    }
}

class MyThread extends Thread{
    public MyThread(String name,int pro){
        super(name);
        this.setPriority(pro);//设置线程的优先级
    }
    @Override
    public void run(){
        for(int i=0;i<30;i++){
            System.out.print(this.getName()+" 线程第 "+i+" 次执行!");
            System.out.print("\t");
            if(i%3==0){
                Thread.yield();//线程让步
            }
        }
    }
}

运行结果:
main finish
高级 线程第 0 次执行!低级 线程第 0 次执行!		高级 线程第 1 次执行!	中级 线程第 0 次执行!	高级 线程第 2 次执行!	低级 线程第 1 次执行!	高级 线程第 3 次执行!	中级 线程第 1 次执行!	高级 线程第 4 次执行!	低级 线程第 2 次执行!	低级 线程第 3 次执行!	低级 线程第 4 次执行!	低级 线程第 5 次执行!	低级 线程第 6 次执行!	低级 线程第 7 次执行!	高级 线程第 5 次执行!	中级 线程第 2 次执行!	高级 线程第 6 次执行!	低级 线程第 8 次执行!高级 线程第 7 次执行!	中级 线程第 3 次执行!	高级 线程第 8 次执行!		高级 线程第 9 次执行!	中级 线程第 4 次执行!	中级 线程第 5 次执行!	中级 线程第 6 次执行!	高级 线程第 10 次执行!	高级 线程第 11 次执行!	高级 线程第 12 次执行!低级 线程第 9 次执行!		中级 线程第 7 次执行!	高级 线程第 13 次执行!	高级 线程第 14 次执行!	高级 线程第 15 次执行!	低级 线程第 10 次执行!	高级 线程第 16 次执行!	中级 线程第 8 次执行!	高级 线程第 17 次执行!	低级 线程第 11 次执行!	高级 线程第 18 次执行!	中级 线程第 9 次执行!	高级 线程第 19 次执行!	低级 线程第 12 次执行!	高级 线程第 20 次执行!	中级 线程第 10 次执行!	高级 线程第 21 次执行!	低级 线程第 13 次执行!	高级 线程第 22 次执行!	中级 线程第 11 次执行!	高级 线程第 23 次执行!	高级 线程第 24 次执行!	中级 线程第 12 次执行!	高级 线程第 25 次执行!	高级 线程第 26 次执行!	高级 线程第 27 次执行!	中级 线程第 13 次执行!	中级 线程第 14 次执行!	中级 线程第 15 次执行!	高级 线程第 28 次执行!	高级 线程第 29 次执行!	低级 线程第 14 次执行!	中级 线程第 16 次执行!	中级 线程第 17 次执行!	低级 线程第 15 次执行!	中级 线程第 18 次执行!	低级 线程第 16 次执行!	中级 线程第 19 次执行!	低级 线程第 17 次执行!	中级 线程第 20 次执行!	中级 线程第 21 次执行!低级 线程第 18 次执行!		中级 线程第 22 次执行!	中级 线程第 23 次执行!	低级 线程第 19 次执行!	中级 线程第 24 次执行!	低级 线程第 20 次执行!	中级 线程第 25 次执行!	中级 线程第 26 次执行!	中级 线程第 27 次执行!	低级 线程第 21 次执行!	中级 线程第 28 次执行!	低级 线程第 22 次执行!	中级 线程第 29 次执行!	低级 线程第 23 次执行!	低级 线程第 24 次执行!	低级 线程第 25 次执行!	低级 线程第 26 次执行!	低级 线程第 27 次执行!	低级 线程第 28 次执行!	低级 线程第 29 次执行!	
Process finished with exit code 0

3、join 线程合并

就是将几个并行线程合并为一个单线程执行,应用场景就是当一个线程必须等待另一个线程执行完毕才能执行时。join方法用来暂停当前线程直到join操作上的线程结束。

join三个重载方法:

//此方法会把当前线程变为wait,直到执行join操作的线程结束,如果该线程在执行中被中断,则会抛出InterruptedException。
void join()
//此方法会把当前线程变为wait,直到执行join操作的线程结束或者在执行join后等待millis的时间。因为线程调度依赖于操作系统的实现,因为这并不能保证当前线程一定会在millis时间变为RUnnable。
void join(long millis)
//此方法会把当前线程变为wait,直到执行join操作的线程结束或者在join后等待millis+nanos的时间。
void join(long millis,int nanos)
public class ThreadTest{
    public static void main(String[] args){
        MyThread thread1=new MyThread("低级",Thread.MIN_PRIORITY);
        MyThread thread2=new MyThread("中级",Thread.NORM_PRIORITY);
        MyThread thread3=new MyThread("高级",Thread.MAX_PRIORITY);

        thread1.start();
        thread2.start();
        thread3.start();
        try{
            thread1.join();
            thread2.join();
            thread3.join();  //等到该线程都执行完毕,主线程才会结束
        }catch (InterruptedException e){
            e.printStackTrace();
        }catch (Exception e){
            e.printStackTrace();
        }

        System.out.println("main finish");
    }
}

class MyThread extends Thread{
    public MyThread(String name,int pro){
        super(name);
        this.setPriority(pro);
    }
    @Override
    public void run(){
        System.out.println(this.getName()+" 线程执行!");
    }
}

运行结果:
低级 线程执行!
高级 线程执行!
中级 线程执行!
main finish

4、线程优先级

每个线程执行时都有一个优先级属性,优先级高的线程可以获得较多的执行机会,而优先级低的线程获得的执行机会较小。但线程的优先级无法保证线程的执行次序,只不过在概率上优先级高的线程获得的执行机会较大。每个线程默认的优先级都与创建它的父线程具有相同的优先级,在默认情况下,main线程具有普通优先级。

setPriority(int newPriority)设置指定线程的优先级。参数是一个整数,范围是1-10之间,同时提供 三个静态常量:

MAX_PRIORITY =10

MIN_PRIORITY =1

NORM_PRIORITY=5

我们应该使用三个静态常量来设定优先级,才能保证程序最好的可移植性。

getPriority()返回指定线程的优先级。

5、后台线程(守护线程)

在程序运行的时候在后台提供一种通用服务的线程,并且这种线程并不属于程序中不可或缺的部分。举例来说,JVM的垃圾回收、内存管理等线程都是守护线程。还有就是在做数据库应用时候,使用的数据库连接池,连接池本身也包含着很多后台线程,监控连接个数、超时时间、状态等等。调用线程对象的方法setDaemon(true),则可以将其设置为守护线程。通常用于执行一些后台作业,守护线程的好处是不需要关心它的结束问题。

setDaemon方法的详细说明:

public final void setDaemon(boolean on)
/*        
将该线程标记为守护线程或用户线程。当正在运行的线程都是守护线程时,Java 虚拟机退出。    
该方法必须在启动线程前调用。 首先调用该线程的 checkAccess 方法,且不带任何参数。这可能抛出 SecurityException(在当前线程中)。   
  参数:
     on - 如果为 true,则将该线程标记为守护线程。    
  抛出:    
    IllegalThreadStateException - 如果该线程处于活动状态。    
    SecurityException - 如果当前线程无法修改该线程。
*/

注意:JRE判断程序是否执行结束的标准是所有的前台执线程行完毕了,而不管后台线程的状态,因此,在使用后台县城时候一定要注意这个问题。

线程同步

java允许多线程并发控制,当多个线程同时操作一个可共享的资源变量时,将会导致数据不准确,相互之间产生冲突,因此加入同步锁以避免在该线程没有完成操作之前,被其他线程的调用,从而保证了该变量的唯一性和准确性。

synchronized关键字

synchronized关键字修饰的方法,java的内置锁会保护整个方法,在调用该方法前,需要获得内置锁,否则进入阻塞状态。

synchronized关键字也可以修饰静态方法,此时,将会锁住整个类。

synchronized关键字也可以修饰语句块,被修饰的语句块会自动被加上内置锁,从而实现同步。

同步是一种高开销的操作,因此应该尽量减少同步的内容。通常没有必要同步整个方法,使用synchronized代码块同步关键代码即可。

class Bank{
    private int count=0;//账户余额

    public void addMoney(int money){          //存钱
        synchronized (this){                 //加上内置锁
            if(count+money<Integer.MAX_VALUE) {
                count += money;
            }else {
                System.out.println("count is full");
            }
            System.out.println(System.currentTimeMillis()+"存进: "+money);
        }
    }

    public void subMoney(int money){  //取钱
        synchronized (this){          //加上内置锁
            if(count-money>0){
                count-=money;
            }else {
                System.out.println("count isn't enough");
            }
            System.out.println(System.currentTimeMillis()+"取出: "+money);
        }
    }

    public void checkMoney(){       //查询余额
        System.out.println("账户余额: "+count);
    }
}

class MyThread extends Thread{
    private Bank bank;
    private String flag;

    public MyThread(String name,Bank bank,String flag){
        super(name);
        this.bank=bank;
        this.flag=flag;
    }

    @Override
    public void run(){
        try {
            if ("add".equals(flag)) {
                this.bank.addMoney(10);
                System.out.println(this.getName()+" sleep: 3s");
                this.sleep(3000);
            } else if ("sub".equals(flag)) {
                this.bank.subMoney(8);
                System.out.println(this.getName()+"sleep: 3s");
                this.sleep(3000);
            } else {
                System.out.println("no action");
            }
        }catch (InterruptedException e){
            e.printStackTrace();
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

public class ThreadTest{
    public static void main(String[] args){
        Bank bank=new Bank();
        MyThread[] myThread=new MyThread[12];
        for(int i=0;i<12;i=i+2){
            myThread[i]=new MyThread("线程"+i,bank,"add");
            myThread[i+1]=new MyThread("线程"+(i+1),bank,"sub");
        }

        for(int i=0;i<12;i++){
            myThread[i].start();
        }
        try{
            for(int i=0;i<12;i++){
                myThread[i].join();
            }
        }catch (InterruptedException e){
            e.printStackTrace();
        }catch (Exception e){
            e.printStackTrace();
        }

        bank.checkMoney();
        System.out.println("main finish");
    }
}

/*
运行结果:
1535276034160存进: 10
线程0 sleep: 3s
1535276034160取出: 8
线程5sleep: 3s
count isn't enough
1535276034160取出: 8
count isn't enough
线程7sleep: 3s
1535276034160取出: 8
线程11sleep: 3s
1535276034160存进: 10
线程6 sleep: 3s
1535276034160存进: 10
线程4 sleep: 3s
1535276034160取出: 8
线程3sleep: 3s
1535276034160取出: 8
线程1sleep: 3s
1535276034160存进: 10
线程2 sleep: 3s
1535276034160取出: 8
线程9sleep: 3s
1535276034160存进: 10
线程10 sleep: 3s
1535276034160存进: 10
线程8 sleep: 3s
账户余额: 28
main finish
*/
​

volatile关键字

volatile关键字为域变量的访问提供了一种免锁机制,相当于告诉虚拟机被volatile修饰的域可能会被其他线程更新,因此每次使用该域就要进行重新计算,而不是使用寄存器的值。同时volatile不会提供任何原子操作,也不能用来修饰final类型的变量。

多线程中的非同步问题主要出现在对域的读写上,如果让域自身避免这个问题,则就不需要修改操作该域的方法。用final域,有锁保护的域和volatile域可以避免非同步的问题。

 

Lock 重入锁

Lock 重入锁实现线程同步,在JavaSE5.0中新增了一个java.util.concurrent包来支持同步。ReentrantLock类是可重入、互斥、实现了Lock接口的锁,它与使用synchronized方法和快具有相同的基本行为和语义,并且扩展了其能力。

ReentrantLock()  //创建一个ReentrantLock实例         
lock()  //获得锁        
unlock()  //释放锁
public class ThreadTest{
    public static void main(String[] args){
        Bank bank=new Bank();
        MyThread[] myThread=new MyThread[12];
        for(int i=0;i<12;i=i+2){
            myThread[i]=new MyThread("线程"+i,bank,"add");
            myThread[i+1]=new MyThread("线程"+(i+1),bank,"sub");
        }

        for(int i=0;i<12;i++){
            myThread[i].start();
        }
        try{
            for(int i=0;i<12;i++){
                myThread[i].join();
            }
        }catch (InterruptedException e){
            e.printStackTrace();
        }catch (Exception e){
            e.printStackTrace();
        }

        bank.checkMoney();
        System.out.println("main finish");
    }
}

class MyThread extends Thread{
    private Bank bank;
    private String flag;
    public MyThread(String name,Bank bank,String flag){
        super(name);
        this.bank=bank;
        this.flag=flag;
    }
    @Override
    public void run(){
        try {
            if ("add".equals(flag)) {
                this.bank.addMoney(10);
                System.out.println(this.getName()+" sleep: 3s");
                this.sleep(3000);
            } else if ("sub".equals(flag)) {
                this.bank.subMoney(8);
                System.out.println(this.getName()+"sleep: 3s");
                this.sleep(3000);
            } else {
                System.out.println("no action");
            }
        }catch (InterruptedException e){
            e.printStackTrace();
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

class Bank{
    private int count=0;                   //账户余额
    private Lock lock=new ReentrantLock();

    public void addMoney(int money){          //存钱
        lock.lock();
        try{
            if(count+money<Integer.MAX_VALUE) {
                count += money;
            }else {
                System.out.println("count is full");
            }
            System.out.println(System.currentTimeMillis()+"存进: "+money);
        }catch (Exception e){
           e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }

    public void subMoney(int money){            //取钱
        lock.lock();
        try{
            if(count-money>0){
                count-=money;
            }else {
                System.out.println("count isn't enough");
            }
            System.out.println(System.currentTimeMillis()+"取出: "+money);
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }

    public void checkMoney(){                //查询余额
        System.out.println("账户余额: "+count);
    }
}
/*
运行结果:
1535277656677存进: 10
线程0 sleep: 3s
1535277656677取出: 8
线程1sleep: 3s
count isn't enough
1535277656677取出: 8
线程5sleep: 3s
count isn't enough
1535277656677取出: 8
线程3sleep: 3s
1535277656677存进: 10
线程4 sleep: 3s
1535277656677存进: 10
线程2 sleep: 3s
1535277656677存进: 10
线程6 sleep: 3s
1535277656677取出: 8
线程7sleep: 3s
1535277656677存进: 10
线程8 sleep: 3s
1535277656677取出: 8
线程9sleep: 3s
1535277656677存进: 10
线程10 sleep: 3s
1535277656678取出: 8
线程11sleep: 3s
账户余额: 28
main finish
*/

线程通信

Object类的wait(),notify(),notifyAll()实现通信

线程执行wait()后,就放弃了运行资格,处于冻结状态。

notify()执行时唤醒的也是线程池中的线程,线程池中有多个线程时唤醒第一个被冻结的线程。

notifyall(), 唤醒线程池中所有线程。

注意:wait(), notify(),notifyall()都用在同步里面,因为这3个函数是对持有锁的线程进行操作,而只有同步才有锁,所以要使用在同步中。同时在使用时必须标识它们所操作的线程持有的锁,因为等待和唤醒必须是同一锁下的线程;而锁可以是任意对象,所以这3个方法都是Object类中的方法。

public class Main {
    public static void main(String[] args){
        Resource res=new Resource();
        Producer1 pro=new Producer1(res);
        Consumer1 con=new Consumer1(res);
        Thread t1=new Thread(pro);
        Thread t2=new Thread(con);
        t1.start();
        t2.start();
        try{
            t1.join();
            t2.join();
        }catch (InterruptedException e){
            e.printStackTrace();
        }
        System.out.println("main finish");
    }
}

class Resource{
    private String name;
    private int count=1;
    private boolean flag=false;

    public synchronized void set(String name){
        if(flag){
            try{
                wait();
            }catch (InterruptedException e){
                e.printStackTrace();
            }
        }
        this.name=name+"---"+count++;
        System.out.println(Thread.currentThread().getName()+"...生产者..."+this.name);
        flag=true;
        this.notify();
    }

    public synchronized void out(){
        if(!flag){
            try{
                wait();
            }catch (InterruptedException e){
                e.printStackTrace();
            }
        }
        System.out.println(Thread.currentThread().getName()+"...消费者..."+this.name);
        flag=false;
        this.notify();
    }
}

class Producer1 implements Runnable{
    private Resource res;
    public Producer1(Resource res){
        this.res=res;
    }

    public void run(){
        for(int i=0;i<100;i++){
            res.set("商品");
        }
    }
}

class Consumer1 implements Runnable{
    private Resource res;
    public Consumer1(Resource res){
        this.res=res;
    }

    public void run(){
        for(int i=0;i<100;i++){
            res.out();
        }
    }
}
/*
运行结果:
Thread-0...生产者...商品---1
Thread-1...消费者...商品---1
Thread-0...生产者...商品---2
Thread-1...消费者...商品---2
Thread-0...生产者...商品---3
Thread-1...消费者...商品---3
Thread-0...生产者...商品---4
Thread-1...消费者...商品---4
Thread-0...生产者...商品---5
Thread-1...消费者...商品---5
Thread-0...生产者...商品---6
Thread-1...消费者...商品---6
Thread-0...生产者...商品---7
Thread-1...消费者...商品---7
Thread-0...生产者...商品---8
Thread-1...消费者...商品---8
Thread-0...生产者...商品---9
Thread-1...消费者...商品---9
Thread-0...生产者...商品---10
Thread-1...消费者...商品---10
Thread-0...生产者...商品---11
Thread-1...消费者...商品---11
Thread-0...生产者...商品---12
Thread-1...消费者...商品---12
Thread-0...生产者...商品---13
Thread-1...消费者...商品---13
Thread-0...生产者...商品---14
Thread-1...消费者...商品---14
Thread-0...生产者...商品---15
Thread-1...消费者...商品---15
Thread-0...生产者...商品---16
Thread-1...消费者...商品---16
Thread-0...生产者...商品---17
Thread-1...消费者...商品---17
Thread-0...生产者...商品---18
Thread-1...消费者...商品---18
Thread-0...生产者...商品---19
Thread-1...消费者...商品---19
Thread-0...生产者...商品---20
Thread-1...消费者...商品---20
Thread-0...生产者...商品---21
Thread-1...消费者...商品---21
Thread-0...生产者...商品---22
Thread-1...消费者...商品---22
Thread-0...生产者...商品---23
Thread-1...消费者...商品---23
Thread-0...生产者...商品---24
Thread-1...消费者...商品---24
Thread-0...生产者...商品---25
Thread-1...消费者...商品---25
Thread-0...生产者...商品---26
Thread-1...消费者...商品---26
Thread-0...生产者...商品---27
Thread-1...消费者...商品---27
Thread-0...生产者...商品---28
Thread-1...消费者...商品---28
Thread-0...生产者...商品---29
Thread-1...消费者...商品---29
Thread-0...生产者...商品---30
Thread-1...消费者...商品---30
Thread-0...生产者...商品---31
Thread-1...消费者...商品---31
Thread-0...生产者...商品---32
Thread-1...消费者...商品---32
Thread-0...生产者...商品---33
Thread-1...消费者...商品---33
Thread-0...生产者...商品---34
Thread-1...消费者...商品---34
Thread-0...生产者...商品---35
Thread-1...消费者...商品---35
Thread-0...生产者...商品---36
Thread-1...消费者...商品---36
Thread-0...生产者...商品---37
Thread-1...消费者...商品---37
Thread-0...生产者...商品---38
Thread-1...消费者...商品---38
Thread-0...生产者...商品---39
Thread-1...消费者...商品---39
Thread-0...生产者...商品---40
Thread-1...消费者...商品---40
Thread-0...生产者...商品---41
Thread-1...消费者...商品---41
Thread-0...生产者...商品---42
Thread-1...消费者...商品---42
Thread-0...生产者...商品---43
Thread-1...消费者...商品---43
Thread-0...生产者...商品---44
Thread-1...消费者...商品---44
Thread-0...生产者...商品---45
Thread-1...消费者...商品---45
Thread-0...生产者...商品---46
Thread-1...消费者...商品---46
Thread-0...生产者...商品---47
Thread-1...消费者...商品---47
Thread-0...生产者...商品---48
Thread-1...消费者...商品---48
Thread-0...生产者...商品---49
Thread-1...消费者...商品---49
Thread-0...生产者...商品---50
Thread-1...消费者...商品---50
Thread-0...生产者...商品---51
Thread-1...消费者...商品---51
Thread-0...生产者...商品---52
Thread-1...消费者...商品---52
Thread-0...生产者...商品---53
Thread-1...消费者...商品---53
Thread-0...生产者...商品---54
Thread-1...消费者...商品---54
Thread-0...生产者...商品---55
Thread-1...消费者...商品---55
Thread-0...生产者...商品---56
Thread-1...消费者...商品---56
Thread-0...生产者...商品---57
Thread-1...消费者...商品---57
Thread-0...生产者...商品---58
Thread-1...消费者...商品---58
Thread-0...生产者...商品---59
Thread-1...消费者...商品---59
Thread-0...生产者...商品---60
Thread-1...消费者...商品---60
Thread-0...生产者...商品---61
Thread-1...消费者...商品---61
Thread-0...生产者...商品---62
Thread-1...消费者...商品---62
Thread-0...生产者...商品---63
Thread-1...消费者...商品---63
Thread-0...生产者...商品---64
Thread-1...消费者...商品---64
Thread-0...生产者...商品---65
Thread-1...消费者...商品---65
Thread-0...生产者...商品---66
Thread-1...消费者...商品---66
Thread-0...生产者...商品---67
Thread-1...消费者...商品---67
Thread-0...生产者...商品---68
Thread-1...消费者...商品---68
Thread-0...生产者...商品---69
Thread-1...消费者...商品---69
Thread-0...生产者...商品---70
Thread-1...消费者...商品---70
Thread-0...生产者...商品---71
Thread-1...消费者...商品---71
Thread-0...生产者...商品---72
Thread-1...消费者...商品---72
Thread-0...生产者...商品---73
Thread-1...消费者...商品---73
Thread-0...生产者...商品---74
Thread-1...消费者...商品---74
Thread-0...生产者...商品---75
Thread-1...消费者...商品---75
Thread-0...生产者...商品---76
Thread-1...消费者...商品---76
Thread-0...生产者...商品---77
Thread-1...消费者...商品---77
Thread-0...生产者...商品---78
Thread-1...消费者...商品---78
Thread-0...生产者...商品---79
Thread-1...消费者...商品---79
Thread-0...生产者...商品---80
Thread-1...消费者...商品---80
Thread-0...生产者...商品---81
Thread-1...消费者...商品---81
Thread-0...生产者...商品---82
Thread-1...消费者...商品---82
Thread-0...生产者...商品---83
Thread-1...消费者...商品---83
Thread-0...生产者...商品---84
Thread-1...消费者...商品---84
Thread-0...生产者...商品---85
Thread-1...消费者...商品---85
Thread-0...生产者...商品---86
Thread-1...消费者...商品---86
Thread-0...生产者...商品---87
Thread-1...消费者...商品---87
Thread-0...生产者...商品---88
Thread-1...消费者...商品---88
Thread-0...生产者...商品---89
Thread-1...消费者...商品---89
Thread-0...生产者...商品---90
Thread-1...消费者...商品---90
Thread-0...生产者...商品---91
Thread-1...消费者...商品---91
Thread-0...生产者...商品---92
Thread-1...消费者...商品---92
Thread-0...生产者...商品---93
Thread-1...消费者...商品---93
Thread-0...生产者...商品---94
Thread-1...消费者...商品---94
Thread-0...生产者...商品---95
Thread-1...消费者...商品---95
Thread-0...生产者...商品---96
Thread-1...消费者...商品---96
Thread-0...生产者...商品---97
Thread-1...消费者...商品---97
Thread-0...生产者...商品---98
Thread-1...消费者...商品---98
Thread-0...生产者...商品---99
Thread-1...消费者...商品---99
Thread-0...生产者...商品---100
Thread-1...消费者...商品---100
main finish
*/

使用Condition控制线程通信

一个Lock对象上可以绑定多个Condition对象,这样实现了本方线程只唤醒对方线程,而jdk1.5之前,一个同步只能有一个锁,不同的同步只能用锁来区分,且锁嵌套时容易死锁。

class Resource1{
    private String name;
    private int count=1;
    private boolean flag=false;
    private Lock lock=new ReentrantLock();
    private Condition condition_pro=lock.newCondition();//生产者方面的Condition对象
    private Condition condition_con=lock.newCondition();//消费者方面的Condition对象

    public void set(String name){
        lock.lock();
        try{
            while(flag){
                condition_pro.await();       //生产者线程在conndition_pro对象上等待 
            }
            this.name = name + "---" + count++;
            System.out.println("已经生产了:"+count);
            System.out.println(Thread.currentThread().getName() + "...生产者..." + this.name);
            flag = true;
            condition_con.signalAll();
        }catch (InterruptedException e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }

    public void out(){
        lock.lock();
        try{
            while(!flag){
                condition_con.await();  //消费者线程在conndition_con对象上等待 
            }
            System.out.println(Thread.currentThread().getName() + "...消费者..." + this.name);
            if(count>0) count--;
            else{
                System.out.println("没有商品消费的了");
            }
            System.out.println("还有 "+count+" 没有消费");
            flag = false;
            condition_pro.signalAll();  /*唤醒所有在condition_pro对象下等待的线程,也就是唤醒所有生产者线程*/ 
        }catch (InterruptedException e){
            e.printStackTrace();
        }finally {
            lock.unlock();  //unlock()要放在finally块中。
        }
    }

}

class Producer implements Runnable{
    private Resource1 res;
    Producer(Resource1 res){
        this.res=res;
    }

    public void run(){
        for(int i=0;i<30;i++){
            res.set("商品");
        }
    }
}

class Consumer implements Runnable{
    private Resource1 res;
    Consumer(Resource1 res){
        this.res=res;
    }

    public void run(){
        for(int i=0;i<30;i++){
            res.out();
        }
    }
}

public class ConditionTest2 {
    public static void main(String[] args){
        Resource1 r=new Resource1();
        Producer pro=new Producer(r);
        Consumer con=new Consumer(r);
        Thread t1=new Thread(pro);
        Thread t2=new Thread(con);
        Thread t3=new Thread(pro);
        Thread t4=new Thread(con);
        Thread t5=new Thread(pro);
        Thread t6=new Thread(con);
        Thread t7=new Thread(pro);
        Thread t8=new Thread(con);
        t1.start();
        t2.start();
        t3.start();
        t4.start();
        t5.start();
        t6.start();
        t7.start();
        t8.start();
    }
}
/*
运行结果:
已经生产了:2
Thread-0...生产者...商品---1
Thread-1...消费者...商品---1
还有 1 没有消费
已经生产了:2
Thread-4...生产者...商品---1
Thread-3...消费者...商品---1
还有 1 没有消费
已经生产了:2
Thread-6...生产者...商品---1
Thread-7...消费者...商品---1
还有 1 没有消费
已经生产了:2
Thread-0...生产者...商品---1
Thread-1...消费者...商品---1
还有 1 没有消费
已经生产了:2
Thread-4...生产者...商品---1
Thread-3...消费者...商品---1
还有 1 没有消费
已经生产了:2
Thread-6...生产者...商品---1
Thread-7...消费者...商品---1
还有 1 没有消费
已经生产了:2
Thread-0...生产者...商品---1
Thread-1...消费者...商品---1
还有 1 没有消费
已经生产了:2
Thread-4...生产者...商品---1
Thread-3...消费者...商品---1
还有 1 没有消费
已经生产了:2
Thread-6...生产者...商品---1
Thread-7...消费者...商品---1
还有 1 没有消费
已经生产了:2
Thread-0...生产者...商品---1
Thread-1...消费者...商品---1
还有 1 没有消费
已经生产了:2
Thread-4...生产者...商品---1
Thread-3...消费者...商品---1
还有 1 没有消费
已经生产了:2
Thread-6...生产者...商品---1
Thread-7...消费者...商品---1
还有 1 没有消费
已经生产了:2
Thread-0...生产者...商品---1
Thread-1...消费者...商品---1
还有 1 没有消费
已经生产了:2
Thread-4...生产者...商品---1
Thread-3...消费者...商品---1
还有 1 没有消费
已经生产了:2
Thread-6...生产者...商品---1
Thread-7...消费者...商品---1
还有 1 没有消费
已经生产了:2
Thread-0...生产者...商品---1
Thread-1...消费者...商品---1
还有 1 没有消费
已经生产了:2
Thread-4...生产者...商品---1
Thread-3...消费者...商品---1
还有 1 没有消费
已经生产了:2
Thread-6...生产者...商品---1
Thread-7...消费者...商品---1
还有 1 没有消费
已经生产了:2
Thread-0...生产者...商品---1
Thread-1...消费者...商品---1
还有 1 没有消费
已经生产了:2
Thread-4...生产者...商品---1
Thread-3...消费者...商品---1
还有 1 没有消费
已经生产了:2
Thread-6...生产者...商品---1
Thread-7...消费者...商品---1
还有 1 没有消费
已经生产了:2
Thread-0...生产者...商品---1
Thread-1...消费者...商品---1
还有 1 没有消费
已经生产了:2
Thread-4...生产者...商品---1
Thread-3...消费者...商品---1
还有 1 没有消费
已经生产了:2
Thread-6...生产者...商品---1
Thread-7...消费者...商品---1
还有 1 没有消费
已经生产了:2
Thread-0...生产者...商品---1
Thread-1...消费者...商品---1
还有 1 没有消费
已经生产了:2
Thread-4...生产者...商品---1
Thread-3...消费者...商品---1
还有 1 没有消费
已经生产了:2
Thread-6...生产者...商品---1
Thread-7...消费者...商品---1
还有 1 没有消费
已经生产了:2
Thread-0...生产者...商品---1
Thread-7...消费者...商品---1
还有 1 没有消费
已经生产了:2
Thread-2...生产者...商品---1
Thread-1...消费者...商品---1
还有 1 没有消费
已经生产了:2
Thread-4...生产者...商品---1
Thread-3...消费者...商品---1
还有 1 没有消费
已经生产了:2
Thread-6...生产者...商品---1
Thread-7...消费者...商品---1
还有 1 没有消费
已经生产了:2
Thread-2...生产者...商品---1
Thread-1...消费者...商品---1
还有 1 没有消费
已经生产了:2
Thread-4...生产者...商品---1
Thread-3...消费者...商品---1
还有 1 没有消费
已经生产了:2
Thread-6...生产者...商品---1
Thread-7...消费者...商品---1
还有 1 没有消费
已经生产了:2
Thread-2...生产者...商品---1
Thread-1...消费者...商品---1
还有 1 没有消费
已经生产了:2
Thread-4...生产者...商品---1
Thread-3...消费者...商品---1
还有 1 没有消费
已经生产了:2
Thread-6...生产者...商品---1
Thread-7...消费者...商品---1
还有 1 没有消费
已经生产了:2
Thread-2...生产者...商品---1
Thread-1...消费者...商品---1
还有 1 没有消费
已经生产了:2
Thread-4...生产者...商品---1
Thread-3...消费者...商品---1
还有 1 没有消费
已经生产了:2
Thread-6...生产者...商品---1
Thread-7...消费者...商品---1
还有 1 没有消费
已经生产了:2
Thread-2...生产者...商品---1
Thread-1...消费者...商品---1
还有 1 没有消费
已经生产了:2
Thread-4...生产者...商品---1
Thread-3...消费者...商品---1
还有 1 没有消费
已经生产了:2
Thread-6...生产者...商品---1
Thread-7...消费者...商品---1
还有 1 没有消费
已经生产了:2
Thread-2...生产者...商品---1
Thread-1...消费者...商品---1
还有 1 没有消费
已经生产了:2
Thread-4...生产者...商品---1
Thread-3...消费者...商品---1
还有 1 没有消费
已经生产了:2
Thread-6...生产者...商品---1
Thread-7...消费者...商品---1
还有 1 没有消费
已经生产了:2
Thread-2...生产者...商品---1
Thread-1...消费者...商品---1
还有 1 没有消费
已经生产了:2
Thread-4...生产者...商品---1
Thread-3...消费者...商品---1
还有 1 没有消费
已经生产了:2
Thread-6...生产者...商品---1
Thread-7...消费者...商品---1
还有 1 没有消费
已经生产了:2
Thread-2...生产者...商品---1
Thread-1...消费者...商品---1
还有 1 没有消费
已经生产了:2
Thread-4...生产者...商品---1
Thread-3...消费者...商品---1
还有 1 没有消费
已经生产了:2
Thread-6...生产者...商品---1
Thread-7...消费者...商品---1
还有 1 没有消费
已经生产了:2
Thread-2...生产者...商品---1
Thread-1...消费者...商品---1
还有 1 没有消费
已经生产了:2
Thread-4...生产者...商品---1
Thread-3...消费者...商品---1
还有 1 没有消费
已经生产了:2
Thread-6...生产者...商品---1
Thread-7...消费者...商品---1
还有 1 没有消费
已经生产了:2
Thread-2...生产者...商品---1
Thread-1...消费者...商品---1
还有 1 没有消费
已经生产了:2
Thread-4...生产者...商品---1
Thread-3...消费者...商品---1
还有 1 没有消费
已经生产了:2
Thread-6...生产者...商品---1
Thread-7...消费者...商品---1
还有 1 没有消费
已经生产了:2
Thread-2...生产者...商品---1
Thread-1...消费者...商品---1
还有 1 没有消费
已经生产了:2
Thread-4...生产者...商品---1
Thread-3...消费者...商品---1
还有 1 没有消费
已经生产了:2
Thread-6...生产者...商品---1
Thread-7...消费者...商品---1
还有 1 没有消费
已经生产了:2
Thread-2...生产者...商品---1
Thread-1...消费者...商品---1
还有 1 没有消费
已经生产了:2
Thread-4...生产者...商品---1
Thread-3...消费者...商品---1
还有 1 没有消费
已经生产了:2
Thread-6...生产者...商品---1
Thread-7...消费者...商品---1
还有 1 没有消费
已经生产了:2
Thread-2...生产者...商品---1
Thread-1...消费者...商品---1
还有 1 没有消费
已经生产了:2
Thread-4...生产者...商品---1
Thread-3...消费者...商品---1
还有 1 没有消费
已经生产了:2
Thread-6...生产者...商品---1
Thread-7...消费者...商品---1
还有 1 没有消费
已经生产了:2
Thread-2...生产者...商品---1
Thread-1...消费者...商品---1
还有 1 没有消费
已经生产了:2
Thread-4...生产者...商品---1
Thread-3...消费者...商品---1
还有 1 没有消费
已经生产了:2
Thread-6...生产者...商品---1
Thread-7...消费者...商品---1
还有 1 没有消费
已经生产了:2
Thread-2...生产者...商品---1
Thread-1...消费者...商品---1
还有 1 没有消费
已经生产了:2
Thread-4...生产者...商品---1
Thread-3...消费者...商品---1
还有 1 没有消费
已经生产了:2
Thread-6...生产者...商品---1
Thread-7...消费者...商品---1
还有 1 没有消费
已经生产了:2
Thread-2...生产者...商品---1
Thread-1...消费者...商品---1
还有 1 没有消费
已经生产了:2
Thread-4...生产者...商品---1
Thread-3...消费者...商品---1
还有 1 没有消费
已经生产了:2
Thread-6...生产者...商品---1
Thread-7...消费者...商品---1
还有 1 没有消费
已经生产了:2
Thread-2...生产者...商品---1
Thread-1...消费者...商品---1
还有 1 没有消费
已经生产了:2
Thread-4...生产者...商品---1
Thread-3...消费者...商品---1
还有 1 没有消费
已经生产了:2
Thread-6...生产者...商品---1
Thread-7...消费者...商品---1
还有 1 没有消费
已经生产了:2
Thread-2...生产者...商品---1
Thread-1...消费者...商品---1
还有 1 没有消费
已经生产了:2
Thread-4...生产者...商品---1
Thread-3...消费者...商品---1
还有 1 没有消费
已经生产了:2
Thread-6...生产者...商品---1
Thread-7...消费者...商品---1
还有 1 没有消费
已经生产了:2
Thread-2...生产者...商品---1
Thread-1...消费者...商品---1
还有 1 没有消费
已经生产了:2
Thread-4...生产者...商品---1
Thread-3...消费者...商品---1
还有 1 没有消费
已经生产了:2
Thread-6...生产者...商品---1
Thread-7...消费者...商品---1
还有 1 没有消费
已经生产了:2
Thread-2...生产者...商品---1
Thread-1...消费者...商品---1
还有 1 没有消费
已经生产了:2
Thread-4...生产者...商品---1
Thread-3...消费者...商品---1
还有 1 没有消费
已经生产了:2
Thread-6...生产者...商品---1
Thread-7...消费者...商品---1
还有 1 没有消费
已经生产了:2
Thread-2...生产者...商品---1
Thread-1...消费者...商品---1
还有 1 没有消费
已经生产了:2
Thread-4...生产者...商品---1
Thread-3...消费者...商品---1
还有 1 没有消费
已经生产了:2
Thread-6...生产者...商品---1
Thread-5...消费者...商品---1
还有 1 没有消费
已经生产了:2
Thread-0...生产者...商品---1
Thread-5...消费者...商品---1
还有 1 没有消费
已经生产了:2
Thread-0...生产者...商品---1
Thread-5...消费者...商品---1
还有 1 没有消费
已经生产了:2
Thread-0...生产者...商品---1
Thread-5...消费者...商品---1
还有 1 没有消费
已经生产了:2
Thread-0...生产者...商品---1
Thread-5...消费者...商品---1
还有 1 没有消费
已经生产了:2
Thread-0...生产者...商品---1
Thread-5...消费者...商品---1
还有 1 没有消费
已经生产了:2
Thread-0...生产者...商品---1
Thread-5...消费者...商品---1
还有 1 没有消费
已经生产了:2
Thread-0...生产者...商品---1
Thread-5...消费者...商品---1
还有 1 没有消费
已经生产了:2
Thread-0...生产者...商品---1
Thread-5...消费者...商品---1
还有 1 没有消费
已经生产了:2
Thread-0...生产者...商品---1
Thread-5...消费者...商品---1
还有 1 没有消费
已经生产了:2
Thread-0...生产者...商品---1
Thread-5...消费者...商品---1
还有 1 没有消费
已经生产了:2
Thread-0...生产者...商品---1
Thread-5...消费者...商品---1
还有 1 没有消费
已经生产了:2
Thread-0...生产者...商品---1
Thread-5...消费者...商品---1
还有 1 没有消费
已经生产了:2
Thread-0...生产者...商品---1
Thread-5...消费者...商品---1
还有 1 没有消费
已经生产了:2
Thread-0...生产者...商品---1
Thread-5...消费者...商品---1
还有 1 没有消费
已经生产了:2
Thread-0...生产者...商品---1
Thread-5...消费者...商品---1
还有 1 没有消费
已经生产了:2
Thread-0...生产者...商品---1
Thread-5...消费者...商品---1
还有 1 没有消费
已经生产了:2
Thread-0...生产者...商品---1
Thread-5...消费者...商品---1
还有 1 没有消费
已经生产了:2
Thread-0...生产者...商品---1
Thread-5...消费者...商品---1
还有 1 没有消费
已经生产了:2
Thread-0...生产者...商品---1
Thread-5...消费者...商品---1
还有 1 没有消费
已经生产了:2
Thread-0...生产者...商品---1
Thread-5...消费者...商品---1
还有 1 没有消费
已经生产了:2
Thread-2...生产者...商品---1
Thread-5...消费者...商品---1
还有 1 没有消费
已经生产了:2
Thread-2...生产者...商品---1
Thread-5...消费者...商品---1
还有 1 没有消费
已经生产了:2
Thread-2...生产者...商品---1
Thread-5...消费者...商品---1
还有 1 没有消费
已经生产了:2
Thread-2...生产者...商品---1
Thread-5...消费者...商品---1
还有 1 没有消费
已经生产了:2
Thread-2...生产者...商品---1
Thread-5...消费者...商品---1
还有 1 没有消费
已经生产了:2
Thread-2...生产者...商品---1
Thread-5...消费者...商品---1
还有 1 没有消费
已经生产了:2
Thread-2...生产者...商品---1
Thread-5...消费者...商品---1
还有 1 没有消费
已经生产了:2
Thread-2...生产者...商品---1
Thread-5...消费者...商品---1
还有 1 没有消费
已经生产了:2
Thread-2...生产者...商品---1
Thread-5...消费者...商品---1
还有 1 没有消费
*/

阻塞队列BlockingQueue控制线程通信

BlockingQueue是Queue的子接口,具有一个特征:向BlockingQueue中放入元素,队列已满,则线程被阻塞,当从BlockingQueue取出元素时,若队列已空,则线程被阻塞,程序的两个线程通过交替向BlockingQueue中放入、取出元素,可以很好的控制线程的通信。

//BlockingQueue提供如下两个支持阻塞的方法
put(E e)  //尝试把Eu元素放如BlockingQueue中,如果该队列的元素已满,则阻塞该线程。
take()  //尝试从BlockingQueue的头部取出元素,如果该队列的元素已空,则阻塞该线程。
//BlockingQueue继承了Queue接口,当然也可以使用Queue接口中的方法
//在队列尾部插入元素
add(E e)//当该队列已满时,抛出异常
offer(E e)//当该队列已满时,返回false
put(E e)//当该队列已满时,阻塞队列。

//在队列头部删除并返回删除的元素。
remove()//当该队列已空时,抛出异常
poll()//当该队列已空时,返回false
take()//当该队列已空时,阻塞队列

//在队列头部取出但不删除元素
element()//当队列已空时,抛出异常
peek()//当队列已空时,返回false

//BlockingQueue接口包含如下5个实现类:
ArrayBlockingQueue //基于数组实现的BlockingQueue队列。
LinkedBlockingQueue //基于链表实现的BlockingQueue队列。
PriorityBlockingQueue //它并不是保准的阻塞队列,该队列调用remove()、poll()、take()等方法提取出元素时,并不是取出队列中存在时间最长的元素,而是队列中最小的元素。 它判断元素的大小即可根据元素(实现Comparable接口)的本身大小来自然排序,也可使用Comparator进行定制排序。
SynchronousQueue //同步队列。对该队列的存、取操作必须交替进行。
DelayQueue //它是一个特殊的BlockingQueue,底层基于PriorityBlockingQueue实现,不过,DelayQueue要求集合元素都实现Delay接口(该接口里只有一个long getDelay()方法) DelayQueue根据集合元素的getDalay()方法的返回值进行排序。
public class ThreadTest{
    public static void main(String[] args){
        BlockingQueue<String> blockingQueue=new ArrayBlockingQueue<String>(1);


        Producer t1=new Producer(blockingQueue);
        Consumer t2=new Consumer(blockingQueue);
        Consumer t3=new Consumer(blockingQueue);
        Consumer t4=new Consumer(blockingQueue);
        t1.start();
        t2.start();
        t3.start();
        t4.start();

        try{
            t1.join();
            t2.join();
            t3.join();
            t4.join();
        }catch (Exception e){
            e.printStackTrace();
        }
        System.out.println("main finish");
    }
}

class Producer extends Thread{
    private BlockingQueue<String> blockingQueue;
    public Producer(BlockingQueue<String> blockingQueue){
        this.blockingQueue=blockingQueue;
    }

    @Override
    public synchronized void run(){
        String[] product=new String[]{
                "java",
                "C++",
                "C",
                "python",
                "go",
                "c#"
        };
        for(int i=0;i<50;i++){
            System.out.println(Thread.currentThread().getName()+"生产元素");
            try {
                System.out.println(Thread.currentThread().getName()+"生产完成: "+product[i%6]);
                blockingQueue.put(product[i%6]);
            }catch (Exception e) {
                e.printStackTrace();
            }
        }
        try{
            for(int i=0;i<10;i++){     //通知其他线程任务结束。
                System.out.println("offer the "+i+" ends");
                blockingQueue.offer("ends",100,TimeUnit.MILLISECONDS);
            }
        }catch (Exception e){
            e.printStackTrace();
        }
        System.out.println("生产者执行完毕!");
    }
}

class Consumer extends Thread{
    private BlockingQueue<String> blockingQueue;

    public Consumer(BlockingQueue<String> blockingQueue){
        this.blockingQueue=blockingQueue;
    }

    @Override
    public synchronized void run(){
        String sl=null;
        while (true){
            try{
                System.out.println(Thread.currentThread().getName()+"消费元素");
                sl=blockingQueue.take();
            }catch (Exception e){
                e.printStackTrace();
            }
            if("ends".equals(sl)){  //判断队列的任务是否完毕,若完毕则结束线程
                System.out.println(Thread.currentThread().getName() + "执行完毕: ");
                break;
            }else {
                System.out.println(Thread.currentThread().getName() + "消费完成: " + sl);
            }
        }
    }
}
/*
运行结果:
Thread-1消费元素
Thread-2消费元素
Thread-3消费元素
Thread-0生产元素
Thread-0生产完成: java
Thread-0生产元素
Thread-0生产完成: C++
Thread-1消费完成: java
Thread-2消费完成: C++
Thread-0生产元素
Thread-2消费元素
Thread-1消费元素
Thread-0生产完成: C
Thread-0生产元素
Thread-0生产完成: python
Thread-3消费完成: C
Thread-2消费完成: python
Thread-0生产元素
Thread-0生产完成: go
Thread-2消费元素
Thread-3消费元素
Thread-0生产元素
Thread-1消费完成: go
Thread-1消费元素
Thread-0生产完成: c#
Thread-0生产元素
Thread-2消费完成: c#
Thread-2消费元素
Thread-0生产完成: java
Thread-0生产元素
Thread-0生产完成: C++
Thread-3消费完成: java
Thread-3消费元素
Thread-1消费完成: C++
Thread-1消费元素
Thread-0生产元素
Thread-0生产完成: C
Thread-0生产元素
Thread-0生产完成: python
Thread-2消费完成: C
Thread-2消费元素
Thread-3消费完成: python
Thread-3消费元素
Thread-0生产元素
Thread-0生产完成: go
Thread-0生产元素
Thread-0生产完成: c#
Thread-1消费完成: go
Thread-2消费完成: c#
Thread-2消费元素
Thread-0生产元素
Thread-0生产完成: java
Thread-1消费元素
Thread-3消费完成: java
Thread-3消费元素
Thread-0生产元素
Thread-0生产完成: C++
Thread-0生产元素
Thread-0生产完成: C
Thread-2消费完成: C++
Thread-1消费完成: C
Thread-0生产元素
Thread-0生产完成: python
Thread-1消费元素
Thread-2消费元素
Thread-3消费完成: python
Thread-3消费元素
Thread-0生产元素
Thread-0生产完成: go
Thread-0生产元素
Thread-1消费完成: go
Thread-1消费元素
Thread-0生产完成: c#
Thread-0生产元素
Thread-0生产完成: java
Thread-2消费完成: c#
Thread-2消费元素
Thread-3消费完成: java
Thread-3消费元素
Thread-0生产元素
Thread-0生产完成: C++
Thread-0生产元素
Thread-0生产完成: C
Thread-1消费完成: C++
Thread-1消费元素
Thread-2消费完成: C
Thread-2消费元素
Thread-0生产元素
Thread-0生产完成: python
Thread-0生产元素
Thread-0生产完成: go
Thread-3消费完成: python
Thread-1消费完成: go
Thread-1消费元素
Thread-3消费元素
Thread-0生产元素
Thread-0生产完成: c#
Thread-0生产元素
Thread-0生产完成: java
Thread-2消费完成: c#
Thread-1消费完成: java
Thread-1消费元素
Thread-0生产元素
Thread-2消费元素
Thread-0生产完成: C++
Thread-0生产元素
Thread-0生产完成: C
Thread-3消费完成: C++
Thread-3消费元素
Thread-1消费完成: C
Thread-1消费元素
Thread-0生产元素
Thread-0生产完成: python
Thread-0生产元素
Thread-0生产完成: go
Thread-2消费完成: python
Thread-2消费元素
Thread-3消费完成: go
Thread-3消费元素
Thread-0生产元素
Thread-0生产完成: c#
Thread-0生产元素
Thread-1消费完成: c#
Thread-1消费元素
Thread-0生产完成: java
Thread-0生产元素
Thread-2消费完成: java
Thread-2消费元素
Thread-0生产完成: C++
Thread-0生产元素
Thread-0生产完成: C
Thread-3消费完成: C++
Thread-1消费完成: C
Thread-1消费元素
Thread-0生产元素
Thread-0生产完成: python
Thread-0生产元素
Thread-0生产完成: go
Thread-3消费元素
Thread-1消费完成: go
Thread-1消费元素
Thread-0生产元素
Thread-2消费完成: python
Thread-2消费元素
Thread-0生产完成: c#
Thread-0生产元素
Thread-3消费完成: c#
Thread-3消费元素
Thread-0生产完成: java
Thread-0生产元素
Thread-1消费完成: java
Thread-1消费元素
Thread-0生产完成: C++
Thread-0生产元素
Thread-2消费完成: C++
Thread-2消费元素
Thread-0生产完成: C
Thread-0生产元素
Thread-0生产完成: python
Thread-3消费完成: C
Thread-3消费元素
Thread-1消费完成: python
Thread-1消费元素
Thread-0生产元素
Thread-0生产完成: go
Thread-0生产元素
Thread-0生产完成: c#
Thread-2消费完成: go
Thread-2消费元素
Thread-3消费完成: c#
Thread-3消费元素
Thread-0生产元素
Thread-0生产完成: java
Thread-0生产元素
Thread-0生产完成: C++
Thread-1消费完成: java
Thread-1消费元素
Thread-2消费完成: C++
Thread-2消费元素
Thread-0生产元素
Thread-0生产完成: C
Thread-0生产元素
Thread-0生产完成: python
Thread-3消费完成: C
Thread-3消费元素
Thread-1消费完成: python
Thread-1消费元素
Thread-0生产元素
Thread-0生产完成: go
Thread-0生产元素
Thread-0生产完成: c#
Thread-2消费完成: go
Thread-0生产元素
Thread-3消费完成: c#
Thread-3消费元素
Thread-0生产完成: java
Thread-2消费元素
Thread-1消费完成: java
Thread-1消费元素
Thread-0生产元素
Thread-0生产完成: C++
Thread-3消费完成: C++
Thread-3消费元素
offer the 0 ends
offer the 1 ends
Thread-2执行完毕: 
Thread-1执行完毕: 
offer the 2 ends
offer the 3 ends
Thread-3执行完毕: 
offer the 4 ends
offer the 5 ends
offer the 6 ends
offer the 7 ends
offer the 8 ends
offer the 9 ends
生产者执行完毕!
main finish
*/

线程组

线程组表示一个线程的集合,线程组既可以包含线程,也可以包含其他线程组,构成一棵树,在树中,除了初始线程组外,每个线程组都有一个父线程组。一般而言,会将一些功能相同或者类似的线程放置到同一个线程组中,方便统一管理。

java中使用ThreadGroup类描述线程,其体系结构如下图:

新线程创建时如果没有指定线程组,默认属于当前线程所在的线程组。新线程创建时如果指定了线程组,其线程组的父线程组就是当前线程所在的线程组。

public class MainA {
    public static void main(String[] args){
        //当前main线程所在的线程组的名字:
        System.out.println(Thread.currentThread().getThreadGroup().getName());
        //当前main线程所在的线程组父线程组名字:
        System.out.println(Thread.currentThread().getThreadGroup().getParent().getName());
        //自定义一个线程组
        ThreadGroup threadGroup=new ThreadGroup("tg1");
        //创建线程,指定线程组为tg1
        Thread thread=new Thread(threadGroup, new Runnable() {
            @Override
            public void run() {
                Thread t=new Thread(new Runnable() {
                    @Override
                    public void run() {
                        System.out.println("this is Thread t's runnable");
                    }
                });
                //新线程t没有指定线程组,默认为thread的线程组即tg1
                System.out.println(t.getThreadGroup().getName());
            }
        });
        //新线程thread创建时指定的线程组
        System.out.println(thread.getThreadGroup().getName());
        //新线程thread创建时指定的线程组,父线程组默认是当前main线程的线程组:
        System.out.println(thread.getThreadGroup().getParent().getName());
    }
}
/*

运行结果:
main
system
tg1
main
*/
//构造器
//构建一个指定名字的线程组
ThreadGroup(String name)
//构建一个指定名字跟父线程组的线程组 
ThreadGroup(ThreadGroup parent, String name)


//方法
//获取此线程组活动线程个数,是一个估值,不精确
int activeCount() 
//获取此线程获取的子线程组个数
int activeGroupCount() 
//销毁此线程组中所有的线程与子线程组
void destroy() 
//获取此线程组的名字
String getName() 
//获取此线程组的父线程组
ThreadGroup getParent() 
//把此线程组及其子组中的所有活动线程复制到指定数组中。
enumerate(Thread[] list) 
//把对此线程组中的所有活动子组的引用复制到指定数组中。
enumerate(ThreadGroup[] list) 
//中断此线程组中的所有线程
void interrupt() 
//将有关此线程组的信息打印到标准输出。
void list() 
//当此线程组中的线程因为一个未捕获的异常而停止,并且线程没有安装特定 
//Thread.UncaughtExceptionHandler 时,由 JVM调用此方法。
void uncaughtException(Thread t, Throwable e)

//Thread线程
//参数1:线程组
Thread(ThreadGroup group, Runnable target) 
Thread(ThreadGroup group, Runnable target, String name)
public class MainB {
    public static void main(String[] args){
        //创建指定名字的线程组
        ThreadGroup tg=new ThreadGroup("tg1");
        //获取线程组名字
        System.out.println(tg.getName());
        //获取线程组父线程
        System.out.println(tg.getParent());
        for(int i=0;i<5;i++){
            new Thread(tg, new Runnable() {
                @Override
                public void run() {
                    try{
                        Thread.sleep(1000);
                    }catch (InterruptedException e){
                        e.printStackTrace();
                    }
                }
            }).start();
        }
        //该线程组活动线程数
        System.out.println(tg.activeCount());
        //该线程组活动的子线程组数
        System.out.println(tg.activeGroupCount());
        tg.list();
    }
}
/*
运行结果:
tg1
java.lang.ThreadGroup[name=main,maxpri=10]
5
0
java.lang.ThreadGroup[name=tg1,maxpri=10]
    Thread[Thread-0,5,tg1]
    Thread[Thread-1,5,tg1]
    Thread[Thread-2,5,tg1]
    Thread[Thread-3,5,tg1]
    Thread[Thread-4,5,tg1]
*/

作用由两个

1、线程统一管理,统一停止

public class MainC {
    public static void main(String[] args){

        //创建指定的线程组
        ThreadGroup tg=new ThreadGroup("tg1");
        NumCount numCount=new NumCount();
        //启动线程执行
        numCount.count(tg);
        //手动等待计算完
        numCount.waitFinish(tg);
        //强制终止
        //tg.interrupt();
        System.out.println(numCount.getSum());
    }
}
//分段计算1到21亿的和
class SumTask implements Runnable{
    //分段计算的和
    private long sum=0;
   //计算开始位置
    private long begin;
    //计算结束位置,左闭右开原则
    private long end;

    private NumCount numCount;

    public SumTask(NumCount numCount,long begin,long end){
        this.numCount=numCount;
        this.begin=begin;
        this.end=end;
    }

    @Override
    public void run(){
        try{
            if(!Thread.currentThread().isInterrupted()){
                for(long i=begin;i<end;i++){
                    sum+=i;
                }
            }
            //将计算结果归总到结果中
            this.numCount.sum(sum);
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    public long getSum(){
        return sum;
    }
}

class NumCount{
    //21亿
    private long total=2100000000L;
    //分100个线程计算
    private int threadSize=100;
    //每个线程计算的长度
    private long interval=total/threadSize;
    //总和
    private volatile long sum=0;

    public void count(ThreadGroup tg){
        long begin=0;
        long end=0;
        //启动100个线程
        for(int i=0;i<100;i++){
            begin=interval*i;
            end=begin+interval+1;
            new Thread(tg,new SumTask(this,begin,end),"SumTask_"+i).start();
        }
    }

    public synchronized void sum(long value){
        System.out.println(Thread.currentThread().getName()+":"+value);
        this.sum+=value;
    }

    public long getSum(){
        return sum;
    }
    //等待所有线程执行完毕
    public void waitFinish(ThreadGroup tg){
        while(tg.activeCount()>0){
            try{
                Thread.sleep(1000);
            }catch (InterruptedException e){
                e.printStackTrace();
            }
        }
    }
}
/*
运行结果:
SumTask_5:2425500115500000
SumTask_7:3307500157500000
SumTask_6:2866500136500000
SumTask_3:1543500073500000
SumTask_1:661500031500000
SumTask_2:1102500052500000
SumTask_9:4189500199500000
SumTask_4:1984500094500000
SumTask_10:4630500220500000
SumTask_11:5071500241500000
SumTask_14:6394500304500000
SumTask_12:5512500262500000
SumTask_19:8599500409500000
SumTask_16:7276500346500000
SumTask_20:9040500430500000
SumTask_23:10363500493500000
SumTask_27:12127500577500000
SumTask_32:14332500682500000
SumTask_35:15655500745500000
SumTask_31:13891500661500000
SumTask_0:220500010500000
SumTask_39:17419500829500000
SumTask_40:17860500850500000
SumTask_44:19624500934500000
SumTask_47:20947500997500000
SumTask_48:21388501018500000
SumTask_51:22711501081500000
SumTask_52:23152501102500000
SumTask_56:24916501186500000
SumTask_60:26680501270500000
SumTask_68:30208501438500000
SumTask_67:29767501417500000
SumTask_75:33295501585500000
SumTask_76:33736501606500000
SumTask_63:28003501333500000
SumTask_80:35500501690500000
SumTask_91:40351501921500000
SumTask_84:37264501774500000
SumTask_95:42115502005500000
SumTask_13:5953500283500000
SumTask_99:43879502089500000
SumTask_17:7717500367500000
SumTask_18:8158500388500000
SumTask_21:9481500451500000
SumTask_29:13009500619500000
SumTask_22:9922500472500000
SumTask_33:14773500703500000
SumTask_37:16537500787500000
SumTask_38:16978500808500000
SumTask_46:20506500976500000
SumTask_42:18742500892500000
SumTask_50:22270501060500000
SumTask_45:20065500955500000
SumTask_41:18301500871500000
SumTask_34:15214500724500000
SumTask_30:13450500640500000
SumTask_26:11686500556500000
SumTask_25:11245500535500000
SumTask_96:42556502026500000
SumTask_92:40792501942500000
SumTask_88:39028501858500000
SumTask_87:38587501837500000
SumTask_83:36823501753500000
SumTask_79:35059501669500000
SumTask_72:31972501522500000
SumTask_71:31531501501500000
SumTask_64:28444501354500000
SumTask_59:26239501249500000
SumTask_55:24475501165500000
SumTask_24:10804500514500000
SumTask_43:19183500913500000
SumTask_36:16096500766500000
SumTask_28:12568500598500000
SumTask_15:6835500325500000
SumTask_8:3748500178500000
SumTask_98:43438502068500000
SumTask_97:42997502047500000
SumTask_94:41674501984500000
SumTask_93:41233501963500000
SumTask_90:39910501900500000
SumTask_89:39469501879500000
SumTask_86:38146501816500000
SumTask_85:37705501795500000
SumTask_82:36382501732500000
SumTask_81:35941501711500000
SumTask_78:34618501648500000
SumTask_74:32854501564500000
SumTask_77:34177501627500000
SumTask_73:32413501543500000
SumTask_69:30649501459500000
SumTask_70:31090501480500000
SumTask_66:29326501396500000
SumTask_65:28885501375500000
SumTask_62:27562501312500000
SumTask_61:27121501291500000
SumTask_58:25798501228500000
SumTask_49:21829501039500000
SumTask_57:25357501207500000
SumTask_54:24034501144500000
SumTask_53:23593501123500000
2205000105000000000

Process finished with exit code 0
*/

2、线程组统一异常处理

线程组提供一个uncaughtException 方法实现统一异常处理。当组内线程执行报错时, 会字典调用uncaughtException方法,传入线程对象以及异常信息。

public class MainC {
    public static void main(String[] args){

        //创建指定的线程组
       // ThreadGroup tg=new ThreadGroup("tg1");
        final NumCount numCount=new NumCount();

        //自定义一个线程组对象,重写uncaughtException
        ThreadGroup tg=new ThreadGroup("tg1"){
            //需要重写uncaughtException方法,表示线程报错后统一调用该方法
            @Override
            public void uncaughtException(Thread t,Throwable e){
                System.out.println(t.getName()+":"+e.getMessage());
                //执行额外的补救,因为使用的是接口方法启动线程无法获取到线程的target接口,只能通过反射间接获取,最佳的操作是SumTask继承Thread类
                try{
                    //通过反射获取Thread中拥有的runable对象
                    Class clz=t.getClass();
                    Field target=clz.getDeclaredField("target");
                    target.setAccessible(true);
                    SumTask task=(SumTask)target.get(t);
                    long sum=0;
                    //手动补救
                    for(long i=task.getBegin();i<task.getEnd();i++){
                        sum+=i;
                    }
                    numCount.sum(sum);
                }catch (NoSuchFieldException e1){
                    e1.printStackTrace();
                }catch (IllegalAccessException e1){
                    e1.printStackTrace();
                }
            }
        };
        //启动线程执行
        numCount.count(tg);
        //手动等待计算完
        numCount.waitFinish(tg);
        //tg.interrupt();
        System.out.println(numCount.getSum());
    }
}
//分段计算1到21亿的和
class SumTask implements Runnable{
    //分段计算的和
    private long sum=0;
   //计算开始位置
    private long begin;
    //计算结束位置,左闭右开原则
    private long end;

    private NumCount numCount;

    public SumTask(NumCount numCount,long begin,long end){
        this.numCount=numCount;
        this.begin=begin;
        this.end=end;
    }

    @Override
    public void run(){
        if(begin==1659000000){
            throw new RuntimeException("在"+begin+"到"+end+"数字段计算报错");
        }
        try{
            if(!Thread.currentThread().isInterrupted()){
                for(long i=begin;i<end;i++){
                    sum+=i;
                }
            }
            //将计算结果归总到结果中
            this.numCount.sum(sum);
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    public long getSum(){
        return sum;
    }

    public long getBegin() {
        return begin;
    }

    public long getEnd() {
        return end;
    }
}

class NumCount{
    //21亿
    private long total=2100000000L;
    //分100个线程计算
    private int threadSize=100;
    //每个线程计算的长度
    private long interval=total/threadSize;
    //总和
    private volatile long sum=0;

    public void count(ThreadGroup tg){
        long begin=0;
        long end=0;
        //启动100个线程
        for(int i=0;i<100;i++){
            begin=interval*i;
            end=begin+interval+1;
            new Thread(tg,new SumTask(this,begin,end),"SumTask_"+i).start();
        }
    }

    public synchronized void sum(long value){
        System.out.println(Thread.currentThread().getName()+":"+value);
        this.sum+=value;
    }

    public long getSum(){
        return sum;
    }
    //等待所有线程执行完毕
    public void waitFinish(ThreadGroup tg){
        while(tg.activeCount()>0){
            try{
                Thread.sleep(1000);
            }catch (InterruptedException e){
                e.printStackTrace();
            }
        }
    }
}
/*
运行结果:
SumTask_2:1102500052500000
SumTask_8:3748500178500000
SumTask_3:1543500073500000
SumTask_7:3307500157500000
SumTask_1:661500031500000
SumTask_4:1984500094500000
SumTask_0:220500010500000
SumTask_11:5071500241500000
SumTask_12:5512500262500000
SumTask_16:7276500346500000
SumTask_15:6835500325500000
SumTask_20:9040500430500000
SumTask_19:8599500409500000
SumTask_28:12568500598500000
SumTask_36:16096500766500000
SumTask_31:13891500661500000
SumTask_44:19624500934500000
SumTask_39:17419500829500000
SumTask_24:10804500514500000
SumTask_43:19183500913500000
SumTask_59:26239501249500000
SumTask_52:23152501102500000
SumTask_55:24475501165500000
SumTask_51:22711501081500000
SumTask_63:28003501333500000
SumTask_67:29767501417500000
SumTask_71:31531501501500000
SumTask_80:35500501690500000
SumTask_5:2425500115500000
SumTask_75:33295501585500000
SumTask_10:4630500220500000
SumTask_17:7717500367500000
SumTask_14:6394500304500000
SumTask_18:8158500388500000
SumTask_26:11686500556500000
SumTask_22:9922500472500000
SumTask_25:11245500535500000
SumTask_30:13450500640500000
SumTask_37:16537500787500000
SumTask_41:18301500871500000
SumTask_42:18742500892500000
SumTask_46:20506500976500000
SumTask_53:23593501123500000
SumTask_58:25798501228500000
SumTask_54:24034501144500000
SumTask_62:27562501312500000
SumTask_73:32413501543500000
SumTask_77:34177501627500000
SumTask_81:35941501711500000
SumTask_69:30649501459500000
SumTask_74:32854501564500000
SumTask_85:37705501795500000
SumTask_86:38146501816500000
SumTask_82:36382501732500000
SumTask_78:34618501648500000
SumTask_70:31090501480500000
SumTask_66:29326501396500000
SumTask_61:27121501291500000
SumTask_65:28885501375500000
SumTask_57:25357501207500000
SumTask_50:22270501060500000
SumTask_49:21829501039500000
SumTask_45:20065500955500000
SumTask_38:16978500808500000
SumTask_33:14773500703500000
SumTask_34:15214500724500000
SumTask_79:在1659000000到1680000001数字段计算报错
SumTask_29:13009500619500000
SumTask_21:9481500451500000
SumTask_13:5953500283500000
SumTask_6:2866500136500000
SumTask_79:35059501669500000
SumTask_9:4189500199500000
SumTask_76:33736501606500000
SumTask_72:31972501522500000
SumTask_64:28444501354500000
SumTask_40:17860500850500000
SumTask_60:26680501270500000
SumTask_48:21388501018500000
SumTask_56:24916501186500000
SumTask_68:30208501438500000
SumTask_47:20947500997500000
SumTask_35:15655500745500000
SumTask_27:12127500577500000
SumTask_99:43879502089500000
SumTask_23:10363500493500000
SumTask_32:14332500682500000
SumTask_96:42556502026500000
SumTask_95:42115502005500000
SumTask_92:40792501942500000
SumTask_88:39028501858500000
SumTask_91:40351501921500000
SumTask_87:38587501837500000
SumTask_84:37264501774500000
SumTask_83:36823501753500000
SumTask_98:43438502068500000
SumTask_97:42997502047500000
SumTask_94:41674501984500000
SumTask_93:41233501963500000
SumTask_89:39469501879500000
SumTask_90:39910501900500000
2205000105000000000

Process finished with exit code 0
*/

猜你喜欢

转载自blog.csdn.net/qq_33232152/article/details/81976078
今日推荐