多线程七种执行的状态
初始化状态
就绪状态
运行状态
死亡状态
阻塞状态
超时等待
等待状态
start():调用start()方法会使得该线程开始执行,正确启动线程的方式。
wait():调用wait()方法,进入等待状态,释放资源,让出CPU。需要在同步快中调用。
sleep():调用sleep()方法,进入超时等待,不释放资源,让出CPU
stop():调用sleep()方法,线程停止,线程不安全,不释放锁导致死锁,过时。
join():调用sleep()方法,线程是同步,它可以使得线程之间的并行执行变为串行执行。
yield():暂停当前正在执行的线程对象,并执行其他线程,让出CPU资源可能立刻获得资源执行。
yield()的目的是让相同优先级的线程之间能适当的轮转执行
notify():在锁池随机唤醒一个线程。需要在同步快中调用。
nnotifyAll():唤醒锁池里所有的线程。需要在同步快中调用。
Sleep 主动释放cpu执行权 休眠一段时间
运行状态→限时等待状态
限时等待状态→就绪状态→运行状态
Synchronized 没有获取到锁 当前线程变为阻塞状态
如果有线程释放了锁,唤醒正在阻塞没有获取到锁的线程
从新进入到获取锁的状态
wait() 运行—等待状态
notify() 等待状态–阻塞状态(没有获取到锁的线程 队列)
—就绪状态→运行状态
sleep防止CPU占用100%
sleep(long millis) 线程睡眠 millis 毫秒
sleep(long millis, int nanos) 线程睡眠 millis 毫秒 + nanos 纳秒
使用sleep方法避免cpu空转 防止cpu占用100%
public static void main(String[] args) {
new Thread(() -> {
while (true) {
try {
Thread.sleep(30);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
守护线程与用户线程
java中线程分为两种类型:用户线程和守护线程。通过Thread.setDaemon(false)设置为用户线程;通过Thread.setDaemon(true)设置为守护线程。如果不设置次属性,默认为用户线程。
1.守护线程是依赖于用户线程,用户线程退出了,守护线程也就会退出,典型的守护线程如垃圾回收线程。
2.用户线程是独立存在的,不会因为其他用户线程退出而退出。
public class Thread01 {
public static void main(String[] args) {
Thread thread = new Thread(() -> {
while (true) {
try {
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName() + “,我是子线程”);
} catch (Exception e) {
}
}
});
/**
* 1.setDaemon 设置为true 守护线程是依赖于用户线程,用户线程退出了,守护线程也就会退出,典型的守护线程如垃圾回收线程。
* 2.setDaemon 设置为false 用户线程是独立存在的,不会因为其他用户线程退出而退出。
*/
thread.setDaemon(true);
thread.start();
System.out.println("我是主线程,代码执行结束");
}
}
守护线程与用户线程
java中线程分为两种类型:用户线程和守护线程。通过Thread.setDaemon(false)设置为用户线程;通过Thread.setDaemon(true)设置为守护线程。如果不设置次属性,默认为用户线程。
1.守护线程是依赖于用户线程,用户线程退出了,守护线程也就会退出,典型的守护线程如垃圾回收线程。
2.用户线程是独立存在的,不会因为其他用户线程退出而退出。
public class Thread01 {
public static void main(String[] args) {
Thread thread = new Thread(() -> {
while (true) {
try {
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName() + ",我是子线程");
} catch (Exception e) {
}
}
});
/**
* 1.setDaemon 设置为true 守护线程是依赖于用户线程,用户线程退出了,守护线程也就会退出,典型的守护线程如垃圾回收线程。
* 2.setDaemon 设置为false 用户线程是独立存在的,不会因为其他用户线程退出而退出。
*/
thread.setDaemon(true);
thread.start();
System.out.println("我是主线程,代码执行结束");
}
}
如何安全的停止一个线程
调用stop方法
Stop:中止线程,并且清除监控器锁的信息,但是可能导致 线程安全问题,JDK不建议用。 Destroy: JDK未实现该方法。
Interrupt(线程中止)
Interrupt 打断正在运行或者正在阻塞的线程。
1.如果目标线程在调用Object class的wait()、wait(long)或wait(long, int)方法、join()、join(long, int)或sleep(long, int)方法时被阻塞,那么Interrupt会生效,该线程的中断状态将被清除,抛出InterruptedException异常。
2.如果目标线程是被I/O或者NIO中的Channel所阻塞,同样,I/O操作会被中断或者返回特殊异常值。达到终止线程的目的。
如果以上条件都不满足,则会设置此线程的中断状态。
打断正在阻塞的线程
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
try {
Thread.sleep(5000);
} catch (Exception e) {
e.printStackTrace();
}
}, "t1");
t1.start();
try {
Thread.sleep(1000);
} catch (Exception e) {
}
System.out.println("打断子线程");
//调用interrupt 打断正在阻塞的线程
t1.interrupt();
System.out.println("获取打断标记:" + t1.isInterrupted());
}
打断正在运行的线程
public class Thread06 extends Thread {
@Override
public void run() {
while (true) {
// 如果终止了线程,则停止当前线程
if (this.isInterrupted()) {
break;
}
}
}
public static void main(String[] args) {
Thread06 thread06 = new Thread06();
thread06.start();
try {
Thread.sleep(1000);
} catch (Exception e) {
}
thread06.interrupt();
}
}
Lock锁的基本使用
在jdk1.5后新增的ReentrantLock类同样可达到此效果,且在使用上比synchronized更加灵活
相关API:
使用ReentrantLock实现同步
lock()方法:上锁
unlock()方法:释放锁
使用Condition实现等待/通知 类似于 wait()和notify()及notifyAll()
Lock锁底层基于AQS实现,需要自己封装实现自旋锁。
Synchronized —属于JDK 关键字 底层属于 C++虚拟机底层实现
Lock锁底层基于AQS实现-- 变为重量级
Synchronized 底层原理—锁的升级过程
Lock 过程中 注意 获取锁 释放锁
ReentrantLock用法
public class Thread09 implements Runnable {
private int count = 100;
private Lock lock = new ReentrantLock();
@Override
public void run() {
while (true) {
try {
Thread.sleep(30);
} catch (Exception e) {
}
try {
// 获取锁
lock.lock();
if (count > 1) {
count--;
System.out.println(Thread.currentThread().getName() + "," + count);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
// 释放锁
lock.unlock();
}
}
}
public static void main(String[] args) {
Thread09 thread09 = new Thread09();
Thread t1 = new Thread(thread09);
Thread t2 = new Thread(thread09);
t1.start();
t2.start();
}
}
Condition用法
public class Thread01 {
private Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
public static void main(String[] args) throws InterruptedException {
Thread01 thread01 = new Thread01();
thread01.cal();
Thread.sleep(3000);
thread01.signal();
}
public void signal() {
lock.lock();
condition.signal();
lock.unlock();
}
public void cal(){
new Thread(new Runnable() {
@Override
public void run() {
//子线程 需要主动释放锁,同时当前线程变为阻塞状态
try {
lock.lock();
System.out.println(1);
//a开头 lock中的wait
condition.await();
System.out.println(2);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
}).start();
}
}
多线程yield
主动释放cpu执行权
1.多线程yield 会让线程从运行状态进入到就绪状态,让后调度执行其他线程。
2.具体的实现依赖于底层操作系统的任务调度器
public class Thread02 extends Thread {
public Thread02(String name) {
super(name);
}
@Override
public void run() {
for (int i = 0; i < 50; i++) {
if (i == 30) {
System.out.println(Thread.currentThread().getName() + ",释放cpu执行权");
yield();
}
System.out.println(Thread.currentThread().getName() + "," + i);
}
}
public static void main(String[] args) {
new Thread02("1").start();
new Thread02("2").start();
}
}
多线程优先级
1.在java语言中,每个线程都有一个优先级,当线程调控器有机会选择新的线程时,线程的优先级越高越有可能先被选择执行,线程的优先级可以设置1-10,数字越大代表优先级越高
注意:Oracle为Linux提供的java虚拟机中,线程的优先级将被忽略,即所有线程具有相同的优先级。
所以,不要过度依赖优先级。
2.线程的优先级用数字来表示,默认范围是1到10,即Thread.MIN_PRIORITY到Thread.MAX_PRIORTY.一个线程的默认优先级是5,即Thread.NORM_PRIORTY
3.如果cpu非常繁忙时,优先级越高的线程获得更多的时间片,但是cpu空闲时,设置优先级几乎没有任何作用。
public class Thread03 {
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
int count = 0;
for (; ; ) {
System.out.println(Thread.currentThread().getName() + "," + count++);
}
}, "t1线程:");
Thread t2 = new Thread(() -> {
int count = 0;
for (; ; ) {
System.out.println(Thread.currentThread().getName() + "," + count++);
}
}, "t2线程:");
t1.setPriority(Thread.MIN_PRIORITY);
t1.setPriority(Thread.MAX_PRIORITY);
t1.start();
t2.start();
}
}
Join/Wait与sleep之间的区别
sleep(long)方法在睡眠时不释放对象锁
join(long)方法先执行另外的一个线程,在等待的过程中释放对象锁 底层是基于wait封装的,
Wait(long)方法在等待的过程中释放对象锁