精华内容
下载资源
问答
  • 线程状态转化图一: 线程转换状态图二: 线程的几种状态: 新建状态(New): 用new语句创建的线程处于新建状态,此时它和其它java对象一样,仅仅在堆中分配了内存。 就绪状态(Runnable): 当一个线程对象被...

    线程状态转换图:

    线程状态转化图一:

    在这里插入图片描述

    线程的几种状态:

    • 新建状态(New):
      用new语句创建的线程处于新建状态,此时它和其它java对象一样,仅仅在堆中分配了内存。
    • 就绪状态(Runnable):
      当一个线程对象被创建后,其它线程调用它的start()方法,该线程就进入就绪状态。处于就绪状态的线程已经具备了运行条件,但还没有分配到CPU的使用权。该状态的线程位于可运行线程池中,等待获取CPU使用权;一旦获取到CPU,线程就会进入运行状态并自动调用自己的run方法;
    • 运行状态(Running):
      在运行状态的线程执行自己的run方法,直到调用其它方法(如Interrupt()或者抛异常)而终止/等待某资源而阻塞(锁资源)/完成任务而自然死亡。如果在给定的时间片内没有执行完毕,就会被系统给换下来回到等待执行状态。
    • 阻塞状态(Blocked):
      处于运行状态的线程在某些情况下,如执行了sleep()方法或者等待I/O设备资源,将让出CPU执行权停止自己的运行,进入阻塞状态。在阻塞状态下的线程不能进入就绪队列。只有当引起阻塞的原因消除时,如睡眠时间到,或者等待的I/O设备空闲下来。线程就会转入就绪状态,重新到就绪队列中排队等待。
    阻塞状态分为以下三种:
    • 位于对象等待池中的阻塞状态(Blocked in object’s wait pool):
      当线程处于运行状态时,如果执行了某个对象wait()方法,JVM就会把线程放到这个对象的等待池中,wait方法只有所在的线程只有被唤醒才会重新进入就绪状态。wait()方法带参数,如果时间到则不需要唤醒就会重新进入就绪状态,如果时间未到就被唤醒,则和没有参数的wait方法一样。
    • 位于对象锁池的中阻塞状态(Blocked in object’s lock pool):
      当线程处于运行状态时,试图获得某个对象的同步锁时,如果该对象锁被其它线程占用,JVM就会把这个线程放入对象的锁池中。
    • 其他阻塞状态(Otherwise Blocked):
      当前线程执行了sleep方法,或者调用了其它线程的join()方法,或者发出了I/O请求时,就会进入这个状态;
    • 死亡状态(Dead):
      当线程正常退出run方法/因异常退出run()方法,则该线程结束生命周期。死亡的线程不可再次复生。
    注意:

    wait方法是对象锁的方法,因此调用的时候可以释放锁。而sleep和join是线程的方法,因此阻塞时,是带着锁一起阻塞的,在次运行时,因为本身有锁,所以直接运行。

    线程状态转换图二:

    在这里插入图片描述
    在这里插入图片描述

    展开全文
  • 2. 线程的生命周期new:新建状态Runable:就绪状态/运行状态1.Runable: 就绪状态2.Running:运行状态Blocked:阻塞状态Waiting:等待状态Timed_Waiting:超时等待Terminated:终止状态线程状态转化图状态转化的方法...

    1. 守护线程Daemon

    什么是守护进程?

    守护线程:也叫后台线程,脱于控制终端,用来服务用户线程。例如GM垃圾回收 处理后台的工作的线程

    用户线程:一般用户执行的线程。

    t1.setDaemon(Boolean b);设置线程为守护线程,true 即设置为守护线程 ,false 非守护线程,默认为false

    注意:setDaemon方法只能线程启动之前使用,如果对于一个线程已经死亡或者未启动,那么设置setDaemon会抛出IllegalThreadStateException

    t1.isDaemon() 获取当前线程是否为守护线程。

    守护线程的生命周期

    依赖于用户线程,当用户线程存在,守护线程就会存在,反之,用户线程不存在时,守护线程就会消亡

    为什么会有守护线程?

    守护线程具有自动结束生命周期的特征,当用户线程全部处于终止状态,只剩下守护线程,那么JVM会退出,守护线程自动结束。

    假设GM是非守护线程,主函数main结束工作,但JVM无法结束工作,因为GM还在正常工作。

    什么时候需要守护线程?

    它也叫:后台线程,它可以经常用作执行一些后台任务,当关闭JVM时。希望自动关闭某些线程,则可以将这些线程设置为守护线程。

    2. 线程的生命周期

           State枚举类
             NEW, 线程开始
            
           RUNNABLE,运行和就绪
            
            BLOCKED,阻塞状态
            
            WAITING,
    
            TIMED_WAITING,
    
            TERMINATED;终止
    

    new:新建状态

    使用new’创建线程处于新建状态,此时和其他Java对象一样在Java堆中分配内存,在这种状态下,使用**getState()**可以获取线程的状态。

    Runable:就绪状态/运行状态

    在Java中Runable状态表示正在占用cpu或者等待cpu资源

    在操作系统中将Java的Runable状态分为两个状态:RunableRunning状态。

    1.Runable: 就绪状态

    当线程被创建后,调用**start()**方法,线程就处于就绪状态,当其他条件都满足时,可等待CPU的使用权

    2.Running:运行状态

    在运行状态的线程即得到CPU的使用权,执行线程代码,只能是就绪状态到运行状态

    Blocked:阻塞状态

    指线程因为某些原因放弃CPU使用权eg:缺少资源 IO,锁等,暂停运行,即处于阻塞状态。

    Waiting:等待状态

    当前状态,因为线程对象调用了**wait()**方法,JVM就会将线程方法置入等待池中

    Timed_Waiting:超时等待

    调用**Sleep(long time), join(long time)**会时线程处于睡眠状态

    Terminated:终止状态

    当线程执行 **run()**方法结尾处,进入终止状态,表示该线程的生命周期结束。

    线程状态转化图

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pLPlfJqW-1613568140750)(C:\Users\lenovo\AppData\Roaming\Typora\typora-user-images\image-20210202200057604.png)]

    状态转化的方法

    start() 启动新线程

    启动一个新线程,必须首先调用,且只能调用一次,不能重复调用

    JNI:JAVA NATIVE INTERFICE Java本地方法接口

    run():子线程执行体

    可重复调用,

    不需要单独由对象调用,start方法启动后,会主动调用run方法

    直接调用和通过调用start方法调用run的区别:

    直接调用: 就是普通方法调用,还是在主线程中调用,而非启动一个子线程

    yiled():线程让步

    Thread.yiled()

    暂停当前线程的执行,让步于其他优先级相同或优先级更高等待的线程获取CPU的调度,若没有优先级相同或更高的线程,则此线程会继续运行

    运行——》就绪,等待线程调度,需要注意的是仅仅让出CPU资源

    此方法是static方法,直接调用不需要对象

    public static native void yield();//直接通过类名调用
    

    注意:1. 调用yiled方法的线程进行让步,OS进行调度的下一个线程会不会是让步的线程? 会的,如果没有大于的等于让步线程的优先级,即会调度让步的线程

     2. 为什么yiled是静态的,而不是通过对象调用?因为让步的线程。不一定处于运行状态的线程。
    

    :因为yiled方法只能是正在执行的线程让出CPU的使用权,假设使用线程对象调用yiled方法,会产生错觉让当前的线程对象让步,当前执行的线程不一定是正在运行的线程,必须是占用CPU的线程

    sleep():线程休眠

    Thread.sleep()通过类名调用。

    public static native void sleep(long millis) throws InterruptedException;
    

    特点

    1. 线程休眠,调用的线程会进入休眠。
    2. sleep方法是Thread的静态方法
    3. sleep方法可以传入参数,即会有时间限制,时间结束,继续运行。
    4. 在休眠的millis中可以调用**interrupt()**调用,会抛出 InterruptedException的异常中断睡眠。

    Join(): 线程合并

    t.join()

    t.join(long mills)

    t.join(long mills,int nanos;)

    特点:1. Join方法提供了线程的有序进行,将并发执行合并为串行执行。

    在线程A中线程B调用B.join() , 则A线程会在B线程执行完成后执行,按照B,A执行。

    join方法的本质 还是 wait()方法进入等待状态 waitting 或 Timed_waiting 状态

    1. join() 和 sleep() 一样被中断会抛出***InterruptedException***的异常。不同的是join会 释放锁,因为调用了wait(),而sleep()会一直保持锁
    2. 子线程结束后,子线程的this.notifyAll()会被调用,join()返回,父线程只要获取到锁和CPU,就可以继续运行下去了。
    public final void join() throws InterruptedException {
        join(0);
    }
     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);
        }
    

    Interrupt():中断线程

    t.interrupt()

    t.isInterrupt()判断是否处于中断操作

    特点

    1. 只能中断处于“阻塞状态”的线程(sleep . wait , join等方法进入阻塞状态。)在当前状态调用 interrupt方法,就会抛出 InterruptedException
    2. 若当前线程是正在运行状态,调用interrupt方法,线程还会继续执行,直到发生sleep . wait , join等方法,才会进入到阻塞状态,继而抛出 InterruptedException来中断阻塞状态。原因是每个线程都有个线程标志位 解释:当调用interrupt方法时,会变更该线程的标志位(1个比特位,0->1) 当线程进入阻塞状态时,会判断标志位是否变更,如果变更,立即终止该阻塞状态,随即抛出异常。
    public void interrupt() {
        if (this != Thread.currentThread())
            checkAccess();
    
        synchronized (blockerLock) {
            Interruptible b = blocker;
            if (b != null) {
                interrupt0();           // Just to set the interrupt flag  只是设置中断标志2
                b.interrupt(this);
                return;
            }
        }
        interrupt0();
    }
    private native void interrupt0();
    

    3.线程优先级 Priority

    t1.setPriority();设置线程优先级
    t1.getPriority()获取线程的优先级

        public final static int MIN_PRIORITY = 1;最小优先级
        public final static int NORM_PRIORITY = 5;默认优先级
        public final static int MAX_PRIORITY = 10; 最大优先级
    public final void setPriority(int newPriority) {
        ThreadGroup g;
        checkAccess();
        if (newPriority > MAX_PRIORITY (10|| newPriority < MIN_PRIORITY(1) {先判断优先级的安全
            throw new IllegalArgumentException();
        }
        if((g = getThreadGroup()) != null) {
            if (newPriority > g.getMaxPriority()) {
                newPriority = g.getMaxPriority();
            }
            setPriority0(priority = newPriority);
        }
    }
    
    /**
     * Returns this thread's priority.
     *
     * @return  this thread's priority.
     * @see     #setPriority
     */
    public final int getPriority() {
        return priority;
    }
    

    特点

    1. Java中线程的优先级只是它执行的概率比较大,而优先级低的线程并不是没有机会,概率相对低。
    2. 优先级范围 1-10.线程的。
    3. 线程的优先级具有继承性,若线程A内创建线程B,B的优先级会和A的优先级相等。

    4.线程调度

    用户级调度

    调用相应的方法来改变线程的状态而达到线程的调度

    1. 调整线程的优先级
    2. 线程睡眠:sleep
    3. 线程等待:wait
    4. 线程让步:yield
    5. 线程合并:join

    系统级调度

    FIFO:先进先出算法

    SJF:最短作业优先算法

    RR:时间片轮转法

    HPF:最高优先级算法

    一般OS 基于RR 和 HPF 基于时间片的有限级调度。

    5.并发与并行概念

    并发指的是多个线程操作同一个资源,不是同时执行,而是交替执行,单核cpu,只不过 cpu时间片很短,执行速度很快,看起来同时执行

    并行才是真正的同时执行,多核cpu。每一个线程使用一个单独的cpu运行

    展开全文
  • Java多线程问题总结

    2019-03-20 19:30:00
    一:java线程状态和转化过程 参考:https://www.cnblogs.com/happy-coder/p/6587092.html 线程转化图: 说明:线程共包括以下5种状态。1.新建状态(New) : 线程对象被创建后,就进入了新建状态。例如,Thread ...

    本文大部分为整合内容,会参考不少其他的技术博客。如有问题,请联系我。
     

    一:java线程状态和转化过程

    参考:https://www.cnblogs.com/happy-coder/p/6587092.html

    线程转化图:

    说明
    线程共包括以下5种状态。
    1. 新建状态(New)         : 线程对象被创建后,就进入了新建状态。例如,Thread thread = new Thread()。
    2. 就绪状态(Runnable): 也被称为“可执行状态”。线程对象被创建后,其它线程调用了该对象的start()方法,从而来启动该线程。例如,thread.start()。处于就绪状态的线程,随时可能被CPU调度执行。
    3. 运行状态(Running) : 线程获取CPU权限进行执行。需要注意的是,线程只能从就绪状态进入到运行状态。
    4. 阻塞状态(Blocked)  : 阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。阻塞的情况分三种:
        (01) 等待阻塞 -- 通过调用线程的wait()方法,让线程等待某工作的完成。
        (02) 同步阻塞 -- 线程在获取synchronized同步锁失败(因为锁被其它线程所占用),它会进入同步阻塞状态。
        (03) 其他阻塞 -- 通过调用线程的sleep()或join()或发出了I/O请求时,线程会进入到阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。
    5. 死亡状态(Dead)    : 线程执行完了或者因异常退出了run()方法,该线程结束生命周期。

     

    二:现在有T1、T2、T3三个线程,你怎样保证T2在T1执行完后执行,T3在T2执行完后执行?

    主要考察,Thread的join()方法,看下api解释: 等待该线程终止。

    代码如下:

    public class ThreadTest {
     
        /**
         * @param args
         */
        public static void main(String[] args) {
            // TODO Auto-generated method stub
            Thread thread1 = new Thread(){
                @Override
                public void run() {
                    // TODO Auto-generated method stub
                    
                    try {
                        System.out.println("thread 1 running....");
                        sleep(1000);
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }finally{
                        System.out.println("thread 1 stoped....");
                    }
                    super.run();
                }
            };
            Thread thread2 = new Thread(){
                @Override
                public void run() {...}
            };
            Thread thread3 = new Thread(){
                @Override
                public void run() {...}
            };        
                try {
                    thread1.start();
                    thread1.join();
                    thread2.start();
                    thread2.join();
                    thread3.start();
                    thread3.join();
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
        
        }
     
    }

     

    三. Java 中新的 Lock 接口相对于同步代码块(synchronized block)有什么优势?如果让你实现一个高性能缓存,支持并发读取和单一写入,你如何保证数据完整性。

     常见的锁:

    Synchronized,它就是一个:非公平,悲观,独享,互斥,可重入的重量级锁,又叫内置加锁。
    ReentrantLock,它是一个:默认非公平但可实现公平的,悲观,独享,互斥,可重入,重量级锁。
    ReentrantReadWriteLocK,它是一个,默认非公平但可实现公平的,悲观,写独享,读共享,读写,可重入,重量级锁。

    Synchronized的优缺点:

    synchronized又称为内置锁,如果一个代码块被synchronized修饰了,当一个线程获取了对应的锁,并执行该代码块时,其他线程便只能一直等待,等待获取锁的线程释放锁,而这里获取锁的线程释放锁只会有两种情况:

      1)获取锁的线程执行完了该代码块,然后线程释放对锁的占有;

      2)线程执行发生异常,此时JVM会让线程自动释放锁。

    问题:1-那么如果这个获取锁的线程由于要等待IO或者其他原因(比如调用sleep方法)被阻塞了,但是又没有释放锁,其他线程便只能干巴巴地等待,试想一下,这多么影响程序执行效率。

    2-不是太灵活,实现读写锁用synchronized就实现不了。而lock加锁更灵活,对锁的粒度有更好的控制效果。

    Lock就可以解决上面的两个问题,缺点是,lock必须手动释放锁,而synchronized是不需要手动释放。所以更加危险。

    lock的使用方法:

    Lock lock = ...;
    lock.lock();
    try{
        //处理任务
    }catch(Exception ex){     
    }finally{
        lock.unlock();   //释放锁
    }

    如果让你实现一个高性能缓存,支持并发读取和单一写入,你如何保证数据完整性。

    这个实质上就是读写锁的应用。可以直接使用ReadWriteLock接口实现;

    代码如下:

        public class ReadWriteMap<K,V> {
            private final Map<K,V> map;
            private final ReadWriteLock lock = new ReentrantReadWriteLock();
            private final Lock readLock = lock.readLock();
            private final Lock writeLock = lock.writeLock();
    
            public ReadWriteMap(Map<K,V> map){
                this.map = map;
            }
            //remove,putAll等修改内容的方法类似
            public void put(K key,V value){
                writeLock.lock();
                try{
                    map.put(key,value);
                }finally {
                    writeLock.unlock();
                }
            }
            //只读操作
            public V get(K key){
                readLock.lock();
                try{
                    return map.get(key);
                }finally {
                    readLock.unlock();
                }
            }
        }

    事实上,Java自带的ConcurrentHashMap已经能很好的实现上述的功能。

     

    四:Java 中 wait 和 sleep 方法有什么区别

    对于sleep()方法,我们首先要知道该方法是属于Thread类中的。而wait()方法,则是属于Object类中的。

    sleep()方法导致了程序暂停执行指定的时间,让出cpu该其他线程,但是他的监控状态依然保持者,当指定的时间到了又会自动恢复运行状态。

    在调用sleep()方法的过程中,线程不会释放对象锁。

    而当调用wait()方法的时候,线程会放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象调用notify()方法后本线程才进入对象锁定池准备

    获取对象锁进入运行状态。

     

    五:如何在 Java 中实现一个阻塞队列?

    阻塞队列与普通队列的区别在于,当队列是空的时,从队列中获取元素的操作将会被阻塞,或者当队列是满时,往队列里添加元素的操作会被阻塞。试图从空的阻塞队列中获取元素的线程将会被阻塞,直到其他的线程往空的队列插入新的元素。同样,试图往已满的阻塞队列中添加新元素的线程同样也会被阻塞,直到其他的线程使队列重新变得空闲起来,如从队列中移除一个或者多个元素,或者完全清空队列。

    代码:

    public class MyBlockingQueue {
     
      private List queue = new LinkedList();
      private int  limit = 10;
     
      public MyBlockingQueue(int limit){
        this.limit = limit;
      }
     
     
      public synchronized void enqueue(Object item)
      throws InterruptedException  {
        while(this.queue.size() == this.limit) {
          wait();
        }
        if(this.queue.size() == 0) {
          notifyAll();
        }
        this.queue.add(item);
      }
     
     
      public synchronized Object dequeue()
      throws InterruptedException{
        while(this.queue.size() == 0){
          wait();
        }
        if(this.queue.size() == this.limit){
          notifyAll();
        }
     
        return this.queue.remove(0);
      }
     
    }

    如果用 Java 5 的并发类实现,基本上就是用java已经实现的类,例如BlockingQueue接口实现。

     六 如何在 Java 中编写代码解决生产者消费者问题?

    阻塞队列的使用。

     

    七 写一段死锁代码。你在 Java 中如何解决死锁?

    死锁发生需要必备的四个条件:

    1-互斥条件,资源中必须有一个不能被共享;

    2-至少有一个任务它必须持有一个资源且正在等待获取另外一个当前被别的任务持有的资源;

    3-资源不能被任务抢占;

    4-必须有循环等待。

    死锁代码:

        public static void main(String[] args) throws InterruptedException {
            final DeadLock dd1 = new DeadLock();
            final DeadLock dd2 = new DeadLock();
            Thread t1 = new Thread(new Runnable() {
                public void run() { //首先获得dd1的锁 
            synchronized (dd1)
    { //休眠 try { Thread.sleep(50); synchronized (dd2) { System.out.println(Thread.currentThread().getName() + "线程。。"); } } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }, "t1"); Thread t2 = new Thread(new Runnable() { public void run() { synchronized (dd2) { try { synchronized (dd1) { System.out.println(Thread.currentThread().getName() + "线程。。"); } } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }, "t2"); t1.start(); t2.start(); }

    如何解决死锁(只需破坏到死锁的四个条件之一就可):

    1-加锁顺序,所有的线程都按照顺序获取锁。破坏掉第二条。

    2-加锁时限,在获取锁的时候加一个超时时间,如果超时,则释放已占用的锁。破坏掉第四条。

    3-死锁检测。破坏掉第二条。

     

    八:什么是原子操作?Java 中有哪些原子操作?

    原子操作:不可中断的操作。

    常见的原子类:AtomicInteger、AtomicLong

     

    九:Java 中 volatile 关键字是什么?你如何使用它?它和 Java 中的同步方法有什么区别?

    使用volatile修饰的变量会强制将修改的值立即写入主存,主存中值的更新会使缓存中的值失效。

    volatile具有可见性、有序性,不具备原子性。

    有序性:即程序执行时按照代码书写的先后顺序执行。

    volatile适用场景

    1-适用于对变量的写操作不依赖于当前值,对变量的读取操作不依赖于非volatile变量

    2-适用于读多写少的场景。

    3-可用作状态标志。

    4-JDK中volatie应用:JDK中ConcurrentHashMap的Entry的value和next被声明为volatile,AtomicLong中的value被声明为volatile。AtomicLong通过CAS原理(也可以理解为乐观锁)保证了原子性。

    volatile VS synchronized

    volatile synchronized修饰对象修饰变量修饰方法或代码段可见性11有序性11原子性01线程阻塞01对比这个表格,你会不会觉得synchronized完胜volatile,答案是否定的,volatile不会让线程阻塞,响应速度比synchronized高,这是它的优点。

     

    十. 什么是竞态条件?你如何发现并解决竞态条件?

    竞争状态:可能发生在临界状态内的特殊状态。临界状态是被多个线程执行的一段代码,在这个代码段中,线程的执行顺序影响临界状态的并发执行结果。

    如何解决竞争状态,基本原则有三种方式:

    1-减少锁的持有时间;

    2-降低锁的请求频率;

    3-使用带有协调机制的独占锁。例如读写锁

    具体方式:

    1-缩小锁的范围;

    2-锁分解(减少锁的粒度),如果一个锁需要保护多个相互独立的状态变量,那么可以将这个锁分解多个锁。并且每个所只保护一个变量。从而提高可伸缩性,并最终降低每个锁被请求的频率;

    3-锁分段,例如,ConcurrentHashMap的实现中使用了一个包含16个锁的数组,每个锁保护所有散列桶的1/16。

    4-避免热点域,将一些反复计算的结果缓存起来,类似于批处理。

    5-使用带有协调机制的独占锁,例如,读写锁。

     

     十一. 在 Java 中你如何转储线程(thread dump)?如何分析它?

    线程的三种转储方式:

    1-unix:kill -3,输出到/proc//fd/1

    2-windows: ctrl+break

    3-jstack: jstack >> 输出文件

    转储的内容包含:

    1-线程的名字;  2-damon,守护线程;  3-prio线程的优先级;   4-tid,java的线程id;

    5-nid,线程本地标识;  6-线程的运行状态;  7-当前运行的线程在堆中的地址范围。

    可以参考:https://blog.csdn.net/agzhchren/article/details/53727082

     

    十二. 既然 start() 方法会调用 run() 方法,为什么我们调用 start() 方法,而不直接调用 run() 方法?

    当你调用 start() 方法时,它会新建一个线程然后执行 run() 方法中的代码,真正实现了多线程运行。如果直接调用 run() 方法,并不会创建新线程,方法中的代码会在当前调用者的线程中执行。

     

    十三. Java 中你如何唤醒阻塞线程?

    这是有关线程的一个很狡猾的问题。有很多原因会导致阻塞,如果是 IO 阻塞,我认为没有方式可以中断线程(如果有的话请告诉我)。

    另一方面,如果线程阻塞是由于调用了 wait()sleep() 或 join() 方法,你可以中断线程,通过抛出 InterruptedException 异常来唤醒该线程。

    notify()和notifyAll()方法实现唤醒阻塞线程。

     

    十四. Java 中 CyclicBarriar 和 CountdownLatch 有什么区别?

    参考:https://blog.csdn.net/liangyihuai/article/details/83106584

    CountDownLatch计数为0的时候就可以打开门闩了。

    Cyclic Barrier表示循环的障碍物。

    两个类都含有这一个意思:对应的线程都完成工作之后再进行下一步动作,也就是大家都准备好之后再进行下一步。

    然而两者最大的区别是,进行下一步动作的动作实施者是不一样的。这里的“动作实施者”有两种,一种是主线程(即执行main函数),对于CountDownLatch,当计数为0的时候,下一步的动作实施者是main函数;

    另一种是执行任务的其他线程,后面叫这种线程为“其他线程”,区分于主线程。对于CyclicBarrier,下一步动作实施者是“其他线程”。

    import java.util.Random;
    import java.util.concurrent.CountDownLatch;
    
    public class CountDownLatchTest {
    
        public static void main(String[] args) throws InterruptedException {
            CountDownLatch latch = new CountDownLatch(4);
            for(int i = 0; i < latch.getCount(); i++){
                new Thread(new MyThread(latch), "player"+i).start();
            }
            System.out.println("正在等待所有玩家准备好");
            latch.await();
            System.out.println("开始游戏");
        }
    
        private static class MyThread implements Runnable{
            private CountDownLatch latch ;
    
            public MyThread(CountDownLatch latch){
                this.latch = latch;
            }
    
            @Override
            public void run() {
                try {
                    Random rand = new Random();
                    int randomNum = rand.nextInt((3000 - 1000) + 1) + 1000;//产生1000到3000之间的随机整数
                    Thread.sleep(randomNum);
                    System.out.println(Thread.currentThread().getName()+" 已经准备好了, 所使用的时间为 "+((double)randomNum/1000)+"s");
                    latch.countDown();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
    
            }
        }
    }
    运行结果:
    正在等待所有玩家准备好
    player0 已经准备好了, 所使用的时间为 1.235s
    player2 已经准备好了, 所使用的时间为 1.279s
    player3 已经准备好了, 所使用的时间为 1.358s
    player1 已经准备好了, 所使用的时间为 2.583s
    开始游戏
    package com.huai.thread;
    
    import java.util.Random;
    import java.util.concurrent.BrokenBarrierException;
    import java.util.concurrent.CyclicBarrier;
    
    public class CyclicBarrierTest {
        public static void main(String[] args) {
            CyclicBarrier barrier = new CyclicBarrier(3);
            for(int i = 0; i < barrier.getParties(); i++){
                new Thread(new MyRunnable(barrier), "队友"+i).start();
            }
            System.out.println("main function is finished.");
        }
    
    
        private static class MyRunnable implements Runnable{
            private CyclicBarrier barrier;
    
            public MyRunnable(CyclicBarrier barrier){
                this.barrier = barrier;
            }
    
            @Override
            public void run() {
                for(int i = 0; i < 3; i++) {
                    try {
                        Random rand = new Random();
                        int randomNum = rand.nextInt((3000 - 1000) + 1) + 1000;//产生1000到3000之间的随机整数
                        Thread.sleep(randomNum);
                        System.out.println(Thread.currentThread().getName() + ", 通过了第"+i+"个障碍物, 使用了 "+((double)randomNum/1000)+"s");
                        this.barrier.await();   //Waits until all parties have invoked await on this barrier.等待所有的成员都完成这个动作
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } catch (BrokenBarrierException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
    运行结果:
    main function is finished.
    队友1, 通过了第0个障碍物, 使用了 1.432s
    队友0, 通过了第0个障碍物, 使用了 1.465s
    队友2, 通过了第0个障碍物, 使用了 2.26s
    队友1, 通过了第1个障碍物, 使用了 1.542s
    队友0, 通过了第1个障碍物, 使用了 2.154s
    队友2, 通过了第1个障碍物, 使用了 2.556s
    队友1, 通过了第2个障碍物, 使用了 1.426s
    队友2, 通过了第2个障碍物, 使用了 2.603s
    队友0, 通过了第2个障碍物, 使用了 2.784s

     

    十五. 你在多线程环境中遇到的最多的问题是什么?你如何解决的?

    内存干扰、竞态条件、死锁、活锁、线程饥饿是多线程和并发编程中比较有代表性的问题。这类问题无休无止,而且难于定位和调试。
    这是基于经验给出的 Java 面试题。你可以看看Java 并发实战课程来了解现实生活中高性能多线程应用所面临的问题。

     

    十六.java守护线程和用户线程的区别

    守护线程:指在程序运行的时候在后台提供一种通用服务的线程,比如垃圾回收线程就是一个很称职的守护者,并且这种线程并不属于程序中不可或缺的部分。因 此,当所有的非守护线程结束时,程序也就终止了,同时会杀死进程中的所有守护线程。反过来说,只要任何非守护线程还在运行,程序就不会终止。
    用户线程:自己创建的线程。比如:new Thread。这就是自己创建了一个线程。
    守护线程和用户线程的没啥本质的区别:唯一的不同之处就在于虚拟机的离开:如果用户线程已经全部退出运行了,只剩下守护线程存在了,虚拟机也就退出了。 因为没有了被守护者,守护线程也就没有工作可做了,也就没有继续运行程序的必要了。

    十七 线程和进程之间的区别和联系

    进程:是系统进行资源分配和调度管理的一个可并发执行的基本单位。

    线程:是进程中实施调度和分派的基本单位。

    之间的关系:

            1、一个进程可以有多个线程,但至少有一个线程;而一个线程只能在一个进程的地址空间内活动。
            2、资源分配给进程,同一个进程的所有线程共享该进程所有资源。
            3、CPU分配给线程,即真正在处理器运行的是线程。
            4、线程在执行过程中需要协作同步,不同进程的线程间要利用消息通信的办法实现同步。
    进程间的通信方式:1-消息传递;2-共享存储;3-管道通讯。
     

    十八 多线程的上下文切换

    CPU通过时间片分配算法来循环执行任务,当前任务执行一个时间片后会切换到下一个任务。但是,在切换前会保存上一个任务的状态,以便下次切换回这个任务时,可以再次加载这个任务的状态,从任务保存到再加载的过程就是一次上下文切换

     

    十九 一些基本概念

    死锁:多线程中最差的一种情况,多个线程相互占用对方的资源的锁,而又相互等对方释放锁,此时若无外力干预。会一直处于阻塞状态。
    活锁:和死锁相反,活锁是占用锁但是释放。同样也是没有执行任务。多线程中出现了相互谦让,都主动将资源释放给别的线程使用。
    饥饿:线程有优先级别之分,优先级别高的会占用级别低的部分。这样会造成优先级别低的线程无法得到执行。
    无锁:对共享资源没有进行加锁。
    线程组不是线程安全的。
     

    二十 java线程调度算法:

    1-抢占式调度:容易引起线程饥饿。
    2-协同式调度:容易出现阻塞。

     

    二十一 为什么使用Excutor框架比直接创建线程要好

    1.new Thread()的缺点

    (1) 每次new Thread()耗费性能 
    (2) 调用new Thread()创建的线程缺乏管理,被称为野线程,而且可以无限制创建,之间相互竞争,会导致过多占用系统资源,使系统瘫痪
    (3) 不利于扩展,比如定时执行、定期执行

    2.采用线程池的优点

    (1) 重用存在的线程,减少对象创建、销毁的开销,性能佳 
    (2) 可有效控制最大并发线程数,提高系统资源的使用率,同时避免过多资源竞争,避免堵塞 
    (3) 提供定时执行、定期执行、单线程、并发数控制等功能

     

    二十二 Exector、ExectorService、Exectors的区别和联系

    Exector是一个大的接口

    public interface Executor {
        void execute(Runnable command);
    }

    ExectorService是继承Exector的接口

    public interface ExecutorService extends Executor {
        void shutdown();
        <T> Future<T> submit(Callable<T> task);
        <T> Future<T> submit(Runnable task, T result);
        <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException;
    }

    Executors是一个工具类,类似于Collections

    public class Executors {
        public static ExecutorService newFixedThreadPool(int nThreads) {
            return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>());
            }
             
         public static ExecutorService newCachedThreadPool() {
            return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>());
            }
    }

     

     

    转载于:https://www.cnblogs.com/parent-absent-son/p/10567066.html

    展开全文
  • Java多线程升级篇(番外) 今天是java升级篇内容的番外篇。之前的升级篇中,我们总结了两个关键字,两个通信方法。这一篇讲一点番外的内容,没有主题,内容很杂,但是是对升级篇内容的补充。对理解多线程有更多的...

    Java多线程升级篇(番外)

    今天是java升级篇内容的番外篇。之前的升级篇中,我们总结了两个关键字,两个通信方法。这一篇讲一点番外的内容,没有主题,内容很杂,但是是对升级篇内容的补充。对理解多线程有更多的帮助。

    Java线程的状态转化

    明确知道java的线程的状态至关重要,今天总结一下java的线程的状态以及能够改变状态的所有的方法以及各种状态之间的转换。
    如下图所示:
    在这里插入图片描述
    新建一个线程对象以后,再调用他的start方法,系统就会为此线程分配CPU资源,使其处于Runnable(可运行)状态,相当于是预备状态。如果该线程抢占到CPU资源,此线程就会处于Running(运行)状态。
    可运行状态和运行状态可相互切换,因为有可能线程运行一段时间后,有其他高级优先级的线程抢占了CPU资源,此时此线程就从Running状态变成Runnable状态。
    线程进入可运行状态大致可以分为五种情况:
    1、调用sleep方法后经过的时间超过了指定的休眠时间;
    2、线程调用的阻塞IO已经返回,阻塞方法执行完毕;
    3、线程成功的获得了试图同步的监视器;
    4、线程正在等待某个通知,其他线程发出了通知;
    5、挂起状态的线程调用了resume恢复方法。
    Blocked是阻塞的意思,例如遇到了一个IO操作,此时CPU处于空闲状态,可能会转而把CPU时间片分配给其他线程,这时也可以称为“暂停状态”。Blocked状态结束以后,进入到可运行状态,等待系统的重新分配资源。
    线程出现阻塞的情况大致可以分为五种情况:
    1、线程调用sleep方法,主动放弃占用的处理器资源;
    2、线程调用了阻塞式IO方法,在该方法返回前,该线程被阻塞;
    3、线程试图获得一个同步监视器,但该同步监视器正被其他线程所持有;
    4、线程等待某个通知;
    5、程序调用了suspend方法将该线程挂起。此方法容易导致死锁,尽量避免使用该方法。
    Run方法运行结束以后进入销毁阶段,整个线程执行完毕。

    每个锁对象都有两个队列,一个是就绪队列,一个是阻塞队列。就绪队列存储了将要获得锁的线程,阻塞队列存储了被阻塞的线程。
    一个线程被唤醒以后,才会进入就绪队列,等待CPU的调度;反之,一个线程被wait后,就会进入到阻塞队列,等待下一次被唤醒。

    实现每一个线程自己的共享变量

    所有线程使用同一个变量可以设置为public static,但是对于每一个线程都有自己的共享变量需要有一些特殊的实现方法。

    ThreadLocal类

    public class LoginClass {
    	public static ThreadLocal t=new ThreadLocal();
    }
    public class New_thread extends Thread{
    	public void run() {
    		try {
    			for(int i=0;i<10;i++) {
    				LoginClass.t.set("New_tread "+(i+1));
    				System.out.println("New_thread get value "
    				+LoginClass.t.get());
    				Thread.sleep(1000);
    			}
    		}catch(InterruptedException e) {
    			e.printStackTrace();
    		}
    	}
    }
    public class Another_thread extends Thread{
    	public void run() {
    		try {
    			for(int i=0;i<10;i++) {
    				LoginClass.t.set("Another_thread "+(i+1));
    				System.out.println("Another_thread get value "
    				+LoginClass.t.get());
    				Thread.sleep(1000);
    			}
    		}catch(InterruptedException e) {
    			e.printStackTrace();
    		}
    	}
    }
    public class Test_thread{
    	public static void main(String[] args){
    		try {
    			New_thread t1=new New_thread();
    			Another_thread t2=new Another_thread();
    			t1.start();t2.start();
    			for(int i=0;i<10;i++) {
    				LoginClass.t.set("main "+(i+1));
    				System.out.println("main get value "+LoginClass.t.get());
    				Thread.sleep(1000);
    			}
    		}catch(InterruptedException e) {
    			e.printStackTrace();
    		}
    	}
    }
    

    运行结果:
    在这里插入图片描述
    每一个线程都有一个自己的值,每一次运行的时候,得到的是自己的值,不会对别的线程造成影响。

    子线程从父线程中得到数据

    有时候我们需要子线程能够得到父线程的数值,甚至对数值的结果进行修改,在这种情况下,我们就可以使用InheritableThreadLocal类。下面我们尝试使用InheritableThreadLocal类并且尝试让子线程修改父线程的值。
    示例代码如下:

    public class LoginClass {
    	public static InheritableThreadLocal t=new InheritableThreadLocal();
    }
    public class New_thread extends Thread{
    	public void run() {
    		try {
    			for(int i=0;i<3;i++) {
    				System.out.println("New_thread get value "
    				+LoginClass.t.get());
    				Thread.sleep(1000);
    			}
    		}catch(InterruptedException e) {
    			e.printStackTrace();
    		}
    	}
    }
    public class Test_thread{
    	public static void main(String[] args){
    		try {
    			LoginClass.t.set("main set the value");
    			for(int i=0;i<3;i++) {
    				System.out.println("main get value "+LoginClass.t.get());
    				Thread.sleep(1000);
    			}
    			New_thread t1=new New_thread();
    			t1.start();
    			t1.join();
    			for(int i=0;i<3;i++) {
    				System.out.println("main get value "+LoginClass.t.get());
    				Thread.sleep(1000);
    			}
    		}catch(InterruptedException e) {
    			e.printStackTrace();
    		}
    	}
    }
    

    运行结果:
    在这里插入图片描述
    从上述代码可以看出,子线程已经成功的获得父线程的内容,现在,让我们尝试让子线程修改父线程的值,并且重新让父线程得到值,看看结果究竟如何。我们再修改一下代码,如下所示:

    public class New_thread extends Thread{
    	public void run() {
    		try {
    			LoginClass.tx.set("New_thread value");
    			for(int i=0;i<3;i++) {
    				System.out.println("New_thread get value "
    				+LoginClass.tx.get());
    				Thread.sleep(1000);
    			}
    		}catch(InterruptedException e) {
    			e.printStackTrace();
    		}
    	}
    }
    

    运行结果:
    在这里插入图片描述
    由此可见,子线程可以获得父线程的内容,也可以修改父线程的内容,但是,修改的内容不会影响到父线程的内容。换一句话说,爸爸借你的东西你可以随便玩,但是还给爸爸的时候必须是原来的样子,有点你爸爸还是你爸爸的既视感,哈哈。

    到目前为止,关于线程间通信的内容已经基本结束了,升级篇一共有四篇加上一篇番外篇。学习线程的时候就觉得内容实在是太多了,现在总结起来更是多,不得不一边看着书,一边整理自己的思路。而且线程的内容并不容易理解,一旦有多个线程工作,就需要花费很长的时间理清线程之间的关系,以及把握好线程的通信内容,否则,就会有很多的隐藏风险。
    线程一直是面试中的重点,也是实际操作中必不可少的一部分,甚至没有线程就没有现在的众多的平台软件。
    到此为止,java线程的内容先休息一段时间,换换口味,写一段时间的java虚拟机的内容。八月份即将到来,就准备一下,争取每周两篇的更新频率吧。

    展开全文
  • java多线程(三)

    2017-07-08 23:30:16
    线程状态转化图 [img]http://dl2.iteye.com/upload/attachment/0125/8008/d4811fc5-e155-3be6-b55a-2b7e3c4e568f.jpg[/img] 这个图我觉得应该是市面比较通俗易懂的多线程状态转换图,wait()和sleep()的区别...
  • java多线程的学习总结

    2018-02-17 23:16:42
    首先看一张关于多线程转载于http://blog.csdn.net/evankaka 看完之后会对多线程的流程有个大概的理解,稍后的学习也可以重新查看该,进行加深理解 关于线程和进程的概念 线程和进程的状态和...
  • JAVA 线程状态转化线程状态图说明:线程共包括以下5种状态。1. 新建状态(New) : 线程对象被创建后,就进入了新建状态。例如,Thread thread = new Thread()。2. 就绪状态(Runnable): 也被称为“可执行状态”。...
  • 线程的生命周期,对于多线程开发是非常重要的,也是面试中常见的问题。下面我们就梳理一下线程的生命周期,快速理解掌握。线程生命周期总览这是线程生命周期的总览:在中可以看到一个线程的所有状态,我们先了解...
  • 线程的生命周期,对于多线程开发是非常重要的,也是面试中常见的问题。下面我们就梳理一下线程的生命周期,快速理解掌握。线程生命周期总览这是线程生命周期的总览:在中可以看到一个线程的所有状态,我们先了解...
  • 线程的生命周期,对于多线程开发是非常重要的,也是面试中常见的问题。下面我们就梳理一下线程的生命周期,快速理解掌握。线程生命周期总览这是线程生命周期的总览:在中可以看到一个线程的...
  • 线程状态转化借用网上的一幅: 说明: 线程一共分为5种状态新建状态(new)线程对象被创建后,就进入了新建状态,例如:Thread t = new Thread();就绪状态(Runnable)线程对象被创建后,其它线程调用了该对象的...
  •  说到线程给我的第一感觉是并发执行,其原理就是操作系统中的线程知识。然后用Java来实现其上层应用。...各个状态之间可以一定的条件相互转化,如所示:  Start() sleep() wait()  Sleep()
  • 一起走进多线程(二)

    2020-07-08 14:55:41
    这是本人的java多线程系列的第二篇博客,如果有什么疑问欢迎大家评论区提出讨论。 初识Java并发编程 首先了解下线程的生命周期:这里借用了https://blog.csdn.net/pange1991/article/details/53860651中的 线程...
  • 目前Java的面试中,可以说多线程是必问的。在我们学习Java时,这也是非常重要的一...线程状态转化图11.i–与System.out.println()的异常12.如何知道代码段被哪个线程调用?13.线程活动状态?14.sleep()方法15.如何优雅
  • JAVA基础课程讲义

    2017-08-30 23:39:14
    JAVA中如何实现多线程(重点!!) 168 通过继承Thread类实现多线程 168 通过Runnable接口实现多线程 169 线程状态和sleep/yield/join/stop/destroy方法 170 新生状态 170 就绪状态 170 运行状态 170 死亡状态 170 ...
  • 疯狂JAVA讲义

    2014-10-17 13:35:01
    2.2.7 状态 36 2.3 Java的面向对象特征 36 2.3.1 一切都是对象 37 2.3.2 类和对象 37 2.4 本章小结 37 第3章 数据类型和运算符 38 3.1 注释 39 3.1.1 单行注释和多行注释 39 3.1.2 文档注释 40 学生...
  • java范例开发大全

    2013-03-08 20:06:54
    13.1 多线程的五种基本状态 405 实例222 启动线程 405 实例223 参赛者的比赛生活(线程休眠唤醒) 407 实例224 资源搜索并下载(线程等待和通报) 410 实例225 模拟淘宝购物买卖双方交易问题 412 实例226 携子之手 ...
  • java初学者必看

    热门讨论 2012-02-24 16:07:34
    11.2.3 线程状态转换 11.2.4 等待线程结束 11.3 线程调度 11.4 线程同步 11.4.1 同步概念 11.4.2 同步格式 11.4.3 同步应用 11.5 线程通信 11.5.1 生产者/消费者 11.5.2 共享队列 11.5.3 运行生产者/消费...
  • Java程序员面试宝典pdf

    热门讨论 2013-02-21 13:06:13
    8.2 Java中的多线程编程 136 面试题080 如何让一个类成为线程类 136 面试题081 解释Runnable接口与Thread类的区别 137 面试题082 如何启动一个线程 138 面试题083 如何使用sychronized来让线程同步 139 面试题084 ...
  • 线程状态:谈Java线程状态(熟悉掌握线程状态转化图) 线程安全 原因: 多个线程对共享变量的操作(代码层面) 原子性、可见性、有序性(底层原因 ) 解决方式: 共享变量加锁(synchronized加锁 、 Lock体系) ...
  • java范例开发大全源代码

    热门讨论 2011-10-30 23:31:51
     实例159 书展(抽象工厂模式) 246  实例160 汽车适配器(Adapter适配器模式) 248  8.4 垃圾回收 250  实例161 垃圾回收的机制 250  第9章 面向对象的四大特征(教学视频:65分钟) 252  9.1 ...
  • JAVA面试题最全集

    2010-03-13 13:09:10
    谈谈java多线程 23.谈谈文件加密技术 24.软件开发生命周期 25.路由协议种类及特点 26.java的awt和swing组件的GUI设计的关键 27.对于java流的认识 28.简单描述一下awt与swing区别。 29.简述java编程中事件处理...
  • Java范例开发大全 (源程序)

    热门讨论 2011-04-27 07:47:22
    第1篇 Java编程基础  第1章 Java开发环境的搭建(教学视频:9分钟)... 13.1 多线程的五种基本状态 405  实例222 启动线程 405  实例223 参赛者的比赛生活(线程休眠唤醒) 407  实例224 资源搜索并下载(线程...
  • java范例开发大全(pdf&源码)

    热门讨论 2013-07-04 13:04:40
    13.1 多线程的五种基本状态 405 实例222 启动线程 405 实例223 参赛者的比赛生活(线程休眠唤醒) 407 实例224 资源搜索并下载(线程等待和通报) 410 实例225 模拟淘宝购物买卖双方交易问题 412 实例226 携子之手 ...
  • 8.2 Java中的多线程编程 136 面试题080 如何让一个类成为线程类 136 面试题081 解释Runnable接口与Thread类的区别 137 面试题082 如何启动一个线程 138 面试题083 如何使用sychronized来让线程同步 139 面试题...
  • Java范例开发大全(全书源程序)

    热门讨论 2013-04-05 11:50:26
    13.1 多线程的五种基本状态 405 实例222 启动线程 405 实例223 参赛者的比赛生活(线程休眠唤醒) 407 实例224 资源搜索并下载(线程等待和通报) 410 实例225 模拟淘宝购物买卖双方交易问题 412 实例226 携子...
  • 其他部分,几乎融合了自己开源的大部分封装库,比如,状态管理,视频库,轮播,幸运大转盘[老虎机],画廊,自定义进度条,图片缩放,线程池 3.2 相关特性说明 侧滑菜单:DrawerLayout+NavigationView 基本遵循...
  • startActivities : 启动个 Activity startHomeActivity : 回到桌面 getActivityList : 获取 Activity 栈链表 getLauncherActivity : 获取启动项 Activity getTopActivity : 获取栈顶 Activity ...
  • 线程池正是为了解决多线程效率低的问题而产生的,他使得线程可以被复用,就是线程执行结束后不被销毁,而是可以继续执行其他任务。(这里可以用tomcat做例子进行思考) 很多人想问,线程池听起来高大上,但在实际...

空空如也

空空如也

1 2 3
收藏数 41
精华内容 16
关键字:

java多线程状态转化图

java 订阅