java基础多线程

Posted rainouyang

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java基础多线程相关的知识,希望对你有一定的参考价值。

  

 

线程的创建

方式1:继承Java.lang.Thread类,并覆盖run() 方法

技术图片
package com.demo.Thread;

public class ThreadDemo01 extends Thread{
    public static void main(String[] args) {
        new ThreadDemo01().start();
    }
    @Override
    public void run() {
        int count = 50;
        while(count>0) {
            System.out.println(count--);
        }
    }    
}
继承Thread

方式2:实现Java.lang.Runnable接口,并实现run() 方法

技术图片
package com.demo.Thread;

public class ThreadDemo01 {
    public static void main(String[] args) {
        Thread th = new Thread(new Runnable() {
            public void run() {
                int count = 50;
                while(count>0) {
                    System.out.println(count--);
                }
            }
        });
        
        th.start();
    }
}
实现run()接口

线程的生命周期

新生状态 (new)

  • new关键字建立一个线程对象后,该线程对象就处于新生状态。
  • 处于新生状态的线程有自己的内存空间,通过调用start进入就绪状态

就绪状态(Runnable)

  • 处于就绪状态线程具备了运行条件,但还没分配到CPU,处于线程就绪队列,等待系统为其分配CPU
  • 当系统选定一个等待执行的线程后,它就会从就绪状态进入执行状态,该动作称之为“cpu调度”。

运行状态(Running)

  • 在运行状态的线程执行自己的run方法中代码,直到等待某资源而阻塞或完成任务而死亡。
  • 如果在给定的时间片内没有执行结束,就会被系统给换下来回到等待执行状态。

阻塞状态(Blocked)

阻塞状态是线程因为某种原因放弃CPU使用权限,暂时停止运行。直到线程进入就绪状态,才有机会进入运行状态。阻塞的三种情况

  1. 等待阻塞:通过调用线程的wait()方法,让线程等待某工作的完成。
  2. 同步阻塞:线程在获取synchronized同步锁失败(因为锁被其他线程占用),它会进入同步阻塞状态。
  3. 其他阻塞:通过调用线程的sleep()或join()或发出了I/O请求时,线程会进入到阻塞状态。当sleep()状态超时、join()等待线程终止或超时、或者I/O处理完毕时,线程重新转入就绪状态。

死亡状态:

死亡状态是线程生命周期中的最后一个阶段。线程死亡的原因有三个

  1. 正常运行的线程完成了它的全部工作;
  2. 线程被强制性地终止,如通过执行stop方法来终止一个线程--容易导致死锁,不推荐。
  3. 线程抛出未捕获的异常

 技术图片

 

 

Thread类常用方法

 

String

getName()

返回该线程的名称。

static void

sleep(long millis)

在指定的毫秒数内让线程休眠

void

start()  

使该线程开始执行

Void

join()

等待该线程终止。

static void

yield()

暂停当前正在执行的线程对象,并执行其他线程

static Thread

currentThread()

返回对当前正在执行的线程对象

 

 

sleep()方法

sleep()使当前线程进入停滞状态(阻塞当前线程),让出CUP的使用、目的是不让当前线程独自霸占该进程所获的CPU资源,以留一定时间给其他线程执行的机会;
   sleep()是Thread类的Static(静态)的方法;因此他不能改变对象的机锁,所以当在一个Synchronized块中调用Sleep()方法是,线程虽然休眠了,但是对象的机锁并木有被释放,其他线程无法访问这个对象(即使睡着也持有对象锁)。
  在sleep()休眠时间期满后,该线程不一定会立即执行,这是因为其它线程可能正在运行而且没有被调度为放弃执行,除非此线程具有更高的优先级。

wait()方法

wait()方法是Object类里的方法;当一个线程执行到wait()方法时,它就进入到一个和该对象相关的等待池中,同时失去(释放)了对象的机锁(暂时失去机锁,wait(long timeout)超时时间到后还需要返还对象锁);其他线程可以访问;
  wait()使用notify或者notifyAlll或者指定睡眠时间来唤醒当前等待池中的线程。
  wiat()必须放在synchronized block中,否则会在program runtime时扔出”java.lang.IllegalMonitorStateException“异常。

线程礼让Yield

static void yield ()

让当前正在执行线程暂停,不是阻塞线程,而是将线程转入就绪状态

如果调用了yield方法之后,没有其他等待执行的线程,这个时候当前线程就会马上恢复执行!

技术图片
package com.demo.Thread;

public class ThreadDemo01 {
    public static void main(String[] args) {
        new Thread(new YieldDemo()).start();
        
        new Thread(new Runnable() {
            @Override
            public void run() {
                for(int i = 1;i <= 50;i++) {
                    System.out.println("Thread:"+i);
                }
            }
        }).start();
        
    }
}

class YieldDemo implements Runnable{
    public void run() {    
        for(int i = 1;i <= 50;i++) {
            System.out.println("YieldDemo:"+i);
            if(i%5==0) {
                System.out.println("###########礼让###########");
                Thread.yield();
            }
            
        }
    }
}
礼让

 

线程插队Join

void join() 

合并线程,等待此线程执行完毕后,在执行其他线程,其他线程进入阻塞状态

为什么要用join()方法

在很多情况下,主线程生成并起动了子线程,如果子线程里要进行大量的耗时的运算,主线程往往将于子线程之前结束,但是如果主线程处理完其他的事务后,需要用到子线程的处理结果,也就是主线程需要等待子线程执行完成之后再结束,这个时候就要用到join()方法了

 

观察线程状态变化

public Thread.State  getState()

返回该线程的状态。 该方法用于监视系统状态,不用于同步控制。

枚举 Thread.State

线程状态。线程可以处于下列状态之一:

NEW

  尚未启动的线程处于这种状态

RUNNABLE

  可运行线程的线程状态,就绪状态和运行状态

BLOCKED

  受阻塞并等待某个监视器锁的线程处于这种状态

WAITING

  无限期地等待另一个线程来执行某一特定操作的线程处于这种状态,某一线程因为调用下列方法之一而处于等待状态。

  •   有时间的wait
  •   有时间的.join

TIMED_WAITING

  等待另一个线程来执行取决于指定等待时间的操作的线程处于这种状态,某一线程因为调用以下带有指定正等待时间的方法之一而处于定时等待状态:

  •      sleep
  •   有时间的 wait
  •   有时间的join

TERMINATED

  线程结束死亡

 

技术图片
package com.demo.Thread;

public class ThreadDemo01 {
    public static void main(String[] args) {
        
         Thread t = new Thread(()-> {
             // 每500毫秒阻塞一次
             for(int i = 0;i < 5;i++) {
                 try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
             }
             
         });
        
         // 新生状态
         System.out.println("状态:"+t.getState());
         
         // 启动线程
         t.start();
         
        // 每500毫秒查看线程的状态
         for(int i = 0;i < 8;i++) {
            try {
                 System.out.println("状态:"+t.getState());
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
         }
    /*     
         控制台输出
         状态:NEW
         状态:RUNNABLE
         状态:TIMED_WAITING
         状态:TIMED_WAITING
         状态:TIMED_WAITING
         状态:TIMED_WAITING
         状态:RUNNABLE
         状态:TERMINATED
         状态:TERMINATED
    */
    }
}
观察线程的状态变化

 

 

线程优先级

Thread类 提供三种优先级常量

技术图片

线程优先级1-10,

MAX_PRIORITY 10

MIN_PRIORITY 1

NORM_PRIORITY 5

优先级越大代表被cpu优先执行的概率越大

技术图片
package com.demo.Thread;

public class ThreadDemo01 {
    public static void main(String[] args) {
        // 设置线程的名字
        Thread old = new Thread(new MyThread(),"老人");
        Thread child = new Thread(new MyThread(),"小孩");
        Thread woman = new Thread(new MyThread(),"女人");
        Thread man = new Thread(new MyThread(),"男人");
        // 设置优先级(必须在启动线程之前设置优先级)
        old.setPriority(Thread.MAX_PRIORITY);
        child.setPriority(Thread.MAX_PRIORITY);
        woman.setPriority(Thread.MIN_PRIORITY);
        man.setPriority(Thread.MIN_PRIORITY);
        // 启动线程
        man.start();
        woman.start();
        child.start();
        old.start();
        
//        old.start();
//        child.start();
//        woman.start();
//        man.start();
    }
}

class MyThread implements Runnable{
    public void run() {
        // 输出当前线程的名字和优先级
        System.out.println(Thread.currentThread().getName()+"------>"+Thread.currentThread().getPriority());
    }
}
测试线程优先级

 

 守护线程

守护线程是给其他程提供服务用的,比如gc就是守护线程。

那么如果所有的非守护线程都执行完毕后,那么守护线程和JVM就会一起结束(因为没有可以守护的线程,当然守护线程就结束)

 

主线程结束后,普通线程继续执行。

技术图片
package com.demo.Thread;

public class ThreadDemo01 {
    public static void main(String[] args) {
        Thread t = new Thread(()-> {
            // 无限循环
            while(true) {
                try {
                    System.out.println("我是普通线程");
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                
            }
        });

        t.start();
        
        for(int i = 1;i <= 5;i++) {
            try {
                System.out.println("main线程 time="+i);
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

//        控制台结果
//        main线程 time=1
//        我是普通线程
//        main线程 time=2
//        我是普通线程
//        我是普通线程
//        main线程 time=3
//        main线程 time=4
//        我是普通线程
//        我是普通线程
//        main线程 time=5
//        我是普通线程
//        我是普通线程
//        我是普通线程
//        我是普通线程
//        ...
//        ... 
//        ... 
//        ... 一直输出下去
    }
}
普通线程

 

设置为守护线程的时候,非守护线程全部结束后,守护线程和jVM一并结束

技术图片
package com.demo.Thread;

public class ThreadDemo01 {
    public static void main(String[] args) {
        Thread t = new Thread(()-> {
            // 无限循环
            while(true) {
                try {
                    System.out.println("我是普通线程");
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                
            }
        });
        // 设置为守护线程,如果非主线程全部结束后,守护线程将会结束
        t.setDaemon(true);
        t.start();
        
        for(int i = 1;i <= 5;i++) {
            try {
                System.out.println("main线程 time="+i);
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        
//        控制台打印
//        main线程 time=1
//        我是普通线程
//        我是普通线程
//        main线程 time=2
//        我是普通线程
//        main线程 time=3
//        我是普通线程
//        main线程 time=4
//        我是普通线程
//        main线程 time=5
//        我是普通线程

    }
}
守护线程

synchronized

synchronized关键字的作用域有二种:

  1. 是某个对象实例内,synchronized aMethod(){}可以防止多个线程同时访问这个对象的synchronized方法(如果一个对象有多个synchronized方法,只要一个线程访问了其中的一个synchronized方法,其它线程不能同时访问这个对象中任何一个synchronized方法)。这时,不同的对象实例的synchronized方法是不相干扰的。也就是说,其它线程照样可以同时访问相同类的另一个对象实例中的synchronized方法;
  2. 是某个类的范围,synchronized static aStaticMethod{}防止多个线程同时访问这个类中的synchronized static 方法。它可以对类的所有对象实例起作用。 

除了方法前用synchronized关键字,synchronized关键字还可以用于方法中的某个区块中,表示只对这个区块的资源实行互斥访问。用法是: synchronized(this){/*区块*/},它的作用域是当前对象; 

synchronized关键字是不能继承的,也就是说,基类的方法synchronized f(){} 在继承类中并不自动是synchronized f(){},而是变成了f(){}。继承类需要你显式的指定它的某个方法为synchronized方法;

A.无论synchronized关键字加在方法上还是对象上,它取得的锁都是对象,而不是把一段代码或函数当作锁――而且同步方法很可能还会被其他线程的对象访问。

B.每个对象只有一个锁(lock)与之相关联。

参考

https://www.cnblogs.com/GarfieldEr007/p/5746362.html

 

以上是关于java基础多线程的主要内容,如果未能解决你的问题,请参考以下文章

java基础入门-多线程同步浅析-以银行转账为样例

java多线程基础

Java基础之多线程

Java多线程基础

多线程编程学习一(Java多线程的基础)

Java 多线程基础多线程的实现方式