start()方法的执行步骤
- 通过jvm告诉操作系统创建thread。
- 操作系统开辟内存并调用本地函数创建Thread线程对象。
- 操作系统对Thread对象进行调度,以确定执行时机。
- Thread在操作系统中被成功执行。
因为start()方法的执行需要调用操作系统本地的方法(函数),因此start()的执行比较耗时。下面代码的执行结果通常情况下是先输出运行结束,后输出MyThread。但也具有不确定性。
public class MyThread extends Thread{
@override
public void run(){
super.run();
System.out.println("MyThread");
}
}
public class Run{
public static void main(String[] args){
MyThread myThread = new MyTread();
myThread.start();
System.out.println("运行结束");
}
}
Thread.java类中的start()和run()方法的区别?
Thread类的start()通知“线程规划器”,此线程已经准备好,准备调用线程对象的run方法。这个过程其实就是让系统安排一个时间来调用Thread中的run方法,既让线程执行具体的任务,具有随机执行的效果。
如果调用thread类的run()方法,就不会异步执行了,而是同步执行,那么此线程对象不会交给“线程规划器”来处理,而是由main主线程来调用run()方法,也就等于run方法的代码执行完成后才会执行后面的代码。
start()方法是真正的开启多线程,run()方法只会顺序执行。详见如下代码:
public class MyThread2 extends Thread{
public MyThread2(){
System.out.println("构造方法打印:"+Thread.currentThread().getName());
}
@Override
public void run(){
System.out.println("run方法打印:"+Thread.currentThread().getName());
}
}
public class Test {
public static void main(String[] args) {
MyThread2 thread2 = new MyThread2();
thread2.start();
}
}
-----------------------------------------------------
执行结果:
构造方法打印:main
run方法打印:Thread-0
public class Test {
public static void main(String[] args) {
MyThread2 thread2 = new MyThread2();
thread2.run();
}
}
-----------------------------------------------------
执行结果:
构造方法打印:main
run方法打印:main
suspend()方法和resume()方法
suspend()方法和resume()方法组合使用可以很方便的实现线程的暂停与重新启动,(stop()方法会销毁线程对象,suspend()方法不会销毁线程对象),但是suspend()方法和resume()方法会有如下缺点。
- suspend()方法和resume()方法会发生独占
- suspend()方法和resume()方法会发生数据不完整
Thread.setPriority()方法
Thread.setPriority()方法可以设置线程优先级,优先级设置1-10,1:优先级最低,10:优先级最高,5:默认优先级。
即使设置了优先级,线程的执行也是随机的,当两个线程优先级相差不大时,优先级高的不一定先执行结束。
优先级高的线程,执行效率高(同样逻辑条件下运行的更快)。
死锁
不同线程同时等待不可能被释放的所,从而导致所有任务都不能继续执行的现象较死锁。
多线程死锁实例
public class DeadLock implements Runnable {
private String name;
private Object lock1 = new Object();
private Object lock2 = new Object();
public void setName(String name) {
this.name = name;
}
@Override
public void run() {
if (name.equals("a")) {
synchronized (lock1) {
try {
System.out.println("name = " + name);
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lock2) {
System.out.println("持有锁lock1 -> lock2");
}
}
}
if (name.equals("b")) {
synchronized (lock2) {
try {
System.out.println("name = " + name);
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lock1) {
System.out.println("持有锁lock2 -> lock1");
}
}
}
}
}
public static void main(String[] args) {
try{
DeadLock deadLock = new DeadLock();
deadLock.setName("a");
Thread t1 = new Thread(deadLock);
t1.start();
Thread.sleep(200);
deadLock.setName("b");
Thread t2 = new Thread(deadLock);
t2.start();
}catch (InterruptedException e){
}
}
线程死锁可通过JDK自带工具来检测,
- cmd命令窗口他能够过jps查看java线程
- jstack -l 线程id命令可以检测出死锁现象
synchronized
(synchronized修饰方法是写在public前和public后是一样的)
- 修饰静态或者非静态的方法的区别
synchronized修饰非静态的方法,锁对象是当前类的实例。synchronized修饰静态方法是,锁对象是当前类的 .class单例对象
public class MyService {
//锁对象为当前类对应的Class类对象
synchronized public static void method1() {
}
//锁对象为当前类对应的Class类对象
public void method2() {
synchronized (MyService.class) {
}
}
//锁对象为当前类的对象
synchronized public void method3() {
}
//锁对象为当前类的对象
public void method4() {
synchronized (this) {
}
}
//锁对象为字符串abc
public void method5() {
synchronized ("abc") {
}
}
/**
* 以上方法method1和method2是同步关系行,method3和method4是同步关系,
* method1/method2 和method5之间是异步关系,method1/method2和method3/method4之间是异步的
* method3/method4和method5直接也是异步的
*/
}
- synchronized是可重如锁
A线程持有object对象的lock锁,B线程可以以异步的方式调用object对象的非synchronized类型的方法。
A线程持有object对象的lock锁,B现成如果调用object对象的synchronized类型的方法,则需要等待,也就是同步执行。
在方法声明处添加synchronized并不是锁方法,而是锁当前类的对象,在java中只有"讲对象当做锁"的说法,没有"锁方法"的说法。
synchronized锁可重入的验证
public class MyService1 {
//synchronized锁可重入验证
synchronized public void test1(){
System.out.println("test1");
test2();
}
synchronized public void test2(){
System.out.println("test2");
test3();
}
synchronized public void test3(){
System.out.println("test3");
}
}
public class MyThreadService1 extends Thread{
@Override
public void run(){
MyService1 service1 = new MyService1();
service1.test1();
}
}
当子类继承父类时,子类可以通过锁重入调用父类的同步方法。
- synchronized(非this对象X)格式写法的结论
synchronized使用X对象作为监视器,可有以下结论:
1、当多个线程同时执行synchronized(x){}同步代码块时呈现同步效果
2、当其他线程执行x对象中的synchronized同步方法时呈同步效果
3、其他线程执行x对象方法里边的synchronized(this)代码块时呈同步效果
当其他现场调用非synchronized的方法是还是异步执行的
- synchronized在字节码命令中的原理
在方法中使用synchronized关键字实现同步的原因是使用了flag标记ACC_SYNCHRONIZED,当方法调用时,调用指令会检查方法的ACC_SYNCHRONIZED访问标志是否设置,如果设置了,执行线程先持有同步锁,然后执行方法,最后在方法执行完成释放锁。
使用synchronized修饰代码块,则使用monitorenter和monitorexit指令进行同步处理
synchronized原理参考https://www.cnblogs.com/mingyao123/p/7424911.html
volatile
volatile关键字可以强制从公共内存中读取变量的值。
- volatile具有以下特性:
1、可见性:B线程能马上看见A线程更改的内容
2、原子性:volatile修饰i++是非原子的
3、禁止代码重排序
- i++的步骤
1、从内存中读取i的值;
2、计算i的值;
3、将i的值写到内存中。如果在第2步读取i的值时,另一个线程也修改了i的值,那么这个时候就会出现脏数据,解决办法时Synchronized关键字
wait/notify机制
- wait/notify机制的原理:
拥有相同所的线程才可实现wait/notify机制,wait()方法是Object类的方法,他的作用是当前执行wait()的线程等待,在wait()所在的代码行暂停执行,同时释放锁,直到接到通知或被中断为止。通过通知机制使某个线程继续执行wait()方法后面的代码时,对线程的选择是按照执行wait()的顺序确定(即先执行wait的线程先被通知),并需要再次获得锁。如果调用wait方法时没有获得锁,则抛出IllegalMonitorStateException。notify()方法用来通知那些可能等待锁对象的线程,调用方法前必须获得锁对象,如果有多个线程等待,则按照执行wait的顺序发出一次通知。执行notify后,等待的线程不会马上获得所对象,而是要等到执行nofity方法的线程执行完,才会释放锁,wait状态的线程才可能获取锁。
notifyAll()方法会通知所有处于wait状态的线程,它会按照执行wait方法的倒序对线程进行唤醒。
- wait()、sleep()、notify()的释放锁时机
wait():立即释放锁
sleep():不释放锁
notify():不释放锁
- wait(long)方法
wait(long)方法的功能是等待一段时间内是否有现成对锁进行notify通知唤醒,如果超出这个时间现成自动唤醒,继续向下执行需要再次持有锁。
wait方法一般与while搭配使用。当线程调用wait方法后,再对线程对象执行interrupt()方法会抛出InterruptedException异常。
join()方法
join方法的作用是等待线程对象销毁,例如:子线程执行处理耗时的数据,主线程调用需要这个数据时,就可以通过join方法。
join方法具有时线程排队的效果,类似同步的运行效果,但是join方法与synchronized的区别是join方法在内部使用wait方法进行等待,而synchronized通过锁实现。
join(long)方法
x.join(long)方法中的参数用于设定等待时间,不管线程x是否执行完,时间到了并且重新获得了锁,则当前线程会继续向后运行,如果一直没有获得锁,则一直尝试,直到获得锁为止。
join(long)与sleep(long)的区别:join(long)方法释放锁,sleep(long)方法不释放锁
ThreadLocal类
类ThreadLocal的主要作用是将数据放入当前线程对象中的Map中,这个Map是Thread类的实例变量。类ThreadLocal自己不管理、不存储任何数据,只是数据与map之间的桥梁,用于将数据放入到map中。
每个线程的map存有自己的数据,map中的key存储的的是ThreadLocal对象,value为存储的值。每个线程的Map值只对当前线程可见,其他线程不可以访问。当前线程销毁时,map随之销毁,map中的数据如果没有被应用,则会被GC回收。
- ThreadLocal的原理(源码分析)
https://blog.csdn.net/weixin_40027906/article/details/103929340
ReenraantLock类
synchronized是非公平锁,reentrantlock可以指定为公平锁。
synchronized修饰非静态的方法时,所对象为当前类的class单例对象。
ReentrantLock可以替代synchronized,使用synchronized锁定如果遇到异常,jvm会自动释放锁,使用reentrantlock必须手动释放锁,一般在finally中释放。
reentrantlock可是使用trylock尝试锁定,这样无法锁定,或者在给定的时间无法锁定,线程可以决定继续等待。
使用ReentrantLock还可以调用lockInterruptibly方法,对线程interrupt方法做出相应。
https://blog.csdn.net/f641385712/article/details/81136653
AQS原理
AbstractQueuedSynchronized(AQS)!,类如其名,抽象的队列式的同步器,AQS定义了一套多线程访问共享资源的同步器框架,许多同步类实现都依赖于它,如常用的ReentrantLock/Semaphore/CountDownLatch...。我们以ReentrantLock作为讲解切入点。
ReentrantLock把所有Lock接口的操作都委派到一个Sync类上,该类继承了AbstractQueuedSynchronizer:
static abstract class Sync extends AbstractQueuedSynchronizer
Sync又有两个子类:
final static class NonfairSync extends Sync
final static class FairSync extends Sync
https://www.jianshu.com/p/279baac48960