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();
	}
}
  • 退出循环的条件就是满足了唤醒该线程的条件。

五、源码:

原文地址:https://www.cnblogs.com/ruigege0000/p/13876044.html