java基础:线程及线程池一

前言

多线程编程的根本目的,就是更好的利用cpu资源,采用多线程的方式去同时完成几件事情而不互相干扰。

1.多线程的一些基本概念

  • 多线程:指的是这个程序(一个进程)运行时产生了不止一个线程
  • 并发:通过cpu调度算法,让用户看上去同时执行,实际上从cpu操作层面不是真正的同时。并发往往在场景中有公用的资源,那么针对这个公用的资源往往产生瓶颈,我们会用TPS或者QPS来反应这个系统的处理能力。
  • 线程安全:经常用来描绘一段代码。指在并发的情况之下,该代码经过多线程使用,线程的调度顺序不影响任何结果。这个时候使用多线程,我们只需要关注系统的内存,cpu是不是够用即可。反过来,线程不安全就意味着线程的调度顺序会影响最终结果,如不加事务的转账代码。
  • 同步: Java中的同步指的是通过人为的控制和调度,保证共享资源的多线程访问成为线程安全,来保证结果的准确。如上面的代码简单加入@synchronized关键字。在保证结果准确的同时,提高性能,才是优秀的程序。线程安全的优先级高于性能。

2. 线程的状态

线程的状态
线程在Running的过程中可能会遇到阻塞(Blocked)情况:

  1. 调用join()和sleep()方法,sleep()时间结束或被打断,join()中断,IO完成都会回到Runnable状态,等待JVM的调度。
  2. 调用wait(),使该线程处于等待池(wait blocked pool),直到notify()/notifyAll(),线程被唤醒被放到锁定池(lock blocked pool ),释放同步锁使线程回到可运行状态(Runnable)
  3. 对Running状态的线程加同步锁(Synchronized)使其进入(lock blocked pool ),同步锁被释放进入可运行状态(Runnable)。

此外,在runnable状态的线程是处于被调度的线程,此时的调度顺序是不一定的。Thread类中的yield方法可以让一个running状态的线程转入runnable。

3.多线程编程的两种方式

3.1 继承Thread类

继承Thread类可以一种多线程实现方式,但查看Thread类我们可以发现其也是Runnable接口的一个实例,并且,启动线程的唯一方法就是通过Thread类的start()实例方法。start()方法是一个native方法,它将启动一个新线程,并执行run()方法。这种方式实现多线程很简单,通过自己的类直接extend Thread,并复写run()方法,就可以启动新线程并执行自己定义的run()方法。例如:

public class ThreadDemo1 extends Thread{
    @Override
    public void run() {
        //输出当前线程的名称
        String name = Thread.currentThread().getName();
        for(int i=0;i<100;i++){
            try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
        System.out.println(name+i);
        }
    }   
}

public class ThreadMethod{  
    public static void main(String[] args) {
        new ThreadDemo01().start();
    }
}

3.2 实现Runnable接口

如果自己的类已经继承另一个类,就无法直接extends Thread,此时,必须实现一个Runnable接口,如下:

public class ThreadDemo implements Runnable {
    @Override
    public void run() {
        //输出当前线程的名称
        String name = Thread.currentThread().getName();
        for(int i=0;i<100;i++){
            System.out.println(name+i);
        }
    }   
}
//为了启动线程必须先实例化一个Thread,并传入子定义的线程实例
public class ThreadMethod{  
    public static void main(String[] args) {
        new Thread(new ThreadDemo()).start();
    }
}

3.3 两种实现方式区别

  1. 继承Thread:直接使用Thread类中的方法,代码简单 ,由于子类重写父类的run(),当调用start()时,直接找子类的run()方法 。但由于单继承的原因受到限制。
  2. 实现Runnable接口:不能直接使用Thread类中的方法,需要先获取到线程对象后,才能得到Thread的方法,代码复杂。Thread的构造函数中传入了Runnable引用,成员变量记住它,start()调用Thread中的run()方法时,判断成员变量Runnable的引用是否为空,不为空则在Thread的run()方法中调用Runnable的run()方法。编译看Runnable的run(),运行看子类run()方法。 由于可以多实现 因此不受限制。

4.主要的方法

  1. synchronized:给线程加锁,实现同步
  2. wait():等待, 必须等到notify/notifyAll 方法才能进入runnable状态
  3. notify/notifyAl
  4. sleep(long l) : 睡眠指定时间
  5. join() :在一个线程中调用other.join(),将等待other执行完后才继续本线程。
  6. interrupte():
  7. yield():暂停当前正在执行的线程对象,并执行其他线程。
    关于中断:它并不像stop方法那样会中断一个正在运行的线程。线程会不时地检测中断标识位,以判断线程是否应该被中断(中断标识值是否为true)。终端只会影响到wait状态、sleep状态和join状态。被打断的线程会抛出InterruptedException。

Thread.interrupted()检查当前线程是否发生中断,返回boolean
synchronized在获锁的过程中是不能被中断的。

中断是一个状态!interrupt()方法只是将这个状态置为true而已。所以说正常运行的程序不去检测状态,就不会终止,而wait等阻塞方法会去检查并抛出异常。如果在正常运行的程序中添加while(!Thread.interrupted()) ,则同样可以在中断后离开代码体

5. 线程中获取异常

多线程不能用try,catch来获取线程中的异常

猜你喜欢

转载自blog.csdn.net/weixin_41555736/article/details/82150146