多线程理解(java)

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

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值