一、线程相关概念
1.进程
是程序的一次执行过程,或者是正在运行的一个程序。是动态过程:有其自身的产生、存在和消亡的过程。操作系统将为进程分配新的内存空间。
2.线程
由进程创建,是进程的实体;一个进程可拥有多个线程。
3.单线程
在同一个时刻,只允许执行一个线程。
4.多线程
同一个时刻,可以执行多个线程。
5.并发
同一个时刻,多个任务交替执行,造成“貌似同时”执行的错觉。简而言之,单核CPU执行多任务就是并发。
6.并行
二、线程基本使用
2.1集成Thread类,重写run方法
class Cat extends Thread {
int times = 0;
/**
* 重写run方法
*/
@Override
public void run() {
while (true) {
// 该线程每间隔一秒,在控制台输出
System.out.println(String.format("喵喵,我是小猫咪%d,%s", ++times, Thread.currentThread().getName()));
// 线程休眠一秒
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (times == 80) {
// 当times == 8,退出while,此时线程退出
break;
}
}
}
}
2.1.1为什么不是run()方法启动线程?
run()
方法是一个普通方法,没有真正启动一个线程,就会把run()
方法执行完毕,才向下执行。
2.1.2start()方法执行源码
java.lang.Thread
public synchronized void start() {
...
start0();
...
}
start0()
是本地方法,由JVM调用,底层是C/C++实现的。
真正实现多线程的效果是start0()
,而不是run()
。
private native void start0();
注意:start()
方法调用start0()
之后,该线程不会立马执行,而是将线程变成了可运行状态,具体什么时候执行,取决于CPU,由CPU进行统一调度。
2.2实现Runnable接口,重写run方法
class Dog implements Runnable {
int count = 0;
@Override
public void run() {
while (true) {
System.out.println(String.format("小狗%d号,汪汪叫~", ++count));
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (count == 8) {
break;
}
}
}
}
3.多线程执行
创建T1,继承自Runnable
class T1 implements Runnable {
int count = 0;
@Override
public void run() {
while (true) {
// 每个一秒输出Hello World,输出10次
System.out.println(String.format("T1, hello world, %d", ++count));
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (count == 10) {
break;
}
}
}
}
创建T2,继承自Runnable
class T2 implements Runnable {
int count = 0;
@Override
public void run() {
while (true) {
// 每个一秒输出Hello World,输出10次
System.out.println(String.format("T2, hello world, %d", ++count));
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (count == 5) {
break;
}
}
}
}
在main方法中调用
public static void main(String[] args) {
T1 t1 = new T1();
T2 t2 = new T2();
Thread thread1 = new Thread(t1);
Thread thread2 = new Thread(t2);
thread1.start();
thread2.start();
}
4.继承Thread和实现Runnable的区别
1.从Java的设计角度来看,通过继承Thread或者实现Runnable接口创建线程本质上没有区别,从JDK帮助文档可以看到Thread类本身就实现了Runnable接口。
2.实现Runnable接口的方式更加适合多线程共享一个资源的情况,并且避免了单继承的限制。
T3 t3 = new T3();
new Thread(t3).start();
new Thread(t3).start();
建议:使用实现Runnable接口实现继承。
三、线程常用方法
1、注意事项和细节
1.start()
底层会创建新的线程,调用run()
,run()
就是一个简单的方法调用,不会启动新的线程。
2.线程优先级的范围
public final static int MIN_PRIORITY = 1;
public final static int NORM_PRIORITY = 5;
public final static int MAX_PRIORITY = 10;
3.interrupt()
中断线程,但并没有真正结束线程,一般用于中断正在休眠的线程。
4.sleep(long millis)
线程的静态方法,使得当前线程休眠。
5.yield()
Thread的静态方法,线程的礼让,让出CPU,让其他线程执行,但礼让的时间不确定,所以也不一定礼让成功。
6.join()
线程的插队,插队的线程一旦插队成功,则肯定先执行完插入的线程所有的任务。
2、用户线程和守护线程
5.2.1用户线程
也称之为工作线程,当线程执行完或通知方式结束
5.2.2守护线程
一般是为工作线程服务的,当所有的用户线程结束,守护线程自动结束。
常见的守护线程:垃圾回收机制。
四、线程的生命周期
JDK中用Thread.State枚举列举了线程的几种状态
public enum State {
/**
* 尚未启动的线程处于此状态
*/
NEW,
/**
* 在JVM中执行的线程处于此状态
*/
RUNNABLE,
/**
* 被阻塞等待监视器锁定的线程处于此状态
*/
BLOCKED,
/**
* 处于等待状态的线程正在等待另一个线程执行特定操作
*/
WAITING,
/**
* 具有指定等待时间的等待线程的线程状态
*/
TIMED_WAITING,
/**
* 已退出的线程处于此状态
*/
TERMINATED;
}
五、同步
在多线程编程中,一些敏感数据不允许被多个线程同时访问,此时使用线程同步访问技术,保证数据在任何同一时候,最多只有一个线程访问,以保证数据的完整性。
1、Synchronize
5.1.1同步代码块
synchronized (obj) {
// TODO
}
5.1.2同步方法
public synchronized void m() {
// TODO
}
六、互斥锁
1、基本介绍
1.在Java语言中,引入了互斥锁的概念,保证数据操作的完整性。
2.每个对象都对应一个可称为“互斥锁”的标记,这个标记用来保证在同一时刻,只有一个线程能访问该对象。
3.关键字synchronized
来与互斥锁联系。当某个对象用synchronized
修饰时,则该对象在任一时刻只能有一个线程访问。
4.同步的局限性:导致程序的执行效率降低。
5.同步方法(非静态)的锁可以是this
,也可以是其他对象(要求多个对象看到的是同一个对象)。
6.同步方法(静态的)的锁可以是当前类本身。
2、注意事项
1.同步方法中如果没有使用static修饰:默认锁对象为this
2.如果同步方法使用static修饰,默认锁对象:当前类.class
3.实现的落地步骤:
需要分析上锁的代码
选择同步代码或同步方法
要求多个线程的锁对象为同一个
七、线程的死锁
多个线程都占了对方的资源,但不可相让,导致了死锁,在编程中一定要避免死锁的产生。
八、释放锁
1、以下操作将释放锁
1.当前线程的同步方法、同步代码块执行完毕。
2.当前线程所在的同步代码块、同步方法遇到break
、return
3.当前线程在同步代码块、同步方法中出现了未处理的Error
或Exception
,导致异常结束。
4.当前线程在同步代码块、同步方法中执行了线程的await()
方法,当前线程暂停,并释放锁。
2、以下操作不会释放锁
1.线程执行同步代码块时,程序调用Thread.sleep()
、Thread.yield()
方法暂停当前线程的执行,不会释放锁。
2.线程执行同步代码块时,其他线程调用了该线程的suspend()
方法将该线程挂起,该线程不会释放锁。
注意: 应避免使用suspend()
和resume()
控制线程,方法不再推荐使用。