线程小总结_2012

来源:互联网 发布:全球产品复制软件 编辑:程序博客网 时间:2024/04/30 03:13

线程小总结

内容来自于网络通过自己整理而成。


多线程
sleep是Thread类的静态方法。sleep的作用是让线程休眠制定的时间,在时间到达时恢复,
也就是说sleep将在接到时间到达事件事恢复线程执行


wait是Object的方法,也就是说可以对任意一个对象调用wait方法,调用wait方法将会将调用者的线程挂起,
直到其他线程调用同一个对象的notify方法才会重新激活调用者


sleep并不释放锁
wait 释放锁?
但是wait()和sleep()都可以通过interrupt()方法打断线程的暂停状态,从而使线程立刻抛出InterruptedException。
如果线程A希望立即结束线程B,则可以对线程B对应的Thread实例调用interrupt方法。如果此刻线程B正在wait/sleep/join,
则线程B会立刻抛出InterruptedException,在catch() {} 中直接return即可安全地结束线程。
需要注意的是,InterruptedException是线程自己从内部抛出的,并不是interrupt()方法抛出的。
对某一线程调用interrupt()时,如果该线程正在执行普通的代码,那么该线程根本就不会抛出InterruptedException。
但是,一旦该线程进入到wait()/sleep()/join()后,就会立刻抛出InterruptedException。


什么是守护线程?
Java有两种Thread:"守护线程Daemon"与"用户线程User"。
Java垃圾回收线程就是一个典型的守护线程。
好像守护线程在主线程结束时,自动退出,所以你不用去管这个线程的结束问题, 
就好像背景音乐一样.


http://www.cnblogs.com/ChrisWang/archive/2009/11/28/1612815.html

关于Java的Daemon线程的理解

生产者-消费者例子:

public class CubbyHole {private int seq;private boolean available = false;public synchronized int get() {while(available == false){try{wait();}catch (InterruptedException e) {e.printStackTrace();}}available = false;notify();return seq;}public synchronized void put(int value) {while(available == true){try{wait();}catch (InterruptedException e) {e.printStackTrace();}}seq = value;available = true;notify();}}




生产者类:
public class Producer extends Thread {private CubbyHole cubbyHole;private int number;public Producer(CubbyHole c, int number){cubbyHole = c;this.number = number;}public void run(){for(int i=0; i<10; i++){cubbyHole.put(i);System.out.println("Producer #" + this.number + " put: " +i);try{sleep((int)(Math.random() * 1000));}catch (InterruptedException e) {e.printStackTrace();}}}}




消费者类,同生产者类……


测试类
public class ThreadPCDemo {public static void main(String[] args){CubbyHole c = new CubbyHole();Producer p1 = new Producer(c, 1);Consumer c1 = new Consumer(c, 1);p1.start();c1.start();}}

实现4个线程,其中2个每次加1,另外2个每次减1

/** * 写四个线程 两个线程加1 两个线程减1 * 操作同一个变量 内部类 * @author Administrator * */public class IncDecThread {private int j = 0;boolean flag = false;public synchronized void inc(){while(flag){try{wait();}catch(InterruptedException e){e.printStackTrace();}}j++;System.out.println(Thread.currentThread().getName() + " inc: " + j);flag = true;notifyAll();}public synchronized void dec(){while(!flag){try{wait();}catch(InterruptedException e){e.printStackTrace();}}j--;System.out.println(Thread.currentThread().getName() + " dec: " + j);flag = false;notifyAll();}class T1 implements Runnable{public void run(){while( j < 50){try{inc();Thread.sleep((long) Math.random() * 100);}catch(InterruptedException e){e.printStackTrace();}}}}class T2 implements Runnable{public void run(){while(j > 0){try{dec();Thread.sleep((long) Math.random() * 100);}catch(InterruptedException e){e.printStackTrace();}}}}public static void main(String[] args){IncDecThread idt = new IncDecThread();T1 t1 = idt.new T1();T2 t2 = idt.new T2();Thread t_add1 = new Thread(t1, "加线程1");Thread t_add2 = new Thread(t1, "加线程2");Thread t_sub1 = new Thread(t2, "加线程3");Thread t_sub2 = new Thread(t2, "加线程4");t_add1.start();t_add2.start();t_sub1.start();t_sub2.start();}}


------------------------------------------------------------------------
2012-9-13
http://blog.csdn.net/bzwm/article/details/3881392
浅析 Java Thread.join()
在网上看到有人说“将两个线程合并”。这样解释我觉得理解起来还更麻烦。不如就借鉴下API里的说法:
"等待该线程终止。"
解释一下,是主线程等待子线程的终止。也就是在子线程调用了join()方法后面的代码,
只有等到子线程结束了才能执行。(Waits for this thread to die.)


Thread.yield()
是对线程调度器(Java线程机制的一部分,可以将CPU从一个线程转移给另一个线程)的一种建议,它在声明:

我已经执行完生命周期中最重要的部分了,此刻正是切换给其他任务执行一段时间的大好时机。


volatile

弱同步,可以被看作是一种 “程度较轻的 synchronized”;与 synchronized 块相比,volatile 变量所需的编码较少,并且运行时开销也较少,但是它所能实现的功能也仅是 synchronized 的一部分。

Volatile 变量具有 synchronized 的可见性特性,但是不具备原子特性。这就是说线程能够自动发现 volatile 变量的最新值。

开销小,性能优势。



使用Executor
ExecutorService exec = Executors.newCachedThreadPool();ExecutorService exec = Executors.newFixedThreadPool(5);  //有限线程集for(int i=0; i<5; i++){exec.execute(new LiftOff());exec.shutdown();     //关闭}




*注
public class liftOff implements Runnable{...}


notifyAll()
并非将唤醒"所有正在等待的任务"
事实上,当notifyAll()因某个特定的锁而被调用时,只有等待这个锁的任务才会被唤醒。




Daemon()
后台线程 也译作守护线程
所谓后台(daemon)线程,是指在程序运行的时候在后台提供一种通用服务的线程,并且这种线程并不属于程序
中不可或缺的部分。因此,当所有的非后台线程结束时,程序也就终止了,同时会杀死进程中的所有后台线程。
反过来说,只要有任何非后台线程还在运行,程序就不会终止。
方法:
setDaemon(true)
isDaemon()  //是否是守护线程?


ReentrantLock()
显式锁
private int number =0;private Lock lock = new ReentrantLock()publlic int next(){lock.lock();try{number++;return number;}catch(Exception e){...}finally{lock.unlock();}}




原子
volatile
原子类
AtomicInteger AtomicLong AtomicReference
对于常规编程来说很少派上用场,但涉及性能调优时就大有用武之地了。


生产者-消费者队列(同步队列)
wait()和notifyAll()方法以一种非常低级的方式解决了任务互操作问题,即每次交互时都握手。
在许多情况下,你可以瞄向更高的抽象级别,使用同步队列来解决任务协作问题,同步队列在任何
时刻都只允许一个任务插入或移除元素。在java.util.concurrent.BlockingQueue接口中提供了这个队列
这个接口有大量的标准实现。你通常可以使用LinkedBlockingQueue,他是一个无界队列,还可以使用ArrayBlockingQueue,
它具有固定的尺寸,因此你可以在它被阻塞之前,向其中放置有限数量的元素。
如果消费者任务试图从队列中获取对象,而该队列此时为空,那么这些队列还可以挂起消费者任务,并且当有更多的元素
可用时恢复消费者任务。阻塞队列可以解决非常大量的问题,而其方式与wait() notifyAll()相比,则简单并可靠的多.
优点:
没有任何显式的同步(Synchronized or lock)因为同步由队列(其内部是同步的)和系统的设计隐式的管理了。每片Toast
在任何时刻都只由一个任务在操作。因为队列的阻塞使得处理过程将被自动的挂起和恢复。你可以看到由BlockingQueue
产生的简化十分明显。在使用显式的wait() notifyAll()时存在的类和类之间的耦合被消除了。因为每个类都只和它的
BlockingQueue通信。


死锁的四个条件:
当以下四个条件同时满足时,就会发生死锁。
1) 互斥条件
任务的资源中至少有一个是不能共享的。这里,一根Chopstick一次就只能被一个Philosopher使用。
2) 
至少有一个任务它必须持有一个资源且正在等待获取一个当前被别的任务持有的资源。
也就是说,要发生死锁,Philosopher必须拿着一根Chopstick并且等待另一根.
3)资源不能被任务抢占
任务必须把资源释放当成普通事件。Philosopher很有礼貌,他们不会从其他Philosopher那里抢Chopstick。
4)必须有循环等待
这时,一个任务等待其他任务持有的资源,后者又在等待另外一个任务所持有的资源,这样一直下去,
直到有一个任务在等待第一个任务所持有的资源,使得大家都被锁住。
因为要发生死锁的话,所有这些条件必须全部满足;所以要防止死锁的话,只需要破坏其中一个即可。在程序中,
防止死锁最容易的方法是破坏第四个条件。

(哲学家就餐问题中,最后一个Philosopher被初始化成先拿左边的Chopstick后拿右边的Chopstic,那么这个

哲学家将永远不会阻止其右边的Philosopher拿起他们的Chopstick,就可以防止循环等待。

*前提是其他的Philosopher 先拿右边的Chopstick)


调优
很明显使用Lock通常会比使用synchronized要高效许多,而且synchronized的开销看起来变化范围太大,而Lock相对比较一致。

Atomic类对象只有在非常简单的情况下才有用。

============================================================================

ThreadLocal
ThreadLocal并不是一个Thread,而是Thread的局部变量,也许把它命名为ThreadLocalVariable更容易让人理解一些。
当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以
独立地改变自己的副本,而不会影响其它线程所对应的副本。
API:
1 void set(Object value)
设置当前线程的线程局部变量的值.


2 public Object get()
该方法返回当前线程所对应的线程局部变量.


3 public void remove()
将当前线程局部变量的值删除,目的是为了减少内存的占用,该方法是JDK 5.0新增的方法.


4 protected Object initialValue()
返回该线程局部变量的初始值,该方法是一个protected的方法,显然是为了让子类覆盖而设计的.
【实现原理】
ThreadLocal是如何做到为每一个线程维护变量的副本的呢?其实实现的思路很简单:在ThreadLocal类中有一个Map,
用于存储每一个线程的变量副本,Map中元素的键为线程对象,而值对应线程的变量副本。
【应用场景】
ThreadLocal的应用场合,我觉得最适合的是按线程多实例(每个线程对应一个实例)的对象的访问,
并且这个对象很多地方都要用到。 
多个线程共享一个对象,但互不干扰!


例子:计数器的例子

public class MyThread extends Thread {private SequenceNumber sn;public MyThread(SequenceNumber sn) {this.sn = sn;}public void run(){//每个线程打出3个序列值for (int i = 0; i < 3; i++) {System.out.println("thread[" + Thread.currentThread().getName()+ "] sn[" + sn.getNextNum() + "]");}}}




public class SequenceNumber {//通过匿名内部类覆盖ThreadLocal的initialValue()方法,指定初始值private static ThreadLocal<Integer> seqNum = new ThreadLocal<Integer>() {public Integer initialValue() {return 0;}};//获取下一个序列值public int getNextNum() {seqNum.set(seqNum.get() + 1);return seqNum.get();}public static void main(String[] args) {SequenceNumber sn = new SequenceNumber();MyThread t1 = new MyThread(sn);MyThread t2 = new MyThread(sn);MyThread t3 = new MyThread(sn);t1.start();t2.start();t3.start();}}


输出结果:
thread[Thread-0] sn[1]
thread[Thread-0] sn[2]
thread[Thread-0] sn[3]
thread[Thread-1] sn[1]
thread[Thread-1] sn[2]
thread[Thread-1] sn[3]
thread[Thread-2] sn[1]
thread[Thread-2] sn[2]
thread[Thread-2] sn[3]


观察输出的结果信息,我们发现每个线程所产生的序号虽然都共享同一个SequenceNumber实例,但它们并没有发生相互干扰的情况,
而是各自产生独立的序列号,这是因为我们通过ThreadLocal为每一个线程提供了单独的副本。


【小结】
小结
1
ThreadLocal 不是用来解决共享对象的多线程访问问题的,一般情况下,通过ThreadLocal.set() 到线程中的对象是该线程自己
使用的对象,其他线程是不需要访问的,也访问不到的。
2
ThreadLocal是解决线程安全问题一个很好的思路,它通过为每个线程提供一个独立的变量副本解决了变量并发访问的冲突问题。
在很多情况下,ThreadLocal比直接使用synchronized同步机制解决线程安全问题更简单,更方便,且结果程序拥有更高的并发性。
3
ThreadLocal和Synchonized都用于解决多线程并发访问。但是ThreadLocal与synchronized有本质的区别。
synchronized是利用锁的机制,同步实现数据共享
Thread相反,为每一个线程都提供了变量的副本,隔离对象实现数据的独享,线程之间互不干扰。


参考:
理解ThreadLocal
http://blog.csdn.net/qjyong/article/details/2158097

=============================================================================

多线程 卖票的例子

/** * 程序中有三个线程,但是一共卖了10张票,也就是说使用Runnable实现多线程可以达到资源共享目的。 * @author Administrator * 2013-10-26 */public class SaleTicketRunnable implements Runnable {private int ticket = 10;@Overridepublic void run() {// TODO Auto-generated method stubwhile(ticket >0){System.out.println ("-->" + " Sale ticket: " + ticket--);}}public static void main(String[] args){SaleTicketRunnable str = new SaleTicketRunnable();new Thread(str).start();new Thread(str).start();new Thread(str).start();}}


=============================================================================

一道经典的线程面试题目:

最多能同时运行5个线程,第6个线程只能等待前边的线程执行后才能运行;且后面的线程按优先顺序排队运行。
请写出设计思路及代码

第一种解决方案,自己发明轮子:不使用jdk中已有的api。自己写实现。原生态

排队队列

/** * 同步队列 */import java.util.concurrent.LinkedBlockingQueue;public class RocketQueue extends LinkedBlockingQueue<Rocket> {}
任务类,火箭发射。倒数10个数,发射。

/** *  * 2012-12-27 * */public class Rocket {protected int countDown =10;private static int takeCount =1;private final int id = takeCount++;private boolean enable = false;private int count = 0;public String status(){return "#" + id + "("+(countDown>0 ? countDown : "LiftOff!")+").";}//发射过程中public synchronized void lift(int size, RocketQueue queue) throws InterruptedException{count++;//前几个线程直接放行if(count < size && id < size)enable = true;        //后几个线程放入BlockingQueue排队while(id > size && enable == false){System.out.println("#"+id+"[线程数已满, 等待...]");//放入队列中等候if(!queue.contains(this)){queue.put(this);System.out.println("#"+id+"[已被放入等待队列中.]");}wait();}//发射倒计时while(countDown-- > 0 && enable){System.out.println(status());Thread.sleep((int) Math.random() * 2000);Thread.currentThread().yield();}//执行完毕,线程数减一count--;System.out.println("线程#"+ this.id +" 完成任务!");notifyAll();//如果当前线程数目小于额定数,则从队列中取出线程执行if(count < size && queue.size() >0){Rocket rocket = queue.take();  //从队头取出一个待执行的对象rocket.enable = true;System.out.println("#"+rocket.id+"[已从队列中取出.]");rocket.lift(size, queue);}}}

线程体类,经测试运行正常

import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;public class LiftOff implements Runnable{private Rocket rocket;private RocketQueue queue;private final int limits =5;public LiftOff(Rocket rocket, RocketQueue queue){this.rocket = rocket;this.queue = queue;}public void run(){try{rocket.lift(limits, queue);}catch (InterruptedException e) {// TODO: handle exceptionThread.currentThread().interrupt();e.printStackTrace();}}public static void main(String[] args) {ExecutorService exec = Executors.newFixedThreadPool(10);RocketQueue queue = new RocketQueue();for(int i=1; i<=10; i++){Rocket rocket = new Rocket();exec.execute(new LiftOff(rocket,queue));}exec.shutdown();}}


第二种方案,使用现有的轮子->使用jdk现有api-->>>ThreadPoolExecutor & PriorityBlockingQueue

ThreadPoolExecutor的workQueue参数设置为PriorityBlockingQueue即可解决此问题


按优先级执行的线程队列
ThreadPoolExecutor的workQueue参数设置为PriorityBlockingQueue


ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) 
corePoolSize - 池中所保存的线程数,包括空闲线程。
maximumPoolSize - 池中允许的最大线程数。
keepAliveTime - 当线程数大于核心时,此为终止前多余的空闲线程等待新任务的最长时间。
unit - keepAliveTime 参数的时间单位。
workQueue - 执行前用于保持任务的队列。此队列仅保持由 execute 方法提交的 Runnable 任务。


代码:
import java.util.Date;import java.util.concurrent.PriorityBlockingQueue;import java.util.concurrent.ThreadPoolExecutor;import java.util.concurrent.TimeUnit;public class TestPriorityTask {    private static PriorityBlockingQueue<Runnable> workQueue = new PriorityBlockingQueue<Runnable>();    private static ThreadPoolExecutor pool = new ThreadPoolExecutor(5, 5, 0L, TimeUnit.MILLISECONDS, workQueue);    public static void main(String[] args) throws Exception {        for(int i=0; i<20; i++) {            pool.execute(new Task(i));        }        pool.awaitTermination(0, TimeUnit.MILLISECONDS);        pool.shutdown();    }}


---------------------------------------------------
class Task implements Runnable,Comparable<Task> {    private int priority;//数值越大,优先级越大    public Task(int priority) {        this.priority = priority;    }    public void run() {        System.out.println("当前任务优先级:" + priority + ",执行线程:" + Thread.currentThread() + ",执行此句的时间:" + new Date());        try {            TimeUnit.SECONDS.sleep(3);        } catch (Exception e){}    }    public int compareTo(Task o) {        return o.priority - priority;    }}


面试常见问题;

1 如何避免线程死锁? 

最好根据过往的项目举出实例

死锁的4个条件中部分解答了这个问题。

另外:哲学家进餐问题深入研究.pdf中的解决

1 破坏请求和保持条件

2 破坏“不剥夺”条件

3 破坏环路等待条件


综合 Java编程思想 第四版 以及 resouce/Java多线程 目录下的哲学家进餐问题深入研究.pdf


2 线程的调优?

如果多线程出现效率问题,检查思路:

1>你是否充分调用了sleep()  yield()  wait()?

2>调用sleep()的时间是否够长?

3>是否执行了过多线程?

4>是否试过其他不同的平台或JVM?

多线程的调试非常难调,这正是造成多线程编程被认为是“艺术”的原因之一。 

from---《thinking in Java》


3 线程的测试?

线程测试内容 见我的另一篇blog 

Java Unit 多线程 测试

http://blog.csdn.net/bruce_sky/article/details/8021511


4 例子学习

哲学家就餐

http://94here.blog.51cto.com/696155/446028

简单实用的例子

http://chen498402552-163-com.iteye.com/blog/1309905

来自iteye的例子


【待补充内容】====

1 线程执行返回值

think in java

如果你希望任务完成时能够返回一个值,那么可以实现Callable而不是Runnable接口

从call()而不是run()返回值并且必须使用ExecutorService.submit()调用它,例子:

/** * 从任务中返回值 */import java.util.concurrent.Callable;public class TaskWithResult implements Callable<String> {private int id;public TaskWithResult(int id){this.id = id;}//call()而不是run()public String call(){return "result====>" + id;}}

import java.util.ArrayList;import java.util.concurrent.ExecutionException;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.util.concurrent.Future;public class CallableDemo {public static void main(String[] args) {ExecutorService exec = Executors.newCachedThreadPool();ArrayList<Future<String>> results = new ArrayList<Future<String>>();for(int i=0; i<5; i++){results.add(exec.submit(new TaskWithResult(i)));}for(Future<String> fs:results)try{//判断任务是否完成if(fs.isDone()){//get()获取结果System.out.println(fs.get());}}catch (InterruptedException e) {// TODO: handle exceptione.printStackTrace();return;}catch (ExecutionException e) {// TODO: handle exceptione.printStackTrace();}finally{exec.shutdown();}}}

submit()方法会产生Future对象,你可以用isDone()的方法来判断Future是否完成,get()方法来获取结果

2 线程执行信息

L 上传下载中ftp

StringBuffer cmd = new StringBuffer("ftp \"-s:").append(rootFold).append(VEC_TRANSFER_SCRIPT[transType]).append("\"");System.err.println("DEBUG: " + cmd + "|" + workFolder.getAbsolutePath());//执行ftp脚本Process proc = Runtime.getRuntime().exec(cmd.toString(), null, workFolder);java.io.Writer out = new PrintWriter(System.out);WatchThread stdT = new WatchThread(out, proc.getInputStream(), "STD");WatchThread errT = new WatchThread(out, proc.getErrorStream(), "ERR");stdT.start();errT.start();proc.waitFor();stdT.join();errT.join();if (!errT.hasData()){out.write("传送完成!");boolean flag1=this.writeHistoryLog(historyPath, ftpPara1_0, transferNums);if(!flag1){throw new Exception("写文件 "+historyPath+"/"+ftpPara1_0+".log 失败!");}}else{out.write("传送失败!");throw new Exception("Ftp传送失败。");}……//监视命令行执行的结果class WatchThread extends Thread       {Writer m_dest;InputStreamReader m_src;boolean m_hasData = false;String m_label;public WatchThread(Writer dest, InputStream src, String label){m_dest = dest;m_src = new InputStreamReader(src);m_label = label;}public void run(){char[] vBuff = new char[256];int nCount = 0;try{while(0 <= (nCount = m_src.read(vBuff, 0, vBuff.length))){m_hasData = true;m_dest.write(vBuff, 0, nCount);System.err.print(m_label + ":" + new String(vBuff, 0, nCount));m_dest.flush();}}catch(IOException e){e.printStackTrace(System.err);}}public boolean hasData(){return m_hasData;}      }