Java线程基础

Posted Al_tair

tags:

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

线程基础

大家好呀!我是小笙!我学习了韩顺平老师的类和对象的知识,收获颇丰!现在来和大家分享笔记!

线程

进程和线程的概念

概念:进程是指运行中的程序,是程序的一次执行过程或是正在运行的一个程序。动态过程:产生,存在,消亡的过程

那么线程是什么?

线程由进程创建,是进程的一个实体,一个进程可以拥有多个线程

  • 单线程:同一个时刻只允许一个线程
  • 多线程:同一个时刻,可以执行多个线程

并发:同一个时刻,多个任务交替执行,单核cpu实现多任务

并行:同一个时刻,多个任务同时执行,多核cpu可以实现并行执行多任务

那我们为什么要用多线程而不是多进程呢?

线程间的切换和调度成本远小于进程

线程的生命周期

public enum State 
    // 创建进程,但是资源条件未满足
    NEW,
    // 运行进程
    RUNNABLE,
    // 阻塞进程
    BLOCKED,
    // 无时间限制等待notify()方法唤醒
    WAITING,
    // 有时间限制等待notify()方法唤醒
    TIMED_WAITING,
    // 结束进程
    TERMINATED;

线程基本使用

创建线程的两种方式

  1. 继承Thread类,重写run方法(本质:Thread类也实现了Runable接口)

  2. 实现Runable接口,重写run方法

    // 使用Thread构造接受实现了Runnable的类,可以调用start()方法
    public Thread(Runnable target) 
        this(null, target, "Thread-" + nextThreadNum(), 0);
    
    

源码解析多线程机制

多线程机制说明

用例代码

// 疑问:为什么调用start()方法而不是直接调用run()方法,不都是实现run()方法吗?
// 本质区别有没有创建新的线程,直接调用run方法就是和使用普通方法一样没什么区别,并没有创建线程
public class Thread01 extends Thread
    int times = 0;
    public static void main(String[] args) throws InterruptedException 
        Thread01 thread01 = new Thread01();
        thread01.start();

        for (int i = 0; i < 60; i++) 
            System.out.println(Thread.currentThread().getName()+i);
            Thread.sleep(1000);
        
    

    @Override
    public void run() 
        while(true)
            try 
                Thread.sleep(1000);
            catch (Exception e)
                System.out.println(e.getMessage());
            
            System.out.println("喵喵,我是小猫咪"+ ++times );
            if(times == 80)
                break;
            
        
    

使用Terminal – jconsole工具观察进程

注意要main方法和其他进程要持续较长时间。这样子才好观测

源码分析

// 调用线程start方法:thread01.start();
// 源码分析
public synchronized void start() 
    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) 

        
    


// 本地方法,开辟线程
private native void start0();

进程终止

stop()方法(不推荐)

为什么stop()方法被废弃而不推荐使用呢?

因为stop()方法太过于暴力,强行把执行到一半的程序强行退出,会导致数据不一致的问腿

自制设置标志位退出

public class StopThread 
    public static void main(String[] args) throws InterruptedException 
        Thread1 thread1 = new Thread1();
        thread1.start();
        Thread.sleep(10000);
        thread1.setFlag(false);
    


class Thread1 extends Thread
    private int count = 0;
    // 设置标志位来判断线程终止时间
    private boolean flag = true;
    @Override
    public void run() 
        while (flag)
            try 
                Thread.sleep(50);
             catch (InterruptedException e) 
                e.printStackTrace();
            
            System.out.println(++count);
        
    

    public void setFlag(boolean flag) 
        this.flag = flag;
    

中断方式退出程序

中断方式类似于之前通过标志位方式退出线程的方法,但是中断更加强劲一些,它可以让等待在sleep或者wait的线程引发异常退出

public class InterruptThread 
    public static void main(String[] args) throws InterruptedException 
        Interrupt thread1 = new Interrupt();
        thread1.start();
        Thread.sleep(10000);
        thread1.interrupt();
    


class Interrupt extends Thread
    private int count = 0;
    private boolean flag = true;
    @Override
    public void run() 
        while (true)
            if(this.isInterrupted())
                System.out.println("Interrupted");
                break;
            
            try 
                Thread.sleep(50);
             catch (InterruptedException e) 
                e.printStackTrace();
                // 为什么这里还需要中断一次
                // 因为sleep()方法中断抛出的异常会清除中断标志位,因此还需要再中断一次
                this.interrupt();
            
            System.out.println(++count);
        
    

线程的常用方法

等待wait()和通知notify()

有些人会好奇,wait和notify方法不是Object类的方法吗,为什么放在线程这里特别拿出来讲?

因为这两个方法平时并不能随便调用,它必须包含在对应的同步块中

public final void wait() throws InterruptedException 
    wait(0L);

// 当多个线程在等待,则随机通知其中一个等待线程
public final native void notify();
// 通知所有等待线程
public final native void notifyAll();

Object.wait()方法和Thread.sleep()方法的区别

  • wait()方法可以被唤醒,使用wait方法之后会释放目标对象的锁

  • sleep()方法不会释放任何的资源

等待线程结束join()

join()方法:线程的插队,如果插队的线程一旦插入成功,则肯定先执行完插入的线程的所有任务、

// 无线等待,直到目标线程的任务执行完成
public final void join() throws InterruptedException
// 给出一个最大的等待时间
public final synchronized void join(long millis, int nanos) throws InterruptedException

示例代码

public class Join 
    public static void main(String[] args) throws InterruptedException 
        Thread3 t = new Thread3();
        t.start();
        for (int i = 1; i <= 20; i++) 
            if(i == 5)
                System.out.println("让Thread3先完成");
                t.join();
                System.out.println("main继续执行");
            
            System.out.println("main: " + i);
            Thread.sleep(1000);
        
    


class Thread3 extends Thread
    private int count;
    @Override
    public void run() 
        while(true)
            if(count++ == 20)
                System.out.println("Thread3结束了");
                break;
            
            try 
                Thread.sleep(1000);
             catch (InterruptedException e) 
                e.printStackTrace();
            
            System.out.println("Thread3: "+count);
        
    

join()方法的底层源码

// join方法的本质就是调用wait方法在当前对象实例进行等待
// 被等待的线程会在执行完成后调用notifyAll()方法唤醒等待的进程进程
public final synchronized void join(final long millis)
    throws InterruptedException 
    if (millis > 0) 
        if (isAlive()) 
            final long startTime = System.nanoTime();
            long delay = millis;
            do 
                wait(delay);
             while (isAlive() && (delay = millis -
                   TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime)) > 0);
        
     else if (millis == 0) 
        // 测试此线程是否存在。如果线程已启动且尚未死亡,则该线程处于活动状态 RUNNABLE状态
        while (isAlive()) 
            wait(0);
        
     else 
        throw new IllegalArgumentException("timeout value is negative");
    

谦让yeild()

yeild():线程的礼让,让出cpu让其他进程执行,但是礼让的时间不确定,也不一定礼让成功,还有就是当前处理器是否忙碌,如果处理器完成处理的过来,就不会进行礼让

使用场景:当你觉得这个线程不重要或者优先级很低,那适当让出cpu给那些更重要的线程是否是一个明智之举

用户线程和守护线程

用户线程:又称工作线程,当执行的任务执行完或通知方式结束

守护线程:一般是为工作线程服务,当所有线程结束,守护线程自动结束(比如:垃圾回收机制)

public class ThreadMethod 
    public static void main(String[] args) 
        MyDaemonThread md  = new MyDaemonThread();
        // 设置为守护线程
        md.setDaemon(true);
        md.start();
        for (int i = 0; i < 10; i++) 
            try 
                Thread.sleep(1000);
             catch (InterruptedException e) 
                e.getMessage();
            
            System.out.println("用户线程在此");
        
    


class MyDaemonThread extends Thread
    @Override
    public void run() 
        while(true)
            try 
                Thread.sleep(1000);
             catch (InterruptedException e) 
                e.getMessage();
            
            System.out.println("守护线程在此");
        
    

线程同步机制

同步概念:当有多个线程同时在对内存进行操作,在某一个时刻只允许一个线程对该内存进行操作(比如:写操作)

关键字synchronized的作用是实现线程的同步,它的工作是对同步代码枷锁,使得每一次只能有一个线程进入同步块,从而保证了线程的安全

关键字synchronized的用法

// 指定锁对象  默认锁对象就是this
synchronized(对象) // 需要得到对象的锁,才能操作同步代码
// 直接作用于实例方法  默认锁对象就是this
public synchronized void method()
// 直接作用于静态方法  默认锁对象就是当前类.class
public static synchronized void method()
public class increase01 implements Runnable
    static int count = 0;
    static int count2 = 0;

    public static synchronized  void increase()
        count++;
    
    public  synchronized  void increase2()
        count2++;
    
    public static void main(String[] args) throws InterruptedException 
        // 如果同一类传入的对象不同,对象锁就无法启到作用了,必须使用类的锁才可以锁住
        Thread t1 = new Thread(new increase01());
        Thread t2 = new Thread(new increase01());
        t1.start();t2.start();
        t1.join(); t2.join();
        System.out.println(count); // 20000000
        System.out.println(count2); // 小于20000000
        
        i
        System.out.println("-------");
        increase01.count2 = 0;
        increase01.count = 0;
        // 传入了相同对象,就不需要使用静态锁,对象锁就可以实现
        increase01 inc = new increase01();
        Thread thread = new Thread(inc);
        Thread thread1 = new Thread(inc);
        thread.start(); thread1.start();
        thread.join(); thread1.join();
        System.out.println(count);  // 20000000
        System.out.println(count2); // 20000000
    

    @Override
    public void run() 
        for (int i = 0; i < 10000000; i++) 
            increase();
        
    

释放锁

四种情况释放锁

  • 当前线程的同步方法和同步代码块执行结束
  • 当前的线程在同步代码块和同步方法中遇到break,return
  • 当前线程在同步代码块中出现了未处理的Error或者Exception导致被迫退出
  • 当前的线程在同步代码块或者同步方法中执行了wait房啊,暂停当前的线程同时释放资源

二种情况不释放锁

  • 线程执行同步代码块或者同步方法时,程序调用Thread.sleep()和Thread.yield()方法不会释放锁
  • 线程执行同步代码块或者同步方法时,其他线程调用suspend方法将它挂起,此时它并不会释放该锁(不推荐使用挂起方法)

编程题

1.在main方法中启动两个线程,在第一个线程循环随机打印100 以内的整数,直到第二个线程从键盘中读取了‘Q’命令就终止了第一个线程

public class Homework01 
    public static void main(String[] args) throws InterruptedException 
        RandomNum randomNum = new RandomNum();
        Thread thread = new Thread(randomNum);
        Thread thread2 = new Thread(new Input(randomNum));
        thread.start();
        thread2.start();
    


// 线程1
class RandomNum implements Runnable
    private boolean loop = true;

    public void setLoop(boolean loop) 
        this.loop = loop;
    

    @Override
    public void run() 
        while(loop)
            System.out.println((int)(Math.random()*100));
            try 
                Thread.sleep(1000);
             catch (InterruptedException e) 
                e.getMessage();
            
        
        System.out.println("RandomNum退出程序");
    


// 线程2
class Input implements Runnable

    private  RandomNum rJava多线程基础2

Java基础学习多线程基础面试题形式

JAVA-基础(线程)

Java进阶——线程基础

Java线程基础知识(状态共享与协作)

Java面试题-基础知识