多线程与线程池

1.什么是线程?

线程(Thread):字面意思是线路,即应用程序(进程)中程序执行的线路。JAVA虚拟机允许一个应用程序中,可以同时并发存在多条程序执行线路。每个线程都有一个优先级属性,优先级高的线程,可能会被CPU优先执行。换言之,线程是应用程序(进程)在运行过程中,通过操作系统向CPU发起的一个任务,这个任务只能访问当前进程的内存资源。

百度百科:线程(英语:thread)是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。在Unix System V及SunOS中也被称为轻量进程(lightweight processes),但轻量进程更多指内核线程(kernel thread),而把用户线程(user thread)称为线程。

 

1.1 线程状态

线程状态:即线程对象再不同的运行时期存在这不同的状态,在Thread类中通过一个内部枚举类State保存状态信息。

点击查看代码
public enum State {
        /**
         * Thread state for a thread which has not yet started.
         */
        NEW,

        /**
         * Thread state for a runnable thread.  A thread in the runnable
         * state is executing in the Java virtual machine but it may
         * be waiting for other resources from the operating system
         * such as processor.
         */
        RUNNABLE,

        /**
         * Thread state for a thread blocked waiting for a monitor lock.
         * A thread in the blocked state is waiting for a monitor lock
         * to enter a synchronized block/method or
         * reenter a synchronized block/method after calling
         * {@link Object#wait() Object.wait}.
         */
        BLOCKED,

        /**
         * Thread state for a waiting thread.
         * A thread is in the waiting state due to calling one of the
         * following methods:
         * <ul>
         *   <li>{@link Object#wait() Object.wait} with no timeout</li>
         *   <li>{@link #join() Thread.join} with no timeout</li>
         *   <li>{@link LockSupport#park() LockSupport.park}</li>
         * </ul>
         *
         * <p>A thread in the waiting state is waiting for another thread to
         * perform a particular action.
         *
         * For example, a thread that has called <tt>Object.wait()</tt>
         * on an object is waiting for another thread to call
         * <tt>Object.notify()</tt> or <tt>Object.notifyAll()</tt> on
         * that object. A thread that has called <tt>Thread.join()</tt>
         * is waiting for a specified thread to terminate.
         */
        WAITING,

        /**
         * Thread state for a waiting thread with a specified waiting time.
         * A thread is in the timed waiting state due to calling one of
         * the following methods with a specified positive waiting time:
         * <ul>
         *   <li>{@link #sleep Thread.sleep}</li>
         *   <li>{@link Object#wait(long) Object.wait} with timeout</li>
         *   <li>{@link #join(long) Thread.join} with timeout</li>
         *   <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li>
         *   <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li>
         * </ul>
         */
        TIMED_WAITING,

        /**
         * Thread state for a terminated thread.
         * The thread has completed execution.
         */
        TERMINATED;
    }

可通过Thread.getState()获取当前线程的状态:

点击查看代码
/**
     * Returns the state of this thread.
     * This method is designed for use in monitoring of the system state,
     * not for synchronization control.
     *
     * @return this thread's state.
     * @since 1.5
     */
    public State getState() {
        // get current thread state
        return sun.misc.VM.toThreadState(threadStatus);
    }

1.2JAVA线程状态转换

1.3 sleep()与yield()

  • 线程休眠sleep()

Thread类的sleep()方法,使当前正在执行的线程以指定的毫秒数暂时停止执行,具体停止时间取决于系统定时器和调度程序的精度和准确性。当前线程状态由RUNNABLE切换到TIME_WAITING。sleep方法不会使线程丢失任何监视器,因此当前线程仍然占用CUP分片。

  • 线程让步yield()

yield()方法对线程调度器发出一个暗示,即当前线程愿意让出正在使用的处理器。调度程序可以响应暗示的请求,也可以自由的忽略这个提示。

注意:yield()方法仅仅是一个暗示,没有任何机制保证它一定会采纳。关于线程调度器,它是Java线程机制的底层对象,可以把CPU的使用权从一个线程转移到另一个线程。如果你的计算机是多核处理器,如何分配线程得到不同的处理器执行任务,都需要依赖线程调度器。

 

1.4守护者线程

  • 概念:JAVA线程中有两种线程,一种是用户线程,另一中是守护线程(Daemon)。

所谓守护线程,是指在程序运行的时候在后台提供一种通用的服务线程,比如垃圾回收线程就是一个很称职的守护者(当一个对象不再被引用的时候,内存回收它占领的空间,以便被后来的新对象使用)。

Daemon线程与用户线程在使用时没有任何区别,唯一不同的是:当所有用户线程结束是时,程序也会终止,Java虚拟机不管是否存在守护者线程都会推出。

Thread.setDaemon()方法可将用户线程标记为守护者,调用isDaemon()方法可以判断线程是否是一个守护者。

JAVA线程的实现/创建方式

  • 继承Thread类:Thread类本质上是实现了Runnable接口的一个实例,代表一个线程的实例。启动线程的唯一方法就是通过Thread类的start()方法。start()方法是一个native方法,它将启动一个新线程,并执行run()方法。

  • 实现Runnable接口(无返回值):如果创建的类已经extends一个类,就无法直接extends Thread,此时,可以实现一个Runnable接口。

  • 实现Callable接口(有返回值):Callable接口执行完毕之后返回一个Future对象,在该对象上调用get方法就可以获取到Callable任务返回的Object。

2. 线程安全与共享资源竞争

线程安全问题:多个线程同时访问同一个对象的某个方法时,如果该方法中存在对共享资源的操作,则可能引发线程安全问题。典型的共享资源有:对象的成员变量、磁盘文件、静态变量等。

为防止出现资源并发冲突的解决思路如下:当共享资源被任务使用时,要对资源提前加锁。所有任务都采用抢占模式,即某个任务会抢先对共享资源加上一把锁。如果这是一个排它锁,其他任务在资源被解锁前就无法访问。当然,如果是共享锁,当你浏览某些数据时,其他任务也可以同时浏览,但是不允许修改。

JAVA中提供资源同步的关键字:synchronized,它的作用是获取指定对象的监视器锁。

synchronized用户获取当前对象的内置监视器,同步方法与同步方法之间产生互斥现象,同步方法与非同步方法之间不产生互斥现象。即同一个对象之间的两个synchronized方法会产生互斥现象。

 

Synchronized(Object.class)

Synchronized(Object.class)使用的是类锁,不是对象锁。注意,不要轻易使用Object.class的类锁,因为整个项目中,如果其他业务模块也使用Object.class的类锁,这样就会产生并发冲突。合理使用类锁的基本原则是,尽量使用当前类的监视器锁!

3. 设置守护进程

点击查看代码
package com.icss.守护者线程;


public class Daemon {
	public static void main(String[] args) {
		Thread t1 = new Thread(new Runnable() {
			public void run() {
				while(true) {
					System.out.println(Thread.currentThread().getId() + ",run...");
					try {
						Thread.sleep(1000);
					} catch (Exception e) {
					}
				}
			}
		});
		t1.setDaemon(true);
		t1.start();
		Thread t2 = new Thread(new Runnable() {
			public void run() {
				for(int i=0;i<5;i++) {
					System.out.println(Thread.currentThread().getId() + ",i=" + i);
					try {
						Thread.sleep(1000);
					} catch (Exception e) {
					}
				}

			}
		});
		t2.start();
		System.err.println( Thread.currentThread().getName() + "-主线程结束....");
	}
}

#设置线程的优先级

点击查看代码
package com.icss.线程优先级;

import java.math.BigDecimal;


public class FloatArithmetic implements Runnable{
	 private int priority;
	    public FloatArithmetic(int priority) {
	        this.priority = priority;
	    }
	    public void run() {
	        BigDecimal value = new BigDecimal("0");
	        //按照参数传递值修改当前线程优先级.
	        Thread.currentThread().setPriority(priority);
	        BigDecimal pi = new BigDecimal(Math.PI);
	        BigDecimal e = new BigDecimal(Math.E);
	        //足够耗时的计算,使任务调度可以反应.
	        for (int i = 0; i < 3000; i++) {
	            for (int j = 0; j < 3000; j++) {
	                value = value.add(pi.add(e).divide(pi,4));
	            }
	        }
	        Thread self = Thread.currentThread();
	        System.out.println("线程编号为" + self.getId() + ", 优先级为"
	                + self.getPriority() + ", 计算结果为" + value.doubleValue());
	    }

	    public static void main(String[] args) {
	        new Thread(new FloatArithmetic(Thread.MIN_PRIORITY)).start();
	        new Thread(new FloatArithmetic(Thread.MIN_PRIORITY)).start();
	        new Thread(new FloatArithmetic(Thread.MIN_PRIORITY)).start();
	        new Thread(new FloatArithmetic(Thread.MIN_PRIORITY)).start();
	        new Thread(new FloatArithmetic(Thread.NORM_PRIORITY)).start();
	        new Thread(new FloatArithmetic(Thread.MAX_PRIORITY)).start();
	    }

}

#同一个类的两个静态方法互斥

同一个类之间的两个synchronized静态方法通过synchronized方法获取当前类(class extends object)的内置锁,即object对象的内置监视器来产生互斥作用。

 

#模拟售卖火车票

TicketClass

点击查看代码
package com.icss.train3;

public class Ticket {
	private String tno;		
	public String getTno() {
		return tno;
	}
	public Ticket(String tno) {
		this.tno = tno;
	}
}

#窗口Class

点击查看代码
package com.icss.train3;

class Curtain extends Thread {
   private String cno;       
   public String getCno() {
      return cno;
   }
   public Curtain(String cno,Runnable runable) {
      super(runable);     //给父类的任务赋值
      this.cno = cno;
   }  
}

#售卖火车票

点击查看代码
package com.icss.train3;

import java.util.LinkedList;

public class TicketTask implements Runnable{	
	private LinkedList<Ticket> tickets;	
	
	public TicketTask(LinkedList<Ticket> tickets) {
		this.tickets = tickets;
	}
	public void run() {
        while(this.tickets.size() > 0) {
        	synchronized (this) {
        		Ticket tick = tickets.removeLast();
        		System.out.println("窗口" + Thread.currentThread().getId()
							  + "售出:" + tick.getTno());	
			} 
        	try {
				Thread.sleep(100);
		} catch (Exception e) {	}        	
        }		
	}
}

 

#main

点击查看代码
package com.icss.train3;

import java.util.LinkedList;

public class Test {
	public static void main(String[] args) {
		LinkedList<Ticket> ticks = new LinkedList<>();
		for(int i=0;i<50;i++) {
			Ticket tick = new Ticket("t" + i);
			ticks.add(tick);
		}

		new Curtain("c1",new TicketTask(ticks) ).start();
		new Curtain("c2",new TicketTask(ticks) ).start();
		new Curtain("c3",new TicketTask(ticks) ).start();
	}

}
点击查看代码
窗口c1,9售出 50
窗口c3,11售出 49
窗口c2,10售出 48
窗口c3,11售出 47
窗口c1,9售出 46
窗口c2,10售出 45
窗口c1,9售出 44
窗口c2,10售出 43
窗口c3,11售出 42
窗口c1,9售出 41
窗口c3,11售出 40
窗口c2,10售出 39
窗口c2,10售出 38

 

3.多线程通信:wait()和notify()

java.lang.Object类中内置了用于线程通讯的方法:wait() 和 notify()、notifyAll()

点击查看代码
public final native void notify();

public final native void notifyAll();

public final void wait() throws InterruptedException {
        wait(0);
    }

 

点击查看代码
package com.icss.ui;

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

        Object object = new Object();
        new Thread(new Runnable() {
            public void run() {
                for (int i = 0; i < 10; i++) {
                    System.out.println(Thread.currentThread().getId() + ",i=" + i);
                    if (i == 5) {

                        synchronized (object) {  //先获取object对象的监视器锁
                            try {
                                System.out.println(Thread.currentThread().getId()
                                        + "开始等待...");
                                object.wait();   //使当前线程进入阻塞等待状态....
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                        }
                    }
                }
            }
        }).start();

        new Thread(new Runnable() {
            public void run() {
                System.out.println(Thread.currentThread().getId() + " running...");
                try {
                    Thread.sleep(1000);
                    synchronized (object) {  //先获取object对象的监视器锁
                        System.out.println(Thread.currentThread().getId()
                                + ",发送notify通知...");
                        object.notify();
                    }
                } catch (Exception e) {
                }
            }
        }).start();

        System.out.println("主线程:" + Thread.currentThread().getId() + "结束...");
    }

}

 

3.1 wait()和notify()实例:厨师与侍者

#Chef

点击查看代码
package com.icss.chef2;

class Chef implements Runnable {
    private Order order;
    private Waiter waiter;

    public void setOrder(Order order) {
        this.order = order;
    }

    public void setWaiter(Waiter waiter) {
        this.waiter = waiter;
    }

    public void run() {
        while (true) {
            synchronized (this) {
                try {
                    System.out.println("厨师空闲等待中...");
                    this.wait();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            //厨师收到订单通知
            int rand = (int) (Math.random() * 800);
            try {
                Thread.sleep(rand);
            } catch (Exception e) {
            }
            System.out.println("厨师做菜完成,通知waiter取菜...");
            synchronized (this.waiter) {
                waiter.setMsgID(2);
                waiter.notify();
            }
        }
    }
}

#waiter

点击查看代码
package com.icss.chef2;

class Waiter implements Runnable {
    private Order order;
    private Chef chef;
    private int msgID = 1; //如果新订单通知id=1, 如果是取菜通知id=2

    public void setMsgID(int msgID) {
        this.msgID = msgID;
    }

    public void setChef(Chef chef) {
        this.chef = chef;
    }

    public void setOrder(Order order) {
        this.order = order;
    }

    public void run() {
        while (true) {
            synchronized (this) {
                try {
                    System.out.println("服务员空闲等待中...");
                    this.wait();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            if (msgID == 1) {
                //服务员收到新订单通知
                System.out.println("waiter收到订单:"
                        + this.order.getDno() + "," + this.order.getInfo());
                //通知厨师做菜
                synchronized (chef) {
                    System.out.println("waiter通知厨师做菜...");
                    chef.setOrder(order);
                    chef.notify();
                }
            } else {
                //服务员收到了取菜通知
                System.out.println("waiter取菜给顾客....");
            }
        }
    }
}

#Order

点击查看代码
package com.icss.chef2;

import java.io.Serializable;

public class Order implements Serializable {
    private String dno;
    private String info;

    public String getDno() {
        return dno;
    }

    public String getInfo() {
        return info;
    }

    public void setInfo(String info) {
        this.info = info;
    }

    public Order(String dno) {
        this.dno = dno;
    }
}

#Restaurant

点击查看代码
package com.icss.chef2;

class Restaurant implements Runnable {
    private Waiter waiter;

    public void setWaiter(Waiter waiter) {
        this.waiter = waiter;
    }

    public void run() {
        while (true) {
            int rand = (int) (Math.random() * 5000);
            try {
                System.out.println("饭店等待顾客中---------------");
                Thread.sleep(rand);
                String dno = "d" + System.currentTimeMillis();
                Order order = new Order(dno);
                order.setInfo("宫保鸡丁一份...");
                System.out.println("顾客来了,通知服务员点菜,生成订单:" + dno);
                synchronized (waiter) {
                    waiter.setOrder(order);  //把订单给服务员
                    waiter.setMsgID(1);
                    waiter.notify();
                }

            } catch (Exception e) {
            }
        }
    }
}

#Main

点击查看代码
package com.icss.chef2;

public class Test {
    public static void main(String[] args) {
        Chef chef = new Chef();
        Waiter waiter = new Waiter();
        waiter.setChef(chef);
        chef.setWaiter(waiter);
        Restaurant rest = new Restaurant();
        rest.setWaiter(waiter);
        new Thread(waiter).start();
        new Thread(chef).start();
        new Thread(rest).start();
    }

}

#运行结果

点击查看代码
Waiter-9,等待中
厨师-10,接到新订单,开始做菜...
厨师-10,通知waiter取食物
Waiter-9,收到通知,取走订单 
Waiter-9,等待中
厨师-10,接到新订单,开始做菜...
厨师-10,通知waiter取食物
Waiter-9,收到通知,取走订单 
Waiter-9,等待中
厨师-10,接到新订单,开始做菜...
厨师-10,通知waiter取食物
Waiter-9,收到通知,取走订单 
Waiter-9,等待中
厨师-10,接到新订单,开始做菜...
厨师-10,通知waiter取食物
Waiter-9,收到通知,取走订单 
Waiter-9,等待中
厨师-10,接到新订单,开始做菜...
厨师-10,通知waiter取食物
Waiter-9,收到通知,取走订单 
Waiter-9,等待中
厨师-10,接到新订单,开始做菜...
厨师-10,通知waiter取食物
Waiter-9,收到通知,取走订单 
Waiter-9,等待中
厨师-10,接到新订单,开始做菜...
厨师-10,通知waiter取食物
Waiter-9,收到通知,取走订单 
Waiter-9,等待中
厨师-10,接到新订单,开始做菜...
厨师-10,通知waiter取食物
Waiter-9,收到通知,取走订单 
Waiter-9,等待中
厨师-10,接到新订单,开始做菜...
厨师-10,通知waiter取食物
Waiter-9,收到通知,取走订单 
Waiter-9,等待中
厨师-10,接到新订单,开始做菜...
厨师-10,通知waiter取食物
Waiter-9,收到通知,取走订单 
Waiter-9,等待中
没有食物了,结束!

 

3.2 join线程排队(应用场景:处理紧急任务)

线程A调用线程B对象的join()方法,会导致线程A的运行中断,直到线程B运行完成或超时,线程A才继续运行。

#Main函数

点击查看代码
package com.icss.join;

public class Test {
    public static void main(String[] args) {
        Sleeper sleeper = new Sleeper();
        Joiner joiner = new Joiner();
        sleeper.setJoiner(joiner);
        sleeper.start();
        joiner.start();
    }

}

#分别定义Sleeper和Joiner

#Sleeper

点击查看代码
package com.icss.join;

class Sleeper extends Thread {
    private Joiner joiner;

    public void setJoiner(Joiner joiner) {
        this.joiner = joiner;
    }

    public void run() {
        System.out.println("Sleeper线程id="
                + Thread.currentThread().getId() + " run...");
        try {
            for (int i = 0; i < 10; i++) {
                if (i == 5 && joiner != null) {
                    System.out.println("joiner加入,线程" +
                            Thread.currentThread().getId() + "被阻塞");
                    joiner.join();
                }
                Thread.sleep(100);
                System.out.println("线程"
                        + Thread.currentThread().getId() + "---i=" + i);
            }
        } catch (Exception e) {
        }
        System.out.println(Thread.currentThread().getId() + " end...");
    }
}

#Joiner

点击查看代码
package com.icss.join;

class Joiner extends Thread {
    public void run() {
        System.out.println("Joiner 线程id="
                + Thread.currentThread().getId() + " run...");
        try {
            for (int i = 0; i < 10; i++) {
                Thread.sleep(100);
                System.out.println("线程"
                        + Thread.currentThread().getId() + "---k=" + i);
            }
        } catch (Exception e) {
        }
        System.out.println(Thread.currentThread().getId() + "  end...");
    }
}

#运行结果

点击查看代码
Joiner 线程id=10 run...
Sleeper线程id=9 run...
线程9---i=0
线程10---k=0
线程10---k=1
线程9---i=1
线程10---k=2
线程9---i=2
线程9---i=3
线程10---k=3
线程9---i=4
joiner加入,线程9被阻塞
线程10---k=4
线程10---k=5
线程10---k=6
线程10---k=7
线程10---k=8
线程10---k=9
10  end...
线程9---i=5
线程9---i=6
线程9---i=7
线程9---i=8
线程9---i=9
9 end...

注:join(long millis)方法中传入的超时参数不能为负数,否则将会抛出非法参数异常。如果 millis 参数为0,则表示永远等待。

3.3 线程中断

点击查看代码
package com.icss.interrupt;

public class TestInterrupt {
    public static void main(String[] args) {
        Thread t = new Thread(new Runnable() {
            public void run() {
                for (int i = 0; i < 10; i++) {
                    if (Thread.currentThread().isInterrupted()) {
                        System.out.println("收到中断通知,结束线程...");
                        break;
                    } else {
                        System.out.println(Thread.currentThread().getId()
                                + ",i=" + i);
                        try {
                            Thread.sleep(100);
                        } catch (Exception e) {
                        }
                    }
                }
            }
        });
        t.start();
        try {
            Thread.sleep(500);
        } catch (Exception e) {

        }
        t.interrupt();
        System.out.println(t.getId() + "中断状态:" + t.isInterrupted());
    }

}

3.3.1 停止正在运行的线程

点击查看代码
package com.icss.interrupt;

public class StopTask implements Runnable {
    private boolean flag = true;

    public void run() {
        int i = 0;
        while (flag) {
            try {
                Thread.sleep(200);
            } catch (Exception e) {
            }
            i++;
            System.out.println(Thread.currentThread().getId() + "--i=" + i);
        }
    }

    public void stop() {
        flag = false;
        System.out.println(Thread.currentThread().getId() + "发出停止命令...");
    }

    public static void main(String[] args) {
        StopTask task = new StopTask();
        new Thread(task).start();
        try {
            Thread.sleep(5000);
            task.stop();
        } catch (Exception e) {
        }

    }
}

3.3.2 CountDownLatch计数器

CountDownLatch计数器是java.util.concurrent包中的线程工具类,用于允许一个或多个线程阻塞等待,直到其他线程工作完成才开始执行。

点击查看代码
/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

/*
 * This file is available under and governed by the GNU General Public
 * License version 2 only, as published by the Free Software Foundation.
 * However, the following notice accompanied the original version of this
 * file:
 *
 * Written by Doug Lea with assistance from members of JCP JSR-166
 * Expert Group and released to the public domain, as explained at
 * http://creativecommons.org/publicdomain/zero/1.0/
 */

package java.util.concurrent;
import java.util.concurrent.locks.AbstractQueuedSynchronizer;

/**
 * A synchronization aid that allows one or more threads to wait until
 * a set of operations being performed in other threads completes.
 *
 * <p>A {@code CountDownLatch} is initialized with a given <em>count</em>.
 * The {@link #await await} methods block until the current count reaches
 * zero due to invocations of the {@link #countDown} method, after which
 * all waiting threads are released and any subsequent invocations of
 * {@link #await await} return immediately.  This is a one-shot phenomenon
 * -- the count cannot be reset.  If you need a version that resets the
 * count, consider using a {@link CyclicBarrier}.
 *
 * <p>A {@code CountDownLatch} is a versatile synchronization tool
 * and can be used for a number of purposes.  A
 * {@code CountDownLatch} initialized with a count of one serves as a
 * simple on/off latch, or gate: all threads invoking {@link #await await}
 * wait at the gate until it is opened by a thread invoking {@link
 * #countDown}.  A {@code CountDownLatch} initialized to <em>N</em>
 * can be used to make one thread wait until <em>N</em> threads have
 * completed some action, or some action has been completed N times.
 *
 * <p>A useful property of a {@code CountDownLatch} is that it
 * doesn't require that threads calling {@code countDown} wait for
 * the count to reach zero before proceeding, it simply prevents any
 * thread from proceeding past an {@link #await await} until all
 * threads could pass.
 *
 * <p><b>Sample usage:</b> Here is a pair of classes in which a group
 * of worker threads use two countdown latches:
 * <ul>
 * <li>The first is a start signal that prevents any worker from proceeding
 * until the driver is ready for them to proceed;
 * <li>The second is a completion signal that allows the driver to wait
 * until all workers have completed.
 * </ul>
 *
 *  <pre> {@code
 * class Driver { // ...
 *   void main() throws InterruptedException {
 *     CountDownLatch startSignal = new CountDownLatch(1);
 *     CountDownLatch doneSignal = new CountDownLatch(N);
 *
 *     for (int i = 0; i < N; ++i) // create and start threads
 *       new Thread(new Worker(startSignal, doneSignal)).start();
 *
 *     doSomethingElse();            // don't let run yet
 *     startSignal.countDown();      // let all threads proceed
 *     doSomethingElse();
 *     doneSignal.await();           // wait for all to finish
 *   }
 * }
 *
 * class Worker implements Runnable {
 *   private final CountDownLatch startSignal;
 *   private final CountDownLatch doneSignal;
 *   Worker(CountDownLatch startSignal, CountDownLatch doneSignal) {
 *     this.startSignal = startSignal;
 *     this.doneSignal = doneSignal;
 *   }
 *   public void run() {
 *     try {
 *       startSignal.await();
 *       doWork();
 *       doneSignal.countDown();
 *     } catch (InterruptedException ex) {} // return;
 *   }
 *
 *   void doWork() { ... }
 * }}</pre>
 *
 * <p>Another typical usage would be to divide a problem into N parts,
 * describe each part with a Runnable that executes that portion and
 * counts down on the latch, and queue all the Runnables to an
 * Executor.  When all sub-parts are complete, the coordinating thread
 * will be able to pass through await. (When threads must repeatedly
 * count down in this way, instead use a {@link CyclicBarrier}.)
 *
 *  <pre> {@code
 * class Driver2 { // ...
 *   void main() throws InterruptedException {
 *     CountDownLatch doneSignal = new CountDownLatch(N);
 *     Executor e = ...
 *
 *     for (int i = 0; i < N; ++i) // create and start threads
 *       e.execute(new WorkerRunnable(doneSignal, i));
 *
 *     doneSignal.await();           // wait for all to finish
 *   }
 * }
 *
 * class WorkerRunnable implements Runnable {
 *   private final CountDownLatch doneSignal;
 *   private final int i;
 *   WorkerRunnable(CountDownLatch doneSignal, int i) {
 *     this.doneSignal = doneSignal;
 *     this.i = i;
 *   }
 *   public void run() {
 *     try {
 *       doWork(i);
 *       doneSignal.countDown();
 *     } catch (InterruptedException ex) {} // return;
 *   }
 *
 *   void doWork() { ... }
 * }}</pre>
 *
 * <p>Memory consistency effects: Until the count reaches
 * zero, actions in a thread prior to calling
 * {@code countDown()}
 * <a href="package-summary.html#MemoryVisibility"><i>happen-before</i></a>
 * actions following a successful return from a corresponding
 * {@code await()} in another thread.
 *
 * @since 1.5
 * @author Doug Lea
 */
public class CountDownLatch {
    /**
     * Synchronization control For CountDownLatch.
     * Uses AQS state to represent count.
     */
    private static final class Sync extends AbstractQueuedSynchronizer {
        private static final long serialVersionUID = 4982264981922014374L;

        Sync(int count) {
            setState(count);
        }

        int getCount() {
            return getState();
        }

        protected int tryAcquireShared(int acquires) {
            return (getState() == 0) ? 1 : -1;
        }

        protected boolean tryReleaseShared(int releases) {
            // Decrement count; signal when transition to zero
            for (;;) {
                int c = getState();
                if (c == 0)
                    return false;
                int nextc = c-1;
                if (compareAndSetState(c, nextc))
                    return nextc == 0;
            }
        }
    }

    private final Sync sync;

    /**
     * Constructs a {@code CountDownLatch} initialized with the given count.
     *
     * @param count the number of times {@link #countDown} must be invoked
     *        before threads can pass through {@link #await}
     * @throws IllegalArgumentException if {@code count} is negative
     */
    public CountDownLatch(int count) {
        if (count < 0) throw new IllegalArgumentException("count < 0");
        this.sync = new Sync(count);
    }

    /**
     * Causes the current thread to wait until the latch has counted down to
     * zero, unless the thread is {@linkplain Thread#interrupt interrupted}.
     *
     * <p>If the current count is zero then this method returns immediately.
     *
     * <p>If the current count is greater than zero then the current
     * thread becomes disabled for thread scheduling purposes and lies
     * dormant until one of two things happen:
     * <ul>
     * <li>The count reaches zero due to invocations of the
     * {@link #countDown} method; or
     * <li>Some other thread {@linkplain Thread#interrupt interrupts}
     * the current thread.
     * </ul>
     *
     * <p>If the current thread:
     * <ul>
     * <li>has its interrupted status set on entry to this method; or
     * <li>is {@linkplain Thread#interrupt interrupted} while waiting,
     * </ul>
     * then {@link InterruptedException} is thrown and the current thread's
     * interrupted status is cleared.
     *
     * @throws InterruptedException if the current thread is interrupted
     *         while waiting
     */
    public void await() throws InterruptedException {
        sync.acquireSharedInterruptibly(1);
    }

    /**
     * Causes the current thread to wait until the latch has counted down to
     * zero, unless the thread is {@linkplain Thread#interrupt interrupted},
     * or the specified waiting time elapses.
     *
     * <p>If the current count is zero then this method returns immediately
     * with the value {@code true}.
     *
     * <p>If the current count is greater than zero then the current
     * thread becomes disabled for thread scheduling purposes and lies
     * dormant until one of three things happen:
     * <ul>
     * <li>The count reaches zero due to invocations of the
     * {@link #countDown} method; or
     * <li>Some other thread {@linkplain Thread#interrupt interrupts}
     * the current thread; or
     * <li>The specified waiting time elapses.
     * </ul>
     *
     * <p>If the count reaches zero then the method returns with the
     * value {@code true}.
     *
     * <p>If the current thread:
     * <ul>
     * <li>has its interrupted status set on entry to this method; or
     * <li>is {@linkplain Thread#interrupt interrupted} while waiting,
     * </ul>
     * then {@link InterruptedException} is thrown and the current thread's
     * interrupted status is cleared.
     *
     * <p>If the specified waiting time elapses then the value {@code false}
     * is returned.  If the time is less than or equal to zero, the method
     * will not wait at all.
     *
     * @param timeout the maximum time to wait
     * @param unit the time unit of the {@code timeout} argument
     * @return {@code true} if the count reached zero and {@code false}
     *         if the waiting time elapsed before the count reached zero
     * @throws InterruptedException if the current thread is interrupted
     *         while waiting
     */
    public boolean await(long timeout, TimeUnit unit)
        throws InterruptedException {
        return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
    }

    /**
     * Decrements the count of the latch, releasing all waiting threads if
     * the count reaches zero.
     *
     * <p>If the current count is greater than zero then it is decremented.
     * If the new count is zero then all waiting threads are re-enabled for
     * thread scheduling purposes.
     *
     * <p>If the current count equals zero then nothing happens.
     */
    public void countDown() {
        sync.releaseShared(1);
    }

    /**
     * Returns the current count.
     *
     * <p>This method is typically used for debugging and testing purposes.
     *
     * @return the current count
     */
    public long getCount() {
        return sync.getCount();
    }

    /**
     * Returns a string identifying this latch, as well as its state.
     * The state, in brackets, includes the String {@code "Count ="}
     * followed by the current count.
     *
     * @return a string identifying this latch, as well as its state
     */
    public String toString() {
        return super.toString() + "[Count = " + sync.getCount() + "]";
    }
}

CountDownLatch类的构造函数,需要输入一个等待任务数量。每完成一项任务,就调用一次countDown()方法。某个任务A启动后,调用await()方法,进入阻塞等待状态。当代用getCount()方法,发现等待任务数量为0是,任务A开始执行最后的任务。

应用场景:汽车厂商生产一辆汽车需要多种配件组装,包括车身、发动机等。只有前面的各个环节都完成之后才能进入总装环节。

#Main函数

点击查看代码
package com.icss.countdown;

import java.util.concurrent.CountDownLatch;

public class Test {
    public static void main(String[] args) {
        CountDownLatch cl = new CountDownLatch(10);

        WaitingTask w = new WaitingTask(cl);
        new Thread(w).start();      //汽车总装任务	

        for (int i = 0; i < 10; i++) {
            WorkingTask work = new WorkingTask(cl);
            new Thread(work).start();
        }
    }

}

#WaitingTask

点击查看代码
package com.icss.countdown;

import java.util.concurrent.CountDownLatch;

public class WaitingTask implements Runnable {
    private CountDownLatch cl;

    public WaitingTask(CountDownLatch cl) {
        this.cl = cl;
    }

    public void run() {
        try {
            System.out.println(Thread.currentThread().getId()
                    + "阻塞等待其他任务完成....");
            cl.await();
            Thread.sleep(1000);
            System.out.println(Thread.currentThread().getId()
                    + "汽车总装完成,汽车出厂....");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

#WorkingTask

点击查看代码
package com.icss.countdown;

import java.util.concurrent.CountDownLatch;

public class WorkingTask implements Runnable {
    private CountDownLatch cl;

    public WorkingTask(CountDownLatch cl) {
        this.cl = cl;
    }

    public void run() {
        try {
            int r = (int) (Math.random() * 10);
            Thread.sleep(r * 1000);
            System.out.println(Thread.currentThread().getId() + "任务完成");
            cl.countDown();                 //任务完成后,计数器减一
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

4 线程池与锁

4.1 ReentrantLock与synchronized

 

posted @ 2021-10-26 21:38  BlogMemory  阅读(107)  评论(0编辑  收藏  举报