java线程深入学习
一、java中的线程是通过Thread类创建的,
//下面是构造函数,一个共同的特点就是:都是调用init()进行创建的
public Thread() {
init(null, null, "Thread-" + nextThreadNum(), 0);
} public Thread(Runnable target) {
init(null, target, "Thread-" + nextThreadNum(), 0);
} Thread(Runnable target, AccessControlContext acc) {
init(null, target, "Thread-" + nextThreadNum(), 0, acc);
} public Thread(ThreadGroup group, Runnable target) {
init(group, target, "Thread-" + nextThreadNum(), 0);
} public Thread(String name) {
init(null, null, name, 0);
} public Thread(ThreadGroup group, String name) {
init(group, null, name, 0);
} public Thread(Runnable target, String name) {
init(null, target, name, 0);
} public Thread(ThreadGroup group, Runnable target, String name) {
init(group, target, name, 0);
} public Thread(ThreadGroup group, Runnable target, String name,
long stackSize) {
init(group, target, name, stackSize);
} //init()方法有两个
private void init(ThreadGroup g, Runnable target, String name,
long stackSize) {
init(g, target, name, stackSize, null);
}
/*g:用于将线程分组管理
*target:用于指定线程将要执行的任务
*name:线程的名字
*stackSize:
*acc:
*/
private void init(ThreadGroup g, Runnable target, String name,
long stackSize, AccessControlContext acc) {
//线程必须有一个名字,默认情况下是Thread-x,x是从0开始的int型数
if (name == null) {
throw new NullPointerException("name cannot be null");
} this.name = name.toCharArray(); Thread parent = currentThread();
SecurityManager security = System.getSecurityManager();
if (g == null) {
/* Determine if it's an applet or not */ /* If there is a security manager, ask the security manager
what to do. */
if (security != null) {
g = security.getThreadGroup();
} /* If the security doesn't have a strong opinion of the matter
use the parent thread group. */
if (g == null) {
g = parent.getThreadGroup();
}
}
Thread源码
从构造函数可以看出,创建一个有意义的线程(有可执行的任务),就是向其中传递一个实现Runnable的对象即可,但是也可以继承Thread类(因为该类已经实现了前一条),然后重写run()即可。后一种方法不推荐,因为这个类最重要的是提供需要执行的方法即可。
上图显示了线程状态转换的条件,这些方法的源码见下:
public synchronized void start() {
/**
* 0 状态代表 "NEW".
*/
if (threadStatus != 0)
throw new IllegalThreadStateException(); group.add(this); boolean started = false;
try {
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
/* do nothing. If start0 threw a Throwable then
it will be passed up the call stack */
}
}
} private native void start0(); //执行target的run(),在start之后自动执行,
public void run() {
if (target != null) {
target.run();
}
} //中断线程
public void interrupt() {
//判断是否是当前正在执行的线程,检查权限
if (this != Thread.currentThread())
checkAccess(); synchronized (blockerLock) {
Interruptible b = blocker;
if (b != null) {
interrupt0(); // Just to set the interrupt flag
b.interrupt(this);
return;
}
}
interrupt0();
} //从源码可以看到,该方法调用wait(),使alive状态(start之后,die之前)线程进入等待状态
public final synchronized void join(long millis)
throws InterruptedException {
long base = System.currentTimeMillis();
long now = 0; if (millis < 0) {
throw new IllegalArgumentException("timeout value is negative");
} if (millis == 0) {
while (isAlive()) {
wait(0);
}
} else {
while (isAlive()) {
long delay = millis - now;
if (delay <= 0) {
break;
}
wait(delay);
now = System.currentTimeMillis() - base;
}
}
} public final synchronized void join(long millis, int nanos)
throws InterruptedException { if (millis < 0) {
throw new IllegalArgumentException("timeout value is negative");
} if (nanos < 0 || nanos > 999999) {
throw new IllegalArgumentException(
"nanosecond timeout value out of range");
} if (nanos >= 500000 || (nanos != 0 && millis == 0)) {
millis++;
} join(millis);
} public final void join() throws InterruptedException {
join(0);
} //不能指定时间的休眠
public static native void yield(); //让线程休眠指定时间
public static native void sleep(long millis) throws InterruptedException; public static void sleep(long millis, int nanos)
throws InterruptedException {
if (millis < 0) {
throw new IllegalArgumentException("timeout value is negative");
} if (nanos < 0 || nanos > 999999) {
throw new IllegalArgumentException(
"nanosecond timeout value out of range");
} if (nanos >= 500000 || (nanos != 0 && millis == 0)) {
millis++;
} sleep(millis);
} //java线程的几种状态判断
//start之后die之前都是alive状态
public final native boolean isAlive(); Thread源码
Thread源码
需要注意的是start()是启动一个线程,而run只是执行一个普通方法。
一个线程要执行需要满足两个条件,一是获取CPU,二是获取锁(在有同步情况下),这就是阻塞产生的原因。由前可知阻塞有两种情况,一种是阻塞自身(当前)线程(等待阻塞),另一种就是阻塞其他线程(同步阻塞)。上面有些方法类似,但是就是有这些细小的区别。例如:
①sleep:java中该方法是静态方法(所以一般应该有Thread而非实例进行调用),调用该方法会当前线程使释放CPU,但是不释放锁。下面的例子中在main()中使用t1.sleep()不能使t1休眠,因为t1只是一个普通的Thread对象,而不是线程,其在主线程中使用,所以是主线程休眠,所以出现以下效果。另外注意sleep是静态方法,最好使用类名即Thread调用。
public static void main(String[] args) throws InterruptedException {
Thread t1=new Thread(new Runnable(){
@Override
public void run() {
for(int i=0;i<10;i++){
System.out.println("i="+i);
}
}
});
Thread t2=new Thread(new Runnable(){
@Override
public void run() {
for(int j=0;j<10;j++){
System.out.println("j="+j);
}
}
});
t1.start();
// t1.sleep(5000); //和下面效果一样,但是最好使用下面的方式
Thread.sleep(5000);
System.out.println("a");
t2.start();
}
等待阻塞
②wait:当前线程休眠,释放CPU,同时释放锁,wait是Object对象的方法,必须在同步块中使用,并且由notify或者notifyAll进行唤醒。那么需要使用那个对象的wait()呢,这个问题其实和在那个对象上同步是一样的问题,其实就是在多个线程需要使用的那个对象,在该对象上加锁,并且调用这个对象的wait()。
更过关于这些方法的区别参见:http://blog.csdn.net/evane1890/article/details/3313416
二、多线程:上面是线程的一些基本情况,但是通常都是有多个线程一起使用的。线程之间共享同一片内存区域,所以当多个线程访问同一个数据时就可能出错,为了获得最佳速度,java允许线程保存共享成员变量的私有拷贝,而且只当线程进入或者离开同步代码块时才与共享成员变量的原始值对比。这就引起了多线程的一个问题:可见性。另一个问题就是对于一段代码,只允许一个线程操作,即互斥性。
volatile关键字可以解决可见性的问题,所以对于多个线程访问的变量可以使用。但是其并不能解决原子性,所以并不能保证在多线程中的安全。参考:http://blog.csdn.net/zmissm/article/details/23484457,http://blog.csdn.net/ns_code/article/details/17101369
三、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中执行。
public boolean offer(E e) {
checkNotNull(e);
final ReentrantLock lock = this.lock;
lock.lock();
try {
if (count == items.length)
return false;
else {
insert(e);
return true;
}
} finally {
lock.unlock();
}
}
ArrayBlockingQueue中offer()
在1.5版本中,提供了两个实现:ReentrantLock,ReentrantReadWriteLock。其中,ReentrantLock的效果更类似与synchronized,但是增加了一些特性以提高性能;而ReentrantReadWriteLock则是读写锁,在实现上如果写锁执行,则其他线程都阻塞,但是如果读锁执行则会阻塞写锁而不会阻塞其他的读锁。
同样的,为了在多个线程中实现通信,提供了Condition接口,其中await()/signal()/signalAll()对应了object中的三个方法,区别是这几个方法可以不在锁的范围内进行操作。该接口的实现类由Lock的实现类提供,调用其newCondition()即可。也就是说Condition对象是绑定到Lock对象上的,而且一个Lock对象可以生成多个Condition。
public ArrayBlockingQueue(int capacity, boolean fair) {
if (capacity <= 0)
throw new IllegalArgumentException();
this.items = new Object[capacity];
lock = new ReentrantLock(fair);
notEmpty = lock.newCondition();
notFull = lock.newCondition();
} private void insert(E x) {
items[putIndex] = x;
putIndex = inc(putIndex);
++count;
//可以看到这里调用了signal来唤醒相关线程,但是锁是在offer()中添加的
notEmpty.signal();
}
ArrayBlockingQueue部分函数
下面是ReentrantLock的类图关系,ReentrantLock中的实现都是依赖其中的Sync的有效子类NonfairSync(非公平锁)和FairSync(公平锁)。而锁的实现依赖于其父类。
private final Sync sync; //构造函数
public ReentrantLock() {
sync = new NonfairSync();
}
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
} //功能代码
public void lock() {
sync.lock();
} public boolean tryLock() {
return sync.nonfairTryAcquire(1);
} public void unlock() {
sync.release(1);
} public Condition newCondition() {
return sync.newCondition();
}
ReetrantLock源码1
abstract static class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = -5179523762034025860L; abstract void lock(); final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
} protected final boolean tryRelease(int releases) {
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
} protected final boolean isHeldExclusively() {
// While we must in general read state before owner,
// we don't need to do so to check if current thread is owner
return getExclusiveOwnerThread() == Thread.currentThread();
} final ConditionObject newCondition() {
return new ConditionObject();
} // Methods relayed from outer class final Thread getOwner() {
return getState() == 0 ? null : getExclusiveOwnerThread();
} final int getHoldCount() {
return isHeldExclusively() ? getState() : 0;
} final boolean isLocked() {
return getState() != 0;
} private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
s.defaultReadObject();
setState(0); // reset to unlocked state
}
} /**
* Sync object for non-fair locks
*/
static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L; /**
* Performs lock. Try immediate barge, backing up to normal
* acquire on failure.
*/
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
} protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
} /**
* Sync object for fair locks
*/
static final class FairSync extends Sync {
private static final long serialVersionUID = -3000897897090466540L; final void lock() {
acquire(1);
} /**
* Fair version of tryAcquire. Don't grant access unless
* recursive call or no waiters or is first.
*/
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
}
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.
package thread; import java.util.LinkedList;
import java.util.Queue; public class ProducerConsumer2 {
public static void main(String[] args) {
Storage s=new Storage(); Producer p1=new Producer(s);
Producer p2=new Producer(s);
Producer p3=new Producer(s); Consumer c1=new Consumer(s);
Consumer c2=new Consumer(s);
Consumer c3=new Consumer(s);
Consumer c4=new Consumer(s);
Consumer c5=new Consumer(s); Thread t1=new Thread(p1,"p1");
Thread t2=new Thread(p2,"p2");
Thread t3=new Thread(p3,"p3");
Thread t4=new Thread(c1,"c1");
Thread t5=new Thread(c2,"c2");
Thread t6=new Thread(c3,"c3");
Thread t7=new Thread(c4,"c4");
Thread t8=new Thread(c5,"c5"); t1.start();
t2.start();
t3.start();
t4.start();
t5.start();
t6.start();
t7.start();
t8.start();
}
} class Product{
private int id; public Product(int id) {
this.id=id;
} @Override
public String toString() {
return "Product [id=" + id + "]";
}
} //仓库对象,生产者和消费者之间的桥梁
class Storage{
//仓库容量
Queue<Product> queues = new LinkedList<Product>(); //生产,即向仓库中添加产品
public void push(Product s){
queues.add(s);
} //消费
public Product pop(){
return queues.remove();
}
} class Producer implements Runnable{
private Storage s; public Producer(Storage s) {
this.s=s;
} @Override
public void run() {
while(true){
synchronized(s.queues){
while(s.queues.size()>=10){
try{
System.out.println(Thread.currentThread().getName()+",队满,不能添加");
s.queues.wait();
}catch(InterruptedException e){
e.printStackTrace();
}
}
Product p=new Product((int)(Math.random()*10000));
s.push(p);
System.out.println(Thread.currentThread().getName()+",生产了产品,现在有"+s.queues.size());
System.out.println("============================================");
s.queues.notifyAll();
}
}
}
} class Consumer implements Runnable{
private Storage s; public Consumer(Storage s) {
this.s=s;
} @Override
public void run() {
while(true){
synchronized(s.queues){
//注意这里的while不能换为if
while(s.queues.isEmpty()){
try{
System.out.println("队空,不能消费");
s.queues.wait();
}catch(InterruptedException e){
e.printStackTrace();
}
}
s.pop();
System.out.println(Thread.currentThread().getName()+",消费了产品,现在有"+s.queues.size());
System.out.println("============================================");
s.queues.notifyAll();
}
}
}
}
生产者消费者模型-1
2、使用Lock锁实现
class Sto{
//使用Lock对象代替synchronized,使用Condition进行线程间通讯
Lock lock=new ReentrantLock();
Condition condition=lock.newCondition(); //仓库容量
Queue<Pro> queues = new LinkedList<Pro>(); //生产,即向仓库中添加产品
public void push(){
lock.lock();
// synchronized(queues){
try {
while(queues.size()>=10){
System.out.println("队满,不能生成");
try {
// queues.wait();
condition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Pro p=new Pro((int)Math.random());
queues.add(p);
System.out.println("p=================="+queues.size()+"=====================");
// queues.notifyAll();
condition.signalAll();
} finally{
lock.unlock();
}
// }
} //消费
public void pop(){
lock.lock();
// synchronized(queues){
while(queues.isEmpty()){
System.out.println("队空,不能消费");
try {
// queues.wait();
condition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
queues.remove();
System.out.println("c======================"+queues.size()+"==========================");
// queues.notifyAll();
condition.signalAll();
// }
}
}
生产者和消费者模型-2
3、使用阻塞队列实现
五、其他。
1、ThreadLocal:为当前线程保存一份私有变量,隔离其他线程的访问.主要的操作就是添加/获取/移除/initialValue。
//获取当前线程保存的此线程局部的初始值,实现未返回null,提供给子类进行覆写的
protected T initialValue() {
return null;
}
//为当前线程设置线程私有变量
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
//获取当前线程保存的线程私有变量
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
private T setInitialValue() {
T value = initialValue();
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
return value;
}
//移除
public void remove() {
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null)
m.remove(this);
}
ThreadLocal源码
之所以这些变量操作和线程绑定,是因为①操作时首先获取当前线程对象,②Thread类中有一个ThreadLocal.ThreadLocalMap的成员进行保存变量。
2、Executor框架线程池.
java线程深入学习的更多相关文章
- Java线程池学习
Java线程池学习 Executor框架简介 在Java 5之后,并发编程引入了一堆新的启动.调度和管理线程的API.Executor框架便是Java 5中引入的,其内部使用了线程池机制,它在java ...
- Java线程机制学习
前面的文章中总结过Java中用来解决共享资源竞争导致线程不安全的几种常用方式: synchronized: ReentrantLock: ThreadLocal: 这些都是在简单介绍了基本用法的基础上 ...
- Java线程池学习心得
一.普通线程和线程池的对比 new Thread的弊端如下: a. 每次new Thread新建对象性能差.b. 线程缺乏统一管理,可能无限制新建线程,相互之间竞争,及可能占用过多系统资源导致死机或o ...
- Java基础学习总结(94)——Java线程再学习
Java线程有哪些不太为人所知的技巧与用法? 萝卜白菜各有所爱.像我就喜欢Java.学无止境,这也是我喜欢它的一个原因.日常工作中你所用到的工具,通常都有些你从来没有了解过的东西,比方说某个方法或者是 ...
- java 线程基础学习
今天趁空闲时间看了点线程方面的知识 首先看的是volatile关键字,按照我之前书上看到的一点知识,自己的理解是,volatile关键字会阻止编译优化,因为cpu每次读取数据是并不是从高速缓存中读取, ...
- Java 线程池学习
Java里面线程池的顶级接口是Executor,但是严格意义上讲Executor并不是一个线程池,而只是一个执行线程的工具.真正的线程池接口是ExecutorService. 下面这张图完整描述了线程 ...
- java线程API学习 线程池ThreadPoolExecutor(转)
线程池ThreadPoolExecutor继承自ExecutorService.是jdk1.5加入的新特性,将提交执行的任务在内部线程池中的可用线程中执行. 构造函数 ThreadPoolExecut ...
- JAVA线程池学习,ThreadPoolTaskExecutor和ThreadPoolExecutor有何区别?
初学者很容易看错,如果没有看到spring或者JUC源码的人肯定是不太了解的. ThreadPoolTaskExecutor是spring core包中的,而ThreadPoolExecutor是JD ...
- Java线程池学习总结
一 使用线程池的好处 池化技术相比大家已经屡见不鲜了,线程池.数据库连接池.Http 连接池等等都是对这个思想的应用.池化技术的思想主要是为了减少每次获取资源的消耗,提高对资源的利用率. 线程池提供了 ...
随机推荐
- ubuntu 16.04 安装KVM-多系统
为了使用QQ 只能再跑一个Windows了
- code vs 1216 跳马问题
1216 跳马问题 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 黄金 Gold 题解 题目描述 Description 题目 输入描述 Input Descri ...
- ajax前台传到后台乱码,显示问号的问题
response.setContentType("text/html;charset=gbk"); response.setHeader("Cache-Control&q ...
- nj03---阻塞和线程
Node.js最大的特性就是"异步式I/O"与事件紧密结合的编程模式.这种模式与传统的同步式IO线性的编程思路有很大的不同,因为控制流很大程度上要靠"事件"和& ...
- Android 进程常驻(0)----MarsDaemon使用说明
版权声明:本文为博主原创文章,未经博主允许不得转载. 这是一个轻量级的库,配置几行代码,就可以实现在Android上实现进程常驻,也就是在系统强杀下,以及360获取root权限下,clean mast ...
- hdu_5154 拓扑
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #i ...
- thinkphp5的Illegal string offset 'id'错误
thinkphp5的Illegal string offset 'id'错误 问题 解答 数组同名了,一个html页面传进来两个cateres的数组,所以在找id的时候不知道找这两个里面的哪一个 第一 ...
- [Poj 1015] Jury Compromise 解题报告 (完全背包)
题目链接:http://poj.org/problem?id=1015 题目: 题解: 我们考虑设计DP状态(因为这很显然是一个完全背包问题不是吗?) dp[j][k]表示在外层循环到i时,选了j个人 ...
- 安卓开发--AsyncTask
package com.cnn.asynctask; import android.app.Activity; import android.content.Intent; import androi ...
- Android--Fragment与Activity通信
package com.example.testfragment; import com.example.testfragment.MainFargment.BackString; import an ...