准备写一个线程的专题。说实话线程这一块一直也是模模糊糊,能够应付工作但是不够深入。没有一个完整的思维框架。
先附一张线程的运行状态和需要用的关键字:
理解线程的优先权
1.记住当线程的优先级没有指定时,所有线程都携带普通优先级。
2.优先级可以用从1到10的范围指定。10表示最高优先级,1表示最低优先级,5是普通优先级。
3.记住优先级最高的线程在执行时被给予优先。但是不能保证线程在启动时就进入运行状态。
4.与在线程池中等待运行机会的线程相比,当前正在运行的线程可能总是拥有更高的优先级。
5.由调度程序决定哪一个线程被执行。
6.t.setPriority()用来设定线程的优先级。
7.记住在线程开始方法被调用之前,线程的优先级应该被设定。
8.你可以使用常量,如MIN_PRIORITY,MAX_PRIORITY,NORM_PRIORITY来设定优先级
yield()方法
一个调用yield()方法的线程告诉虚拟机它将自己的线程从运行态转为可运行态让其他线程占用自己的位置去自己的任务。
好比大家都在排队买票,当我发现一个哥们和我一样优先的时候,但是我发现他好像很紧急我会主动等待,等他执行完之后我再执行
注意:我会等待一个哥们,当有两个一起很紧急的时候我只会等待第一个,也就是说一个买完了我就会执行,不会等第二个哥们买完票我再执行
1.Yield是一个静态的原生(native)方法
2.Yield告诉当前正在执行的线程把运行机会交给线程池中拥有相同优先级的线程。
3.Yield不能保证使得当前正在运行的线程迅速转换到可运行的状态
4.它仅能使一个线程从运行状态转到可运行状态,而不是等待或阻塞状态
demo
public class YieldExample
{
public static void main(String[] args)
{
Thread producer = new Producer();
Thread consumer = new Consumer();
//producer.setPriority(Thread.MIN_PRIORITY); //Min Priority
//consumer.setPriority(Thread.MAX_PRIORITY); //Max Priority
producer.start();
consumer.start();
}
}
class Producer extends Thread
{
public void run()
{
for (int i = 0; i < 5; i++)
{
System.out.println("I am Producer : Produced Item " + i);
Thread.yield();
}
}
}
class Consumer extends Thread
{
public void run()
{
for (int i = 0; i < 5; i++)
{
System.out.println("I am Consumer : Consumed Item " + i);
Thread.yield();
}
}
}
运行结果:
I am Consumer : Consumed Item 0
I am Producer : Produced Item 0
I am Consumer : Consumed Item 1
I am Producer : Produced Item 1
I am Consumer : Consumed Item 2
I am Producer : Produced Item 2
I am Consumer : Consumed Item 3
I am Producer : Produced Item 3
I am Consumer : Consumed Item 4
I am Producer : Produced Item 4
可以看到不设置优先级的话两个线程的优先级默认是一样的。然后执行yield()方法的结果就是一个线程等一个线程
将Consumer 的yield()方法注释后:
class Consumer extends Thread
{
public void run()
{
for (int i = 0; i < 5; i++)
{
System.out.println("I am Consumer : Consumed Item " + i);
//Thread.yield();
}
}
}
运行结果:
I am Producer : Produced Item 0
I am Consumer : Consumed Item 0
I am Consumer : Consumed Item 1
I am Consumer : Consumed Item 2
I am Consumer : Consumed Item 3
I am Consumer : Consumed Item 4
I am Producer : Produced Item 1
I am Producer : Produced Item 2
I am Producer : Produced Item 3
I am Producer : Produced Item 4
可以看到第一个Producer再执行完之后就一直等待Consumer全部执行完之后才执行。
现在将优先级加上,且Producer,Consumer 都执行yield()
public class YieldExample
{
public static void main(String[] args)
{
Thread producer = new Producer();
Thread consumer = new Consumer();
producer.setPriority(Thread.MIN_PRIORITY); //Min Priority
consumer.setPriority(Thread.MAX_PRIORITY); //Max Priority
producer.start();
consumer.start();
}
}
运行结果:
I am Consumer : Consumed Item 0
I am Consumer : Consumed Item 1
I am Consumer : Consumed Item 2
I am Consumer : Consumed Item 3
I am Consumer : Consumed Item 4
I am Producer : Produced Item 0
I am Producer : Produced Item 1
I am Producer : Produced Item 2
I am Producer : Produced Item 3
I am Producer : Produced Item 4
说明当优先级不同时:谁的大就先执行谁不会去管你是不是挂起了!
说白了就是还是我的事情比较紧急!
当加入第3个人的时候,我叫Producer等待
代码
public class YieldExample
{
public static void main(String[] args)
{
Thread producer = new Producer();
Thread consumer = new Consumer();
Three three = new Three();
//producer.setPriority(Thread.MIN_PRIORITY); //Min Priority
//consumer.setPriority(Thread.MAX_PRIORITY); //Max Priority
producer.start();
consumer.start();
three.start();
}
}
class Producer extends Thread
{
public void run()
{
for (int i = 0; i < 5; i++)
{
System.out.println("I am Producer : Produced Item " + i);
Thread.yield();
}
}
}
class Consumer extends Thread
{
public void run()
{
for (int i = 0; i < 5; i++)
{
System.out.println("I am Consumer : Consumed Item " + i);
//Thread.yield();
}
}
}
class Three extends Thread
{
public void run()
{
for (int i = 0; i < 5; i++)
{
System.out.println("I am Three : Three Item " + i);
//Thread.yield();
}
}
}
运行结果:**
I am Producer : Produced Item 0
I am Consumer : Consumed Item 0
I am Consumer : Consumed Item 1
I am Consumer : Consumed Item 2
I am Consumer : Consumed Item 3
I am Consumer : Consumed Item 4
I am Producer : Produced Item 1
I am Three : Three Item 0
I am Three : Three Item 1
I am Three : Three Item 2
I am Three : Three Item 3
I am Three : Three Item 4
I am Producer : Produced Item 2
I am Producer : Produced Item 3
I am Producer : Produced Item 4
可以得出结论:
当Producer 挂起后会等待Consumer执行完毕之后就执行自己,接着再yield()的方法之后再执行第三个。说明调用yield()方法只会等待一个线程。
join()方法
与yield()相反的是join()方法自己结束后再执行其他的线程。
demo
public class JoinExample
{
public static void main(String[] args) throws InterruptedException
{
Thread t = new Thread(new Runnable()
{
public void run()
{
System.out.println("First task started");
System.out.println("Sleeping for 2 seconds");
try
{
Thread.sleep(10000);
} catch (InterruptedException e)
{
e.printStackTrace();
}
System.out.println("First task completed");
}
});
Thread t1 = new Thread(new Runnable()
{
public void run()
{
System.out.println("Second task completed");
}
});
t.start(); // Line 15
t.join(); // Line 16
t1.start();
}
}
运行结果:
First task started
Sleeping for 2 seconds
First task completed
Second task completed
如果不加join方法的话一定是Second先运行完因为毕竟First等待了10s.
但是加上join方法后Second必须等待First执行完成之后才执行自己的任务。
notify()
唤醒最先等待的1个线程。
demo
package threadall.test3;
import java.util.logging.Level;
import java.util.logging.Logger;
public class NotificationTest {
private volatile boolean go = false;
public static void main(String args[]) throws InterruptedException {
final NotificationTest test = new NotificationTest();
Runnable waitTask = new Runnable(){
@Override
public void run(){
try {
test.shouldGo();
} catch (InterruptedException ex) {
Logger.getLogger(NotificationTest.class.getName()).
log(Level.SEVERE, null, ex);
}
System.out.println(Thread.currentThread() + " finished Execution");
}
};
Runnable notifyTask = new Runnable(){
@Override
public void run(){
test.go();
System.out.println(Thread.currentThread() + " finished Execution");
}
};
Thread t1 = new Thread(waitTask, "WT1"); //will wait
Thread t2 = new Thread(waitTask, "WT2"); //will wait
Thread t3 = new Thread(waitTask, "WT3"); //will wait
Thread t4 = new Thread(notifyTask,"NT1"); //will notify
Thread t5 = new Thread(notifyTask,"NT2"); //will notify
//starting all waiting thread
t1.start();
t2.start();
t3.start();
//pause to ensure all waiting thread started successfully
Thread.sleep(5000);
//starting notifying thread
t5.start();
t4.start();
}
/*
* wait and notify can only be called from synchronized method or bock
*/
private synchronized void shouldGo() throws InterruptedException {
// while(go != true){
System.out.println(Thread.currentThread()
+ " is going to wait on this object");
wait(); //release lock and reacquires on wakeup
System.out.println(Thread.currentThread() + " is woken up");
// }
// go = false; //resetting condition
}
/*
* both shouldGo() and go() are locked on current object referenced by "this" keyword
*/
private synchronized void go() {
//while (go == false){
System.out.println(Thread.currentThread()
+ " is going to notify all or one thread waiting on this object");
go = true; //making condition true for waiting thread
notify(); // only one out of three waiting thread WT1, WT2,WT3 will woke up
//notifyAll(); // all waiting thread WT1, WT2,WT3 will woke up
// }
}
}
输出结果:
Thread[WT1,5,main] is going to wait on this object
Thread[WT3,5,main] is going to wait on this object
Thread[WT2,5,main] is going to wait on this object
Thread[NT1,5,main] is going to notify all or one thread waiting on this object
Thread[WT1,5,main] is woken up
Thread[NT1,5,main] finished Execution
Thread[NT2,5,main] is going to notify all or one thread waiting on this object
Thread[WT1,5,main] finished Execution
Thread[NT2,5,main] finished Execution
Thread[WT3,5,main] is woken up
Thread[WT3,5,main] finished Execution
执行多次还有一种输出结果
Thread[WT1,5,main] is going to wait on this object
Thread[WT2,5,main] is going to wait on this object
Thread[WT3,5,main] is going to wait on this object
Thread[NT2,5,main] is going to notify all or one thread waiting on this object
Thread[WT1,5,main] is woken up
Thread[NT1,5,main] is going to notify all or one thread waiting on this object
Thread[NT1,5,main] finished Execution
Thread[WT1,5,main] finished Execution
Thread[NT2,5,main] finished Execution
Thread[WT2,5,main] is woken up
Thread[WT2,5,main] finished Execution
结论:我们将三个线程挂起然后用两个唤醒。第一种结果是1.3先挂起然后1.3唤醒。第二种结果是1.2先挂起,然后1.2唤醒,所以notify()的作用是先唤醒最先挂起的一个线程。为什么有时先执行2有时先执行3是因为主线程下有两个子线程,顺序不一定所以要想保证顺序性可以先主线程休眠
notifyAll()
唤醒所有wait()的子线程
将notifyAll()发开,将notify()注释运行
private synchronized void go() {
while (go == false){
System.out.println(Thread.currentThread()
+ " is going to notify all or one thread waiting on this object");
go = true; //making condition true for waiting thread
//notify(); // only one out of three waiting thread WT1, WT2,WT3 will woke up
notifyAll(); // all waiting thread WT1, WT2,WT3 will woke up
}
}
结果:
Thread[WT1,5,main] is going to wait on this object
Thread[WT2,5,main] is going to wait on this object
Thread[WT3,5,main] is going to wait on this object
Thread[NT1,5,main] is going to notify all or one thread waiting on this object
Thread[WT3,5,main] is woken up
Thread[NT1,5,main] finished Execution
Thread[WT3,5,main] finished Execution
Thread[WT2,5,main] is woken up
Thread[WT2,5,main] is going to wait on this object
Thread[WT1,5,main] is woken up
Thread[WT1,5,main] is going to wait on this object
的出结论:表面的结果是1.2.3先wait()然后执行notifyall时根据倒序3.2.1唤醒来执行的,所以得出结论notifyall不能保证执行的书顺序性,至于是不时按照倒序栈的模式执行还需要进一步验证。
interrupt
中断在java中主要有3个方法
interrupt()
isInterrupted()
interrupted()
interrupt(),在一个线程中调用另一个线程的interrupt()方法,即会向那个线程发出信号——线程中断状态已被设置。至于那个线程何去何从,由具体的代码实现决定。
isInterrupted(),用来判断当前线程的中断状态(true or false)。
interrupted()是个Thread的static方法,用来恢复中断状态。
demo
public class InterruptionInJava implements Runnable{
public static void main(String[] args) throws InterruptedException {
Thread testThread = new Thread(new InterruptionInJava(),"InterruptionInJava");
//start thread
testThread.start();
Thread.sleep(1000);
//interrupt thread
testThread.interrupt();
System.out.println("main end");
}
@Override
public void run() {
while(true){
if(Thread.currentThread().isInterrupted()){
System.out.println("Yes,I am interruted,but I am still running");
}else{
System.out.println("not yet interrupted");
}
}
}
}
运行结果:
Yes,I am interruted,but I am still running
Yes,I am interruted,but I am still running
Yes,I am interruted,but I am still running
Yes,I am interruted,but I am still running
Yes,I am interruted,but I am still running
Yes,I am interruted,but I am still running
Yes,I am interruted,but I am still running
说明线程一直处于running状态但是被interrut。
再来看一段JDK中ForkJoinPool的一段代码
for (int i = 0; i <= m; ++i) {
if ((w = ws[i]) != null) {
checkSum += w.base;
w.qlock = -1; // try to disable
if (pass > 0) {
w.cancelAll(); // clear queue
if (pass > 1 && (wt = w.owner) != null) {
if (!wt.isInterrupted()) {
try { // unblock join
wt.interrupt();
} catch (Throwable ignore) {
}
}
if (w.scanState < 0)
U.unpark(wt); // wake up
}
}
}
}
使用场景就是当不符合你的业务逻辑的时候你就需要中断线程。
守护线程
比如GC线程,如果JVM中所有非守护线程(即:常规的用户线程)都结束了,守护线程会被JVM中止。守护线程时为用户线程提供服务。
Fork/Join框架详解
Fork/Join框架是Java 7提供的一个用于并行执行任务的框架,是一个把大任务分割成若干个小任务,最终汇总每个小任务结果后得到大任务结果的框架。
Fork/Join = mapreduce
双端队列和工作密取
意义:正如阻塞队列适用于生产者消费者模式,双端队列同样适用与另一种模式,即工作密取。在生产者-消费者设计中,所有消费者共享一个工作队列,而在工作密取中,每个消费者都有各自的双端队列。 如果一个消费者完成了自己双端队列中的全部工作,那么他就可以从其他消费者的双端队列末尾秘密的获取工作。具有更好的可伸缩性,这是因为工作者线程不会在单个共享的任务队列上发生竞争。
synchronized 的局限性 与 Lock 的优点
Case 1 :
在使用synchronized关键字的情形下,假如占有锁的线程由于要等待IO或者其他原因(比如调用sleep方法)被阻塞了,但是又没有释放锁,那么其他线程就只能一直等待,别无他法。这会极大影响程序执行效率。因此,就需要有一种机制可以不让等待的线程一直无期限地等待下去(比如只等待一定的时间 (解决方案:tryLock(long time, TimeUnit unit)) 或者 能够响应中断 (解决方案:lockInterruptibly())),这种情况可以通过 Lock 解决。
Case 2 :
我们知道,当多个线程读写文件时,读操作和写操作会发生冲突现象,写操作和写操作也会发生冲突现象,但是读操作和读操作不会发生冲突现象。但是如果采用synchronized关键字实现同步的话,就会导致一个问题,即当多个线程都只是进行读操作时,也只有一个线程在可以进行读操作,其他线程只能等待锁的释放而无法进行读操作。因此,需要一种机制来使得当多个线程都只是进行读操作时,线程之间不会发生冲突。同样地,Lock也可以解决这种情况 (解决方案:ReentrantReadWriteLock) 。
Case 3 :
我们可以通过Lock得知线程有没有成功获取到锁 (解决方案:ReentrantLock) ,但这个是synchronized无法办到的。
线程池的使用
线程的创建和销毁是一个耗时的操作。如果在程序中频繁的创建和销毁线程,会对程序的反应速度造成严重的影响。有时候可能导致crash掉。如果在频繁的使用线程就可以使用线程池代替。
多线程使用的主要目的在于:
1、吞吐量:你做WEB,容器帮你做了多线程,但是他只能帮你做请求层面的。简单的说,可能就是一个请求一个线程。或多个请求一个线程。如果是单线程,那同时只能处理一个用户的请求。
2、伸缩性:也就是说,你可以通过增加CPU核数来提升性能。如果是单线程,那程序执行到死也就利用了单核,肯定没办法通过增加CPU核数来提升性能。
--举个简单的例子:
假设有个请求,这个请求服务端的处理需要执行3个很缓慢的IO操作(比如数据库查询或文件查询),那么正常的顺序可能是(括号里面代表执行时间):
a、读取文件1 (10ms)
b、处理1的数据(1ms)
c、读取文件2 (10ms)
d、处理2的数据(1ms)
e、读取文件3 (10ms)
f、处理3的数据(1ms)
g、整合1、2、3的数据结果 (1ms)
单线程总共就需要34ms。
那如果你在这个请求内,把ab、cd、ef分别分给3个线程去做,就只需要12ms了。
主要的思想就是空间换时间或者说资源换时间。