一· 首先了解一下什么是进程?线程?
进程:操作系统中一个程序的执行周期称为一个进程。
线程:一个程序同时执行多个任务。通常,每一个任务就称为一个线程。与进程相比较,线程更"轻量级",创建、撤销一个线程比启动一个新进程开销要小的多。没有进程就没有线程,进程一旦终止,其内的线程也将不复存在。
线程和进程的区别是什么?
多进程与多线程区别:本质区别在于,每个进程拥有自己的一整套变量,而线程则共享数据。共享变量使得线程之间的通信比进程之间通信更有效、更方便。
什么是高并发?
高并发:访问的线程量非常非常高。
高并发带来的问题?
高并发带来的问题:服务器内存不够用,无法处理新的请求。
一个线程,从创建到运行结束,拥有三种不同的状态:
从图片可以详知:线程的三种状态为 就绪状态 运行状态 以及 阻塞状态。
二· JAVA多线程的实现:
2.1 通过继承Thread类实现多线程
java.lang.Thread是一个线程操作的核心类。新建一个线程最简单的方法就是直接继承Thread类,而后覆写该类中的run()方法(就相当于主类中的main方法)
package com.revision.Thread;
public class MyThread extends Thread {
private String title;
public MyThread(String title) {
this.title = title;
}
@Override
public void run() {
//所有线程都从这里开始
for(int i = 0;i <= 10;i++){
System.out.println(this.title + ", i = " + i);
}
}
public static void main(String[] args) {
MyThread mt1 = new MyThread("byl1");
MyThread mt2 = new MyThread("byl2");
MyThread mt3 = new MyThread("byl3");
MyThread mt4 = new MyThread("byl4");
mt1.run();
mt2.run();
mt3.run();
mt4.run();
}
}
当现在有了线程的主体类之后,很自然我们就会想到产生线程类的实例化对象而后调用run()方法。实际上,我们不能够直接去调用run()方法。
这个时候只是做了一个顺序打印,和多线程一点关系都没有。正确启动多线程的方式是调用Thread类中的start()方法。
package com.revision.Thread;
public class MyThread extends Thread {
private String title;
public MyThread(String title) {
this.title = title;
}
@Override
public void run() {
//所有线程都从这里开始
for(int i = 0;i <= 10;i++){
System.out.println(this.title + ", i = " + i);
}
}
public static void main(String[] args) {
MyThread mt1 = new MyThread("byl1");
MyThread mt2 = new MyThread("byl2");
MyThread mt3 = new MyThread("byl3");
MyThread mt4 = new MyThread("byl4");
/*mt1.run();
mt2.run();
mt3.run();
mt4.run();*/
//这里我们不再使用run()方法开启线程
mt1.start();
mt2.start();
mt3.start();
mt4.start();
}
}
可以看到这次执行的顺序已经是被“打乱了的“,也就是在交替执行。
2.2 使用Runnable()接口实现多线程
Thread类的核心功能就是用于线程启动,如果为了启动一个线程而继承这个类,就会产生java单继承的局限性,所以我们有时也需要通过Runnable()接口去实现多线程
观察Runnable()接口:
@FunctionalInterface
public interface Runnable {
/**
* When an object implementing interface <code>Runnable</code> is used
* to create a thread, starting the thread causes the object's
* <code>run</code> method to be called in that separately executing
* thread.
* <p>
* The general contract of the method <code>run</code> is that it may
* take any action whatsoever.
*
* @see java.lang.Thread#run()
*/
public abstract void run();
}
利用实现Runnable()接口,写出代码:
package com.revision.Thread;
public class MyThread1 implements Runnable {
private String title;
public MyThread1(String title) {
this.title = title;
}
@Override
public void run() {
//所有线程都从这里开始
for(int i = 0;i <= 10;i++){
System.out.println(this.title + ", i = " + i);
}
}
public static void main(String[] args) {
MyThread1 mt1 = new MyThread1("byl");
mt1.
}
}
此时我们发现 并没有start()方法供我们使用,应为Runnable()接口里只有一个run()方法。
那么此时就需要关注Thread类提供的构造方法。
我们发现Thread类的构造方法为:
public Thread(Runnable target)
所以我们利用Thread构造器来接受一个实现了runnable接口的对象
package com.revision.Thread;
public class MyThread1 implements Runnable {
private String title;
public MyThread1(String title) {
this.title = title;
}
@Override
public void run() {
//所有线程都从这里开始
for(int i = 0;i <= 10;i++){
System.out.println(this.title + ", i = " + i);
}
}
public static void main(String[] args) {
MyThread1 mt1 = new MyThread1("byl1");
MyThread1 mt2 = new MyThread1("byl2");
new Thread(mt1).start();
new Thread(mt2).start();
}
}
此时我们看到 结果是交替运行,说明启动多线程也成功!
我们也可以利用 匿名内部类的方式启动多线程,或是利用lambda表达式:
1.利用匿名内部类:
package com.revision.Thread;
//利用匿名内部类实现多线程的启动
public class MyThread2 {
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
for(int i = 0;i <= 10;i++){
System.out.println(i);
}
}
}).start();
}
}
2.利用lambda表达式:
public class TestDemo {
public static void main(String[] args) {
Runnable runnable = () -> System.out.println("Hello World");
new Thread(runnable).start();
}
}
2.3Thread与Runnable()的区别:
首先从使用形式来讲,明显使用Runnable实现多线程要比继承Thread类要好,因为可以避免单继承局限。
其次,Thread类是Runnable接口的实现类,所以一定覆写了Runnable的run()方法。
不难发现,在多线程的处理上,我们使用了代理设计模式。
实际上在开发之中使用Runnable还有一个特点:使用Runnable实现的多线程的程序类可以更好的描述出程序共享的概念(并不是说Thread不能)
此时 我们使用继承Thread的方法 实现两个人共同卖票的功能
package com.revision.Thread;
//使用Thread类对售票进行模拟
public class SellTickets extends Thread {
private int ticketnum = 10; //十张票
@Override
public void run() {
while(this.ticketnum >= 1){
System.out.println("剩余票数:"+ ticketnum--);
}
}
public static void main(String[] args) {
new SellTickets().start();
new SellTickets().start();
}
}
运行结果如图
本来希望的是这两个线程共同卖10张票 结果变成各自卖各自的。
利用实现Runnable()接口来实现同步卖票功能:
package com.revision.Thread;
//使用Thread类对售票进行模拟
public class SellTickets implements Runnable{
private int ticketnum = 10; //十张票
@Override
public void run() {
while(this.ticketnum >= 1){
System.out.println("剩余票数:"+ ticketnum--);
}
}
public static void main(String[] args) {
SellTickets st = new SellTickets();
new Thread(st).start();
new Thread(st).start();
}
}
运行结果如图:
因此可以得出结论:
Runnable实现的多线程的程序类可以更好的描述出程序共享的概念
2.4 Callable实现多线程
从JDK1.5开始追加了新的开发包:java.uti.concurrent。这个开发包主要是进行高并发编程使用的,包含很多在高并发操作中会使用的类。在这个包里定义有一个新的接口Callable
@FunctionalInterface
public interface Callable<V> {
/**
* Computes a result, or throws an exception if unable to do so.
*
* @return computed result
* @throws Exception if unable to compute a result
*/
V call() throws Exception;
}
Runnable中的run()方法没有返回值,它的设计也遵循了主方法的设计原则:线程开始了就别回头。但是很多时候需要一些返回值,例如某些线程执行完成后可能带来一些返回结果,这种情况下就只能利用Callable来实现多线程
package com.revision.Thread;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class MyCallable implements Callable {
private int tickets = 10;
@Override
public Object call() throws Exception {
while(this.tickets >= 0){
System.out.println("剩余票数:" + tickets--);
}
return "票卖完了!";
}
public static void main(String[] args) throws InterruptedException,
ExecutionException {
FutureTask<String> task = new FutureTask<>(new MyCallable());
new Thread(task).start();
new Thread(task).start();
System.out.println(task.get());
}
}
以上形式主要是为了取得线程的执行结果。