进程与线程的概念
进程:操作系统中一个程序的执行周期
线程:一个进程同时执行多个任务。通常来讲,每个任务就称为一个线程。
线程与进程比较:
1.线程更加的“轻量级”,创建、撤销一个线程比启动、撤销一个进程开销要小的多。一个进程中的所有线程共享此进程的所有资源。
2.没有进程就没有线程,进程一旦终止,其内的线程也将不复存在
3.进程是操作系统资源调度的基本单位,进程可以独享资源。线程需要依托进程提供的资源,无法独立申请操作系统资源,是OS任务执行的基本单位。
高并发:访问的线程量非常非常高。
高并发带来的问题:服务器内存不够用,无法处理新的请求。
多线程实现
继承Thread类实现多线程
java.lang.Thread是线程操作的核心类。新建一个线程最简单的方法就是直接继承Thread,而后覆写run()方法(相当于主线程的main()方法,线程入口 )
线程启动一定调用Thread类提供的start()方法:public synchronized void start()此方法会自动调用线程的run()方法
范例:实现多线程
class MyThread extends Thread{
private String title;
public MyThread(String title){
this.title=title;
}
public void run(){
for(int i=0;i<3;i++){
System.out.println(this.title+" "+i);
}
}
}
public class Test {
public static void main(String[] args) {
MyThread myThread1=new MyThread("线程1");
MyThread myThread2=new MyThread("线程2");
MyThread myThread3=new MyThread("线程3");
//启动线程
myThread1.start();
myThread2.start();
myThread3.start();
}
}
运行结果:所有线程对象交替执行
看到这里相信有很多童鞋都会有一个疑问就是为什么要通过start()方法来调用run()方法,而不是run()直接执行?
来我们看start方法源码
- start方法只能调用一次,多次调用会抛出异常
- 下面我们看到了在start()方法中调用了start0()方法,而这个方法是一个只声明而未实现的方法同时使用native关键字进行定义。
native指的是调用本机的原生系统函数。
Thread 类有个 registerNatives 本地方法,该方法主要的作用就是注册一些本地方法供 Thread 类使用,如 start0(),stop0() 等等,可以说,所有操作本地线程的本地方法都是由它注册的。这个方法放在一个 static 语句块中,当该类被加载到 JVM 中的时候,它就会被调用,进而注册相应的本地方法。而本地方法 registerNatives 是定义在 Thread.c 文件中的。Thread.c 是个很小的文件,它定义了各个操作系统平台都
要用到的关于线程的公用数据和操作,如下:
观察上边一小段代码,可以容易的看出 Java 线程调用 start->start0 的方法,实际上会调用到 JVM_StartThread 方法
在 jvm.cpp 中,有如下代码段:
这里JVM_ENTRY是一个宏,用来定义JVM_StartThread 函数,可以看到函数内创建了真正的平台相关的本地线程,其线程函数是 thread_entry,如下:
可以看到调用了vmSymbolHandles::run_method_name 方法,而run_method_name是在 vmSymbols.hpp 用宏定义的:
因此我们可以知道,Java线程创建的调用流程如下:
(未启动状态)start()->start0()->进行资源调度,系统分配(JVM)->run(Java方法)执行线程的具体操作任务
Runnable()接口实现多线程
观察Runnable接口:
范例:用Runable接口实现多线程
class MyThread implements Runnable{
private String title;
public MyThread(String title){
this.title=title;
}
public void run(){
for(int i=0;i<3;i++){
System.out.println(this.title+" "+i);
}
}
}
public class Test {
public static void main(String[] args) {
MyThread myThread1=new MyThread("线程1");
MyThread myThread2=new MyThread("线程2");
MyThread myThread3=new MyThread("线程3");
//Thread类提供的构造方法:public Thread(Runnable target)
new Thread(myThread1).start();
new Thread(myThread2).start();
new Thread(myThread3).start();
}
}
范例:使用匿名内部类进行Runnable对象创建
public class Test {
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
for(int i=0;i<3;i++){
System.out.println("hello");
}
}
}).start();
}
}
范例:使用Lamdba表达式进行Runnable对象创建
public class Test {
public static void main(String[] args) {
Runnable runnable=()->System.out.println("hello");
new Thread(runnable).start();
}
}
Thread与Runnable区别
先看下Thread类的定义
public class Thread implements Runnable
1.Thread类与自定义线程类(实现了Runnable接口),是一个典型的代理设计模式。Thread类负责辅助真实业务操作(资源调度,创建线程并启动)
2. 自定义线程类负责真实业务的实现(run方法具体做啥事)
3. 使用Runnable接口实现的多线程程序类可以更好的描述共享的概念
范例:使用Thread实现数据共享(产生若干线程进行同一数据的处理操作)
class MyThread extends Thread{
private Integer ticket=3;
private String title;
public MyThread(String title){
this.title=title;
}
public void run(){
for(int i=0;i<3;i++){
System.out.println(this.title+"还剩下"+this.ticket+"---票");
this.ticket--;
}
}
}
public class Test {
public static void main(String[] args) {
new MyThread("黄牛A").start();
new MyThread("黄牛A").start();
new MyThread("黄牛A").start();
}
}
运行结果:启动三个线程实现卖票处理,结果变为了各自卖个自的票了
范例:使用Runnable实现共享
class MyThread implements Runnable{
private Integer ticket=3;
private String title;
public MyThread(String title){
this.title=title;
}
public void run(){
for(int i=0;i<3;i++){
System.out.println(this.title+"还剩下"+this.ticket+"---票");
this.ticket--;
}
}
}
public class Test {
public static void main(String[] args) {
MyThread mt=new MyThread("黄牛");
Thread thread1=new Thread(mt);
Thread thread2=new Thread(mt);
Thread thread3=new Thread(mt);
thread1.start();
thread2.start();
thread3.start();
}
}
运行结果:三个线程一起卖票
Callable实现多线程
从JDK1.5开始追加了新的开发包:java.util.concurrent。这个开发包主要是进行高并发编程使用的,包含很多在高并发操作中会使用的类。在这个包里定义有一个新的接口Callable
Runnable中的run()方法没有返回值,它的设计也遵循了主方法的设计原则:线程开始了就别回头。但是很多时候需要一些返回值,例如某些线程执行完成后可能带来一些返回结果,这种情况下就只能利用Callable来实现多线程。
范例::使用Callable实现多线程
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
class MyThread implements Callable<String> {
private Integer ticket=3;
public String call(){
for(int i=0;i<3;i++){
System.out.println("还剩下"+this.ticket+"---票");
this.ticket--;
}
return "票买完啦~";
}
}
public class Test {
public static void main(String[] args) {
MyThread myThread=new MyThread();
FutureTask<String> futureTask=new FutureTask<String>(myThread);
Thread thread=new Thread(futureTask);
Thread thread1=new Thread(futureTask);
thread.start();
thread1.start();
try {
System.out.println(futureTask.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
运行结果:票买完就不会再买了
Callable实现多线程(juc)-当线程有返回值的时候只能实现Callable实现多线程。