一、java中的线程是通过Thread类创建的,
1 //下面是构造函数,一个共同的特点就是:都是调用init()进行创建的
2 publicThread() {3 init(null, null, "Thread-" + nextThreadNum(), 0);4 }5
6 publicThread(Runnable target) {7 init(null, target, "Thread-" + nextThreadNum(), 0);8 }9
10 Thread(Runnable target, AccessControlContext acc) {11 init(null, target, "Thread-" + nextThreadNum(), 0, acc);12 }13
14 publicThread(ThreadGroup group, Runnable target) {15 init(group, target, "Thread-" + nextThreadNum(), 0);16 }17
18 publicThread(String name) {19 init(null, null, name, 0);20 }21
22 publicThread(ThreadGroup group, String name) {23 init(group, null, name, 0);24 }25
26 publicThread(Runnable target, String name) {27 init(null, target, name, 0);28 }29
30 publicThread(ThreadGroup group, Runnable target, String name) {31 init(group, target, name, 0);32 }33
34 publicThread(ThreadGroup group, Runnable target, String name,35 longstackSize) {36 init(group, target, name, stackSize);37 }38
39 //init()方法有两个
40 private voidinit(ThreadGroup g, Runnable target, String name,41 longstackSize) {42 init(g, target, name, stackSize, null);43 }44 /*g:用于将线程分组管理45 *target:用于指定线程将要执行的任务46 *name:线程的名字47 *stackSize:48 *acc:49 */
50 private voidinit(ThreadGroup g, Runnable target, String name,51 longstackSize, AccessControlContext acc) {52 //线程必须有一个名字,默认情况下是Thread-x,x是从0开始的int型数
53 if (name == null) {54 throw new NullPointerException("name cannot be null");55 }56
57 this.name =name.toCharArray();58
59 Thread parent =currentThread();60 SecurityManager security =System.getSecurityManager();61 if (g == null) {62 /*Determine if it's an applet or not*/
63
64 /*If there is a security manager, ask the security manager65 what to do.*/
66 if (security != null) {67 g =security.getThreadGroup();68 }69
70 /*If the security doesn't have a strong opinion of the matter71 use the parent thread group.*/
72 if (g == null) {73 g =parent.getThreadGroup();74 }75 }
Thread源码
从构造函数可以看出,创建一个有意义的线程(有可执行的任务),就是向其中传递一个实现Runnable的对象即可,但是也可以继承Thread类(因为该类已经实现了前一条),然后重写run()即可。后一种方法不推荐,因为这个类最重要的是提供需要执行的方法即可。
上图显示了线程状态转换的条件,这些方法的源码见下:
1 public synchronized voidstart() {2 /**
3 * 0 状态代表 "NEW".4 */
5 if (threadStatus != 0)6 throw newIllegalThreadStateException();7
8 group.add(this);9
10 boolean started = false;11 try{12 start0();13 started = true;14 } finally{15 try{16 if (!started) {17 group.threadStartFailed(this);18 }19 } catch(Throwable ignore) {20 /*do nothing. If start0 threw a Throwable then21 it will be passed up the call stack*/
22 }23 }24 }25
26 private native voidstart0();27
28 //执行target的run(),在start之后自动执行,
29 public voidrun() {30 if (target != null) {31 target.run();32 }33 }34
35 //中断线程
36 public voidinterrupt() {37 //判断是否是当前正在执行的线程,检查权限
38 if (this !=Thread.currentThread())39 checkAccess();40
41 synchronized(blockerLock) {42 Interruptible b =blocker;43 if (b != null) {44 interrupt0(); //Just to set the interrupt flag
45 b.interrupt(this);46 return;47 }48 }49 interrupt0();50 }51
52 //从源码可以看到,该方法调用wait(),使alive状态(start之后,die之前)线程进入等待状态
53 public final synchronized void join(longmillis)54 throwsInterruptedException {55 long base =System.currentTimeMillis();56 long now = 0;57
58 if (millis < 0) {59 throw new IllegalArgumentException("timeout value is negative");60 }61
62 if (millis == 0) {63 while(isAlive()) {64 wait(0);65 }66 } else{67 while(isAlive()) {68 long delay = millis -now;69 if (delay <= 0) {70 break;71 }72 wait(delay);73 now = System.currentTimeMillis() -base;74 }75 }76 }77
78 public final synchronized void join(long millis, intnanos)79 throwsInterruptedException {80
81 if (millis < 0) {82 throw new IllegalArgumentException("timeout value is negative");83 }84
85 if (nanos < 0 || nanos > 999999) {86 throw newIllegalArgumentException(87 "nanosecond timeout value out of range");88 }89
90 if (nanos >= 500000 || (nanos != 0 && millis == 0)) {91 millis++;92 }93
94 join(millis);95 }96
97 public final void join() throwsInterruptedException {98 join(0);99 }100
101 //不能指定时间的休眠
102 public static native voidyield();103
104 //让线程休眠指定时间
105 public static native void sleep(long millis) throwsInterruptedException;106
107 public static void sleep(long millis, intnanos)108 throwsInterruptedException {109 if (millis < 0) {110 throw new IllegalArgumentException("timeout value is negative");111 }112
113 if (nanos < 0 || nanos > 999999) {114 throw newIllegalArgumentException(115 "nanosecond timeout value out of range");116 }117
118 if (nanos >= 500000 || (nanos != 0 && millis == 0)) {119 millis++;120 }121
122 sleep(millis);123 }124
125
126 //java线程的几种状态判断127 //start之后die之前都是alive状态
128 public final native booleanisAlive();129
130 Thread源码
Thread源码
需要注意的是start()是启动一个线程,而run只是执行一个普通方法。
一个线程要执行需要满足两个条件,一是获取CPU,二是获取锁(在有同步情况下),这就是阻塞产生的原因。由前可知阻塞有两种情况,一种是阻塞自身(当前)线程(等待阻塞),另一种就是阻塞其他线程(同步阻塞)。上面有些方法类似,但是就是有这些细小的区别。例如:
①sleep:java中该方法是静态方法(所以一般应该有Thread而非实例进行调用),调用该方法会当前线程使释放CPU,但是不释放锁。下面的例子中在main()中使用t1.sleep()不能使t1休眠,因为t1只是一个普通的Thread对象,而不是线程,其在主线程中使用,所以是主线程休眠,所以出现以下效果。另外注意sleep是静态方法,最好使用类名即Thread调用。
1 public static void main(String[] args) throwsInterruptedException {2 Thread t1=new Thread(newRunnable(){3 @Override4 public voidrun() {5 for(int i=0;i<10;i++){6 System.out.println("i="+i);7 }8 }9 });10 Thread t2=new Thread(newRunnable(){11 @Override12 public voidrun() {13 for(int j=0;j<10;j++){14 System.out.println("j="+j);15 }16 }17 });18 t1.start();19 //t1.sleep(5000);//和下面效果一样,但是最好使用下面的方式
20 Thread.sleep(5000);21 System.out.println("a");22 t2.start();23 }
等待阻塞
②wait:当前线程休眠,释放CPU,同时释放锁,wait是Object对象的方法,必须在同步块中使用,并且由notify或者notifyAll进行唤醒。那么需要使用那个对象的wait()呢,这个问题其实和在那个对象上同步是一样的问题,其实就是在多个线程需要使用的那个对象,在该对象上加锁,并且调用这个对象的wait()。
二、多线程:上面是线程的一些基本情况,但是通常都是有多个线程一起使用的。线程之间共享同一片内存区域,所以当多个线程访问同一个数据时就可能出错,为了获得最佳速度,java允许线程保存共享成员变量的私有拷贝,而且只当线程进入或者离开同步代码块时才与共享成员变量的原始值对比。这就引起了多线程的一个问题:可见性。另一个问题就是对于一段代码,只允许一个线程操作,即互斥性。
三、Java中的锁机制:
1、同步锁:
Java中最早解决多线程访问的锁机制就是同步锁,即通过synchronized关键字,并使用的是一个对象(本质上是对象的内置锁)来对一段代码进行锁定,直到当前代码执行完成才释放锁。而其他线程必须等到释放锁之后才能重新获取执行。
注意:①一个对象只有一个锁,所以使用这个对象作为锁的代码块(可以是多个)只能有一个线程执行。例如有两个线程a和b,分别执行x和y代码块,但是由于x和y同时使用o作为锁,所以当a执行x时,b不能执行y。
②synchronized可以用于方法上(此时默认使用this对象,不需要手动设置),也可以使用在方法中的一段代码上,此时需要指定用哪个对象进行锁定,通常是那个多个线程需要访问的对象。也可以在一个静态方法上使用synchronized,使用的是this.class对象作为锁,它会锁住整个类中的代码块;相同的,如果要在一个静态块中使用同步,也必须使用this.class作为锁。
③如果在一段同步代码之内进行多个线程间进行消息传递,使用的是wait()/notify()/notifyAll(),就是那个用来锁定的对象Object的方法。
2、Lock锁:synchronized是语法上的实现,在Java1.5版本之后引入了Lock概念,使锁作为一种类的存在,在java.util.concurrent.locks包下。
其中Lock和ReadWriteLock是接口,定义了锁需要实现的功能,主要的就是lock()/unlock()和readLock()/writeLock()。这里就和synchronized有一个很大的区别即是Lock需要手动的释放锁,并且通常需要在加锁的代码上使用try并将解锁的部分在finally中执行。
1 public booleanoffer(E e) {2 checkNotNull(e);3 final ReentrantLock lock = this.lock;4 lock.lock();5 try{6 if (count ==items.length)7 return false;8 else{9 insert(e);10 return true;11 }12 } finally{13 lock.unlock();14 }15 }
ArrayBlockingQueue中offer()
在1.5版本中,提供了两个实现:ReentrantLock,ReentrantReadWriteLock。其中,ReentrantLock的效果更类似与synchronized,但是增加了一些特性以提高性能;而ReentrantReadWriteLock则是读写锁,在实现上如果写锁执行,则其他线程都阻塞,但是如果读锁执行则会阻塞写锁而不会阻塞其他的读锁。
同样的,为了在多个线程中实现通信,提供了Condition接口,其中await()/signal()/signalAll()对应了object中的三个方法,区别是这几个方法可以不在锁的范围内进行操作。该接口的实现类由Lock的实现类提供,调用其newCondition()即可。也就是说Condition对象是绑定到Lock对象上的,而且一个Lock对象可以生成多个Condition。
1 public ArrayBlockingQueue(int capacity, booleanfair) {2 if (capacity <= 0)3 throw newIllegalArgumentException();4 this.items = newObject[capacity];5 lock = newReentrantLock(fair);6 notEmpty =lock.newCondition();7 notFull =lock.newCondition();8 }9
10 private voidinsert(E x) {11 items[putIndex] =x;12 putIndex =inc(putIndex);13 ++count;14 //可以看到这里调用了signal来唤醒相关线程,但是锁是在offer()中添加的
15 notEmpty.signal();16 }
ArrayBlockingQueue部分函数
下面是ReentrantLock的类图关系,ReentrantLock中的实现都是依赖其中的Sync的有效子类NonfairSync(非公平锁)和FairSync(公平锁)。而锁的实现依赖于其父类。
1 private finalSync sync;2
3 //构造函数
4 publicReentrantLock() {5 sync = newNonfairSync();6 }7 public ReentrantLock(booleanfair) {8 sync = fair ? new FairSync() : newNonfairSync();9 }10
11 //功能代码
12 public voidlock() {13 sync.lock();14 }15
16 public booleantryLock() {17 return sync.nonfairTryAcquire(1);18 }19
20 public voidunlock() {21 sync.release(1);22 }23
24 publicCondition newCondition() {25 returnsync.newCondition();26 }
ReetrantLock源码1
1 abstract static class Sync extendsAbstractQueuedSynchronizer {2 private static final long serialVersionUID = -5179523762034025860L;3
4 abstract voidlock();5
6 final boolean nonfairTryAcquire(intacquires) {7 final Thread current =Thread.currentThread();8 int c =getState();9 if (c == 0) {10 if (compareAndSetState(0, acquires)) {11 setExclusiveOwnerThread(current);12 return true;13 }14 }15 else if (current ==getExclusiveOwnerThread()) {16 int nextc = c +acquires;17 if (nextc < 0) //overflow
18 throw new Error("Maximum lock count exceeded");19 setState(nextc);20 return true;21 }22 return false;23 }24
25 protected final boolean tryRelease(intreleases) {26 int c = getState() -releases;27 if (Thread.currentThread() !=getExclusiveOwnerThread())28 throw newIllegalMonitorStateException();29 boolean free = false;30 if (c == 0) {31 free = true;32 setExclusiveOwnerThread(null);33 }34 setState(c);35 returnfree;36 }37
38 protected final booleanisHeldExclusively() {39 //While we must in general read state before owner,40 //we don't need to do so to check if current thread is owner
41 return getExclusiveOwnerThread() ==Thread.currentThread();42 }43
44 finalConditionObject newCondition() {45 return newConditionObject();46 }47
48 //Methods relayed from outer class
49
50 finalThread getOwner() {51 return getState() == 0 ? null: getExclusiveOwnerThread();52 }53
54 final intgetHoldCount() {55 return isHeldExclusively() ? getState() : 0;56 }57
58 final booleanisLocked() {59 return getState() != 0;60 }61
62 private voidreadObject(java.io.ObjectInputStream s)63 throwsjava.io.IOException, ClassNotFoundException {64 s.defaultReadObject();65 setState(0); //reset to unlocked state
66 }67 }68
69 /**
70 * Sync object for non-fair locks71 */
72 static final class NonfairSync extendsSync {73 private static final long serialVersionUID = 7316153563782823691L;74
75 /**
76 * Performs lock. Try immediate barge, backing up to normal77 * acquire on failure.78 */
79 final voidlock() {80 if (compareAndSetState(0, 1))81 setExclusiveOwnerThread(Thread.currentThread());82 else
83 acquire(1);84 }85
86 protected final boolean tryAcquire(intacquires) {87 returnnonfairTryAcquire(acquires);88 }89 }90
91 /**
92 * Sync object for fair locks93 */
94 static final class FairSync extendsSync {95 private static final long serialVersionUID = -3000897897090466540L;96
97 final voidlock() {98 acquire(1);99 }100
101 /**
102 * Fair version of tryAcquire. Don't grant access unless103 * recursive call or no waiters or is first.104 */
105 protected final boolean tryAcquire(intacquires) {106 final Thread current =Thread.currentThread();107 int c =getState();108 if (c == 0) {109 if (!hasQueuedPredecessors() &&
110 compareAndSetState(0, acquires)) {111 setExclusiveOwnerThread(current);112 return true;113 }114 }115 else if (current ==getExclusiveOwnerThread()) {116 int nextc = c +acquires;117 if (nextc < 0)118 throw new Error("Maximum lock count exceeded");119 setState(nextc);120 return true;121 }122 return false;123 }124 }
ReetrantLock源码2
有关AbstractQueuedSynchronizer实现原理的内容参考:http://www.infoq.com/cn/articles/jdk1.8-abstractqueuedsynchronizer,其中有两个基本:1,使用volatile修饰的state变量用于设置是否有线程获得锁,2,一个FIFO的队列用于保存挂起的线程。
四、有了以上的基础,就可以实现多线程中的一个经典例子:生产者消费者模型,就是有一个仓库,生产者将商品放入其中,当仓满时则不能生产,并阻塞所有的生成线程;此时只能由消费者线程进行消费,但是当仓空时就需要阻塞消费者线程而唤醒生产者线程进行生产。
1、使用同步锁,以及wait/notifyAll机制实现,其中需要注意的是wait的使用规范,详细参见http://www.importnew.com/16453.html,有一条重要的就是:在while中而不是if中使用wait.
1 packagethread;2
3 importjava.util.LinkedList;4 importjava.util.Queue;5
6 public classProducerConsumer2 {7 public static voidmain(String[] args) {8 Storage s=newStorage();9
10 Producer p1=newProducer(s);11 Producer p2=newProducer(s);12 Producer p3=newProducer(s);13
14 Consumer c1=newConsumer(s);15 Consumer c2=newConsumer(s);16 Consumer c3=newConsumer(s);17 Consumer c4=newConsumer(s);18 Consumer c5=newConsumer(s);19
20 Thread t1=new Thread(p1,"p1");21 Thread t2=new Thread(p2,"p2");22 Thread t3=new Thread(p3,"p3");23 Thread t4=new Thread(c1,"c1");24 Thread t5=new Thread(c2,"c2");25 Thread t6=new Thread(c3,"c3");26 Thread t7=new Thread(c4,"c4");27 Thread t8=new Thread(c5,"c5");28
29 t1.start();30 t2.start();31 t3.start();32 t4.start();33 t5.start();34 t6.start();35 t7.start();36 t8.start();37 }38 }39
40 classProduct{41 private intid;42
43 public Product(intid) {44 this.id=id;45 }46
47 @Override48 publicString toString() {49 return "Product [id=" + id + "]";50 }51 }52
53 //仓库对象,生产者和消费者之间的桥梁
54 classStorage{55 //仓库容量
56 Queue queues = new LinkedList();57
58 //生产,即向仓库中添加产品
59 public voidpush(Product s){60 queues.add(s);61 }62
63 //消费
64 publicProduct pop(){65 returnqueues.remove();66 }67 }68
69 class Producer implementsRunnable{70 privateStorage s;71
72 publicProducer(Storage s) {73 this.s=s;74 }75
76 @Override77 public voidrun() {78 while(true){79 synchronized(s.queues){80 while(s.queues.size()>=10){81 try{82 System.out.println(Thread.currentThread().getName()+",队满,不能添加");83 s.queues.wait();84 }catch(InterruptedException e){85 e.printStackTrace();86 }87 }88 Product p=new Product((int)(Math.random()*10000));89 s.push(p);90 System.out.println(Thread.currentThread().getName()+",生产了产品,现在有"+s.queues.size());91 System.out.println("============================================");92 s.queues.notifyAll();93 }94 }95 }96 }97
98 class Consumer implementsRunnable{99 privateStorage s;100
101 publicConsumer(Storage s) {102 this.s=s;103 }104
105 @Override106 public voidrun() {107 while(true){108 synchronized(s.queues){109 //注意这里的while不能换为if
110 while(s.queues.isEmpty()){111 try{112 System.out.println("队空,不能消费");113 s.queues.wait();114 }catch(InterruptedException e){115 e.printStackTrace();116 }117 }118 s.pop();119 System.out.println(Thread.currentThread().getName()+",消费了产品,现在有"+s.queues.size());120 System.out.println("============================================");121 s.queues.notifyAll();122 }123 }124 }125 }
生产者消费者模型-1
2、使用Lock锁实现
1 classSto{2 //使用Lock对象代替synchronized,使用Condition进行线程间通讯
3 Lock lock=newReentrantLock();4 Condition condition=lock.newCondition();5
6 //仓库容量
7 Queue queues = new LinkedList();8
9 //生产,即向仓库中添加产品
10 public voidpush(){11 lock.lock();12 //synchronized(queues){
13 try{14 while(queues.size()>=10){15 System.out.println("队满,不能生成");16 try{17 //queues.wait();
18 condition.await();19 } catch(InterruptedException e) {20 e.printStackTrace();21 }22 }23 Pro p=new Pro((int)Math.random());24 queues.add(p);25 System.out.println("p=================="+queues.size()+"=====================");26 //queues.notifyAll();
27 condition.signalAll();28 } finally{29 lock.unlock();30 }31 //}
32 }33
34 //消费
35 public voidpop(){36 lock.lock();37 //synchronized(queues){
38 while(queues.isEmpty()){39 System.out.println("队空,不能消费");40 try{41 //queues.wait();
42 condition.await();43 } catch(InterruptedException e) {44 e.printStackTrace();45 }46 }47 queues.remove();48 System.out.println("c======================"+queues.size()+"==========================");49 //queues.notifyAll();
50 condition.signalAll();51 //}
52 }53 }
生产者和消费者模型-2
3、使用阻塞队列实现
五、其他。
1、ThreadLocal:为当前线程保存一份私有变量,隔离其他线程的访问.主要的操作就是添加/获取/移除/initialValue。
1 //获取当前线程保存的此线程局部的初始值,实现未返回null,提供给子类进行覆写的
2 protectedT initialValue() {3 return null;4 }5 //为当前线程设置线程私有变量
6 public voidset(T value) {7 Thread t =Thread.currentThread();8 ThreadLocalMap map =getMap(t);9 if (map != null)10 map.set(this, value);11 else
12 createMap(t, value);13 }14 //获取当前线程保存的线程私有变量
15 publicT get() {16 Thread t =Thread.currentThread();17 ThreadLocalMap map =getMap(t);18 if (map != null) {19 ThreadLocalMap.Entry e = map.getEntry(this);20 if (e != null) {21 @SuppressWarnings("unchecked")22 T result =(T)e.value;23 returnresult;24 }25 }26 returnsetInitialValue();27 }28 privateT setInitialValue() {29 T value =initialValue();30 Thread t =Thread.currentThread();31 ThreadLocalMap map =getMap(t);32 if (map != null)33 map.set(this, value);34 else
35 createMap(t, value);36 returnvalue;37 }38 //移除
39 public voidremove() {40 ThreadLocalMap m =getMap(Thread.currentThread());41 if (m != null)42 m.remove(this);43 }
ThreadLocal源码
之所以这些变量操作和线程绑定,是因为①操作时首先获取当前线程对象,②Thread类中有一个ThreadLocal.ThreadLocalMap的成员进行保存变量。
2、Executor框架线程池.