java线程基础

准备写一个线程的专题。说实话线程这一块一直也是模模糊糊,能够应付工作但是不够深入。没有一个完整的思维框架。

先附一张线程的运行状态和需要用的关键字:

image.png

理解线程的优先权

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了。
主要的思想就是空间换时间或者说资源换时间。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,128评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,316评论 3 388
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,737评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,283评论 1 287
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,384评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,458评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,467评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,251评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,688评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,980评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,155评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,818评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,492评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,142评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,382评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,020评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,044评论 2 352

推荐阅读更多精彩内容

  • 进程和线程 进程 所有运行中的任务通常对应一个进程,当一个程序进入内存运行时,即变成一个进程.进程是处于运行过程中...
    胜浩_ae28阅读 5,096评论 0 23
  • 一、进程和线程 进程 进程就是一个执行中的程序实例,每个进程都有自己独立的一块内存空间,一个进程中可以有多个线程。...
    阿敏其人阅读 2,611评论 0 13
  • 进程和线程 进程 所有运行中的任务通常对应一个进程,当一个程序进入内存运行时,即变成一个进程.进程是处于运行过程中...
    小徐andorid阅读 2,803评论 3 53
  •   一个任务通常就是一个程序,每个运行中的程序就是一个进程。当一个程序运行时,内部可能包含了多个顺序执行流,每个顺...
    OmaiMoon阅读 1,666评论 0 12
  • 写在前面的话: 这篇博客是我从这里“转载”的,为什么转载两个字加“”呢?因为这绝不是简单的复制粘贴,我花了五六个小...
    SmartSean阅读 4,719评论 12 45