多线程编程大家都觉得比较新鲜而又比较陌生,对于多线程编程的使用场景也不太了解,这个java并发编程系列的文章会从基础开始讲起,今天主要讲下java中的线程基础知识,让大家能快速上手并了解。
0. 线程状态
1、新建状态(New):新创建了一个线程对象。
2、就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法。该状态的线程位于可运行线程池中,变得可运行,等待获取CPU的使用权。
3、运行状态(Running):就绪状态的线程获取了CPU,执行程序代码。
4、阻塞状态(Blocked):阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。阻塞的情况分三种:
(一)、等待阻塞:运行的线程执行wait()方法,JVM会把该线程放入等待池中。
(二)、同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池中。
(三)、其他阻塞:运行的线程执行sleep()或join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。
5、死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。
1. 线程创建
在java中创建线程非常简单,有两种方法,一种是写一个线程类直接继承自concurrent包里的Thread类,重写run()方法,然后创建该thread实例后直接调用start方法线程就可以执行线程中的run()方法了;
public static class MyThread2 extends Thread {
@Override
public void run(){
System.out.println("MyThread2 run!");
}
}
还有种方法是实现Runable接口,实现接口中的Run方法,然后初始化线程的时候将该线程实现类作为Thread构造函数的入参,进行创建线程,建议使用第二种方式,因为使用继承Thread的方式会导致自定义线程的扩展性变差,java种继承只能继承一个类,但是可以实现多个接口。
public class MyThread implements Runnable {
@Override
public void run() {
System.out.println("MyThread run!");
}
}
public static void main(String[] args) {
Thread thread1 = new Thread(new MyThread());
thread1.start();
System.out.println("main finished!");
}
2. 线程停止
停止线程比较简单,直接调用线程的stop方法,但是对于stop方法最好少用因为调用stop后,会立即终止线程,有可能导致多线程间共享的数据产生不一致的情况,所以如果要终止线程最好是自定义一个变量,通过该boolean变量来保证线程退出自然终止。
3. 线程间数据共享
在线程间共享数据是常常会遇到的问题,一般共享数据大家都是定义一个static变量多线程来共享,但是这就带来一个问题,static变量在内存中是没有锁保护的,很容易出现数据因为并行线程导致覆盖的问题,所以后面的系列文章,我会详细讲解下有哪些方法可以保证数据的一致性。
4. 线程等待、恢复
从上面的线程状态图中可以看到,在运行过程中的线程通过wait方法会进入等待状态,当线程中调用notify或者notifyAll方法则会随机唤醒等待队列中的一个线程或者唤醒所有线程继续执行。