• Java高并发1-创建线程的三种方式、虚假唤醒、native关键字


    一、创建多线程的两种方式

    1.继承Thread类,重写run方法

    2.实现Runnable接口,重写run方法

    3.直接上代码

    package com.ruigege.threadFoundation1;
    
    public class MyThreadExtendsType extends Thread {
    	
    	@Override
    	public void run() {
    		System.out.println("这是一个继承Thread类的多线程表示方法");
    	}
    
    }
    
    
    package com.ruigege.threadFoundation1;
    
    public class MyThreadImplementsRunnable implements Runnable{
    	@Override
    	public void run() {
    		System.out.println("这是一个实现Runable接口的多线程");
    	}
    	
    }
    
    package com.ruigege.threadFoundation1;
    
    
    public class MultiBuildThreadTest {
    	public static void main(String[] args) {
    		//使用继承Thread的类的方式来进行多线程创建
    		MyThreadExtendsType thread1 = new MyThreadExtendsType();
    		thread1.start();
    		//使用实现Runnable接口的方式进行多线程创建
    		Thread thread2 = new Thread(new MyThreadImplementsRunnable());
    		thread2.start();
    		
    		
    	}
    }
    
    

    4.二者之间的差别

    • (1)调用方式:继承的时候直接创建新的实例,然后调用run方法;实现接口的方式,要创建一个实例作为参数作为Thread的构造方法中,再进行调用
    • (2)扩展性:继承只能单继承,耦合度高;实现的方式就不一样了,耦合度小
    • (3)继承的好处,就是在实例中直接使用this关键字就可以使用该线程,而不需要调用Thread.currendThread()方法来获取实例。可以在子类中添加成员变量,如果使用了Runnable方式,那么只能使用主线程中被声明为final的变量
    • (4)两种方式都有一个弊端,就是没有返回值,下面再介绍一种

    5.第三中方式,使用FuntureTask

    package com.ruigege.threadFoundation1;
    
    import java.util.concurrent.Callable;
    
    public class MyThreadImplementsCallable implements Callable<String> {
    	@Override
    	public String call() throws Exception{
    		System.out.println("使用FutureTask的方式来行创建多线程");
    		return "创建好了";
    	}
    	
    
    }
    
    
    package com.ruigege.threadFoundation1;
    
    import java.util.concurrent.ExecutionException;
    import java.util.concurrent.FutureTask;
    
    public class MultiBuildThreadTest {
    	public static void main(String[] args) throws InterruptedException, ExecutionException{
    		//使用Callable和FutureTask来创建多线程
    		FutureTask<String> futurnTask = new FutureTask<>(new MyThreadImplementsCallable());
    		Thread thread3 = new Thread(futurnTask);
    		
    		thread3.start();
    		System.out.println(futurnTask.get());	
    	}
    }
    
    

    1.1

    6.知识扫盲

    • 系统进行资源调度和内存分配的基本单位------进程
    • 线程就是进程的一个执行路径
    • 内部的机制:一个进程中有一个或者多个线程,进程中的堆和方法区线程内公用。线程中的栈和程序计数器,自己独有。
      1.2
    • 程序计数器是用于记录线程当前要执行的地址(原理:CPU是分片加载执行的,一片执行完,要记住下一次从哪里执行)

    执行native方法,pc计数器是undefined地址;执行Java代码的时候才是原定义

    • 堆是什么时候分配的?进程创建的时候就会分配一块内存区域给堆。
    • 就绪状态:调用了start()方法之后,并没有马上执行线程,这个就绪状态就是获取了除了CPU以外的所有的资源的状态,一旦获取了CPU资源后,才会开始真正执行。

    二、native关键字

    • JNI(Java Native Inteface)
    • 作用:使用在方法名称的前面,意味着这个方法,并不是使用Java来编写,而是由其他语言来编写的,这个native就是声明这是个原生函数,具体是由C/C++来实现的并且会编译成dll文件,供Java来进行调用
    • 使用这种机制的原因:Java并非完美,在一些要求高性能的场景中,往往C/C++语言会更加快,于是native的出现能够很好的解决这个问题。
    • 实现的步骤:(1)首先使用native关键字来声明这个方法是原生方法;
      (2)使用Javah生成一个类似于头文件的文件;(3)使用C/C++来编写这个函数,注意其中要引用第二步中产生的头文件(这个头文件又引用了jni.h这个头文件);(4)将这个写好的C码编译成一个动态链接库文件;
      (5)在Java程序中使用System.loadLibrary()来加载这个动态链接库文件。
      1.3

    三、如果获取共享变量的监视器锁

    • 使用这个共享变量的时候,用synchronized关键字进行同步代码块
    synchronized(共享变量){
    
    }
    
    • 该共享变量在方法中的参数的时候
     synchronzied void add (int a,int b){
    	a + b;
    }
    

    四、虚假唤醒

    • 一个线程的共享变量通过调用自身的wait()方法来进入阻塞状态,那如果想要从挂起状态恢复到运行状态,那么只有三种方式:(1)其他线程调用了该对象notify()或者notifyAll()方法的时候;(2)其他线程调用了该对象的interrupt()方法;
    • 如果没有以上两种情况,或者等待超时,仍然恢复运行状态就叫虚假唤醒
    • 避免这种情况的方式
    synchronize(obj){
    	while(条件不满足){
    		obj.wait();
    	}
    }
    
    • 退出循环的条件就是满足了唤醒该线程的条件。

    五、源码:

  • 相关阅读:
    Spring笔记:常用xml标签和属性 山上下了雪
    Spring笔记:Hello World 山上下了雪
    Spring笔记:bean的自动装配 山上下了雪
    IntelliJ IDEA 2020.3.3 x64破解到2099年
    每日长进计划
    idea测试类中的测试方法没有运行按钮
    删除所有的phpfpm进程命令
    高质量编程
    单例模式也能玩出花
    宝塔Linux面板安装命令
  • 原文地址:https://www.cnblogs.com/ruigege0000/p/13876044.html
Copyright © 2020-2023  润新知