一、创建多线程的两种方式
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());
}
}
6.知识扫盲
- 系统进行资源调度和内存分配的基本单位------进程
- 线程就是进程的一个执行路径
- 内部的机制:一个进程中有一个或者多个线程,进程中的堆和方法区线程内公用。线程中的栈和程序计数器,自己独有。
- 程序计数器是用于记录线程当前要执行的地址(原理: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()来加载这个动态链接库文件。
三、如果获取共享变量的监视器锁
- 使用这个共享变量的时候,用synchronized关键字进行同步代码块
synchronized(共享变量){
}
- 该共享变量在方法中的参数的时候
synchronzied void add (int a,int b){
a + b;
}
四、虚假唤醒
- 一个线程的共享变量通过调用自身的wait()方法来进入阻塞状态,那如果想要从挂起状态恢复到运行状态,那么只有三种方式:(1)其他线程调用了该对象notify()或者notifyAll()方法的时候;(2)其他线程调用了该对象的interrupt()方法;
- 如果没有以上两种情况,或者等待超时,仍然恢复运行状态就叫虚假唤醒
- 避免这种情况的方式
synchronize(obj){
while(条件不满足){
obj.wait();
}
}
- 退出循环的条件就是满足了唤醒该线程的条件。
五、源码:
https://github.com/ruigege66/ConcurrentJava
- CSDN:https://blog.csdn.net/weixin_44630050
- 博客园:https://www.cnblogs.com/ruigege0000/
- 欢迎关注微信公众号:傅里叶变换,个人账号,仅用于技术交流