线程的两种创建方式
1、继承Thread类,重写 run 方法
class Thread01 extends Thread {
@Override
public void run() {
int count = 0;
while(true) {
System.out.println("继承Thread线程正在运行:" + Thread.currentThread().getName() + "===" + ++count);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
if (count == 100) {
break;
}
}
}
}
2、实现Runnable接口,重写 run 方法
class Thread02 implements Runnable {
@Override
public void run() {
int count = 0;
while(true) {
System.out.println("实现Runnable线程正在运行:" + Thread.currentThread().getName() + "===" + ++count);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
if (count == 100) {
break;
}
}
}
}
JConsole监控线程执行情况
在控制台输入 JConsole,打开Java监视和管理控制台
线程终止
1、当线程完成任务后,会自动退出
2、还可以通过使用变量来控制run方法退出的方式停止线程,即通知方式
在线程中设置变量,通过控制变量来实现通知方式
class Thread03 extends Thread {
private boolean loop = true;
@Override
public void run() {
int count = 0;
while(loop) {
System.out.println("继承Thread线程正在运行:" + Thread.currentThread().getName() + "===" + ++count);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
if (count == 100) {
break;
}
}
}
public void setLoop(boolean loop) {
// 把传入的参数赋值给当前对象的loop属性
this.loop = loop;
}
}
线程常用方法
- setName 设置线程名称,使之与参数name相同
- getName 返回该线程的名称
- start 使该线程开始运行,Java 虚拟机底层调用该线程的start0 方法
- run 调用线程对象 run 方法
- setPriority 更改线程的优先级
- getPriority 获取线程的优先级
- sleep 在指定的毫秒数内让当前正在执行的线程休眠(暂停执行)
- interrupt 中断线程
- yield 线程的礼让。让出CPU,让其他线程执行,但礼让的时间不确定,所以也不一定礼让成功
- join 线程的插队。插队的线程一旦插队成功,则肯定先执行完插入的线程所有的任务。
用户线程和守护线程
- 用户线程:也叫工作线程,当线程的任务执行完或通知方式结束
- 守护线程:一般是为工作线程服务的,当所有的用户线程结束,守护线程自动结束。设置为守护线程的方法:setDaemon(true)
- 常见的守护线程:垃圾回收机制
线程同步(Synchronized)
1、同步代码块
synchronized(对象) { // 得到对象的锁,才能操作同步代码
// 需要被同步的代码
}
2、synchronized 还可以放在方法声明中,表示整个方法-为同步方法
public synchronized void fun(String str) {
// 需要被同步的代码
}
互斥锁
- Java在Java语言中,引入了对象互斥锁的概念,来保证共享数据操作的完整性
- 每个对象都对应与一个可成为“互斥锁”的标记,这个标记用来保证在任一时刻,只能有一个线程访问该对象
- 关键字synchronized来与对象的互斥锁联系。当某个对象用synchronized修饰时,表名该对象在任一时刻只能由一个线程访问
- 同步的局限性:导致程序的执行效率要降低
- 同步方法(非静态的)的锁可以是this,也可以是其他对象(要求是同一对象)
- 同步方法(静态的)的锁为当前类本身
注意事项
- 同步方法如果是非静态方法,默认锁对象为this
- 同步方法如果是静态方法,默认锁对象为当前类(当前类.class)
- 多个线程的锁对象需为同一对象
线程的死锁
基本介绍
两个或多个线程互相持有对方所需要的锁资源,且都在等待其他线程释放锁资源,从而导致了死锁。
如下代码,就会出现死锁情况:
public class DeaLockDemo {
private static Object lock1 = new Object();
private static Object lock2 = new Object();
public static class ThreadA implements Runnable{
@Override
public void run() {
synchronized (lock1){
System.out.println("ThreadA 获得 lock1 锁资源");
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("ThreadA 尝试获取 lock2 锁资源");
synchronized (lock2){
System.out.println("ThreadA 获得了 lock1 和 lock2 锁资源");
}
}
}
}
public static class ThreadB implements Runnable{
@Override
public void run() {
synchronized (lock2) {
System.out.println("ThreadB 获得 lock2 锁资源");
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("ThreadB 尝试获取 lock1 锁资源");
synchronized (lock1){
System.out.println("ThreadB 获得了 lock1 和 lock2 锁资源");
}
}
}
}
public static void main(String[] args) {
new Thread(new ThreadA(), "ThreadA").start();
new Thread(new ThreadB(), "ThreadB").start();
}
}
释放锁
- 当前线程的同步方法或同步代码块执行结束
- 当前线程的同步代方法或同步代码块遇到break、return
- 当前线程在同步方法或同步代码块中出现了未处理的异常,导致异常结束
- 当前线程在同步方法或同步代码块中,执行了线程对象的wait()方法,当前线程暂停,且释放锁