10.10:学习内容
1. 看到的好东西
- hexo:基于node.js制作的一个博客工具,hexo不需要部署到我们的服务器上,我们的服务器上保存的,其实是基于在hexo通过markdown编写的文章,然后hexo帮我们生成静态的html页面,然后,将生成的html上传到我们的服务器。简而言之:hexo是个静态页面生成、上传的工具。
官方地址:https://hexo.io/zh-cn/
-
Color Thems:idea主题
官方地址:http://color-themes.com/?view=index
2. 名词学习
- James Gosling:詹姆斯·高斯林 ,java之父
- Runnbale:就绪;可运行状态;java接口;
- thread:线;线程;
- concurrent:并发;
- Process:进程;
- Callable:请求即付的;java接口;
3. 基础学习
1. 并发与并行
并发:并发是指两个或多个事件在同一时间段内发生
并行:并行是指两个或多个事件在同一时刻发生
- 单核计算机不能同时处理多个任务,只能让事件任务并发执行。宏观上单核机线程是并行运行的,因为其中线程相互间的执行时间可能是极其微小的,让人感觉是同时发生的。微观上,线程的执行是串行运行,即只能一个线程一个线程的去运行,当计算机只有一个cpu的时候,线程会已某种顺序去排列执行,这就是线程调度。
2. 线程与进程
- 进程:
是指一个内存中正在进行的应用程序,每一个进程都有 一个独立的内存空间,一个应用程序可以运行多个进程。 进程也是应用程序的一次执行过程,是系统程序的基本单位。 系统运行一个程序既是一个进程从创建到执行,执行完后销毁的过程。
-
线程:
线程是进程中的一个执行单元,负责当前进程中的程序的执行,一个进程中至少有一个线程,一个进程可以包含多个进程。
-
进程与线程的关系:
进程就像火车,线程是构成火车的一节节车厢。
进程在系统中运行,就似火车过山洞,线程作为车厢一列列进去,最后一节车厢通过,火车也就成功通过山洞,一个进程也就完成。
-
线程调度:
- 分时调度:所有线程轮流使用cpu的使用权,平均分配每个线程占用cpu的时间
- 抢占式调度:优先让优先级高的线程使用cpu,如果优先级相同,则会在优先级相同的线程中随机选择一个线程,java就是使用抢占式调度。
-
在java中,每次程序的运行至少启动两个线程,一个是main主线程和垃圾收集线程。当java执行一个类时,实际上都会启动一个JVM(java虚拟机),每个JVM都会在cpu中启动一个进程。
3. 创建线程类
一. 声明Thread类的子类
-
JAVA线程类定义:
java使用
java.lang.Thread
类代表线程,java中所有的线程都必须是Thread类或者继承其的子类的实力。 -
创建线程步骤:
- 定义Thread类子类, extend Thread,并重写该类的run()方法,该方法的方法体就代表了线程的主要执行的任务内容,run()方法也可以称为线程执行体。
- 创建Thread子类的实例,new 一个Thread子类,即创建了线程对象。
- 调用线程对象的start()方法来启动线程。
实例:
- 创建Thread子类MyThread,重写run()方法
public class MyThread extends Thread{
//Thread中定义线程名称的构造方法
public MyThread(String name) {
super(name);
}
//重写run方法,即定义线程的任务
@Override
public void run() {
for (int i=0;i<10;i++){
System.out.println("正在执行:"+getName()+i);
}
}
}
- 测试,实例化Thread子类,启动线程Thread.start();
public class test {
public static void main(String[] args) {
//实例化Thread子类,子类参数是String name
MyThread myThread=new MyThread("线程");
//线程启动Thread.start()
myThread.start();
for (int i=0;i<10;i++){
System.out.println("正在执行main线程第"+i+"次");
}
}
- 执行结果
D:\JDK-1.8\bin\java.exe...
正在执行main线程第0次
正在执行:线程:0
正在执行main线程第1次
正在执行:线程:1
正在执行main线程第2次
正在执行:线程:2
正在执行main线程第3次
正在执行:线程:3
正在执行main线程第4次
正在执行:线程:4
正在执行:线程:5
正在执行:线程:6
正在执行:线程:7
正在执行:线程:8
正在执行:线程:9
正在执行main线程第5次
正在执行main线程第6次
正在执行main线程第7次
正在执行main线程第8次
正在执行main线程第9次
- 结果解析:
在java进程启动执行时,main主线程在调用main方法的时候被创建,接着调用了实例化之后的Thread子类线程的start()方法启动新的线程,整个程序的线程都是并发执行。
二.实现Runnable接口
- 创建步骤:
- 定义Runnable接口的实现类,重写run()方法
- 创建Runnable实现类的实例,并将其作为Thread()的参数Target来创建对象,Thread对象才是真正的线程对象。
- start()启动线程
- 实例:
1.定义Runnable接口的实现类,重写run()方法
public class RunnableThread implements Runnable{
@Override
public void run() {
for(int i=0;i<10;i++){
//输出当前正在执行线程的名字Thread.currentThread().getName()
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
2.创建Runnable实现类的实例,并将其作为Thread()的参数Target来创建对象,Thread对象才是真正的线程对象。start()启动线程
public class test {
public static void main(String[] args) {
//实例化Runnable实现类
RunnableThread runnableThread=new RunnableThread();
//将实例化后的Runnable实现类作为对象传入Thread()中
Thread r= new Thread(runnableThread,"runnable线程");
r.start();
for (int i=0;i<10;i++){
System.out.println("正在执行main线程第"+i+"次");
}
}
}
3.结果解析
正在执行main线程第0次
正在执行main线程第1次
正在执行main线程第2次
正在执行main线程第3次
runnable线程:0
runnable线程:1
runnable线程:2
runnable线程:3
runnable线程:4
runnable线程:5
runnable线程:6
runnable线程:7
runnable线程:8
runnable线程:9
正在执行main线程第4次
正在执行main线程第5次
正在执行main线程第6次
正在执行main线程第7次
正在执行main线程第8次
正在执行main线程第9次
三.实现Callable接口
-
Callable接口:java5才开始提供的Callable接口,实质上是Runnable接口的增强版,使用call()作为线程执行体,增强了run()方法,其call()方法带有返回值,也可以声明异常。
-
Future接口: java5提供了Future接口去代表Callable接口中的call()的返回值,并提供了一个FutureTask实现类,实现了Future接口并且实现了runnable接口,从而可以作为Thread()的Target。
-
步骤:
- 创建callable接口实现类,重写call方法,该方法为线程执行体,并有该方法的返回值。
- 实例化Callable,用FutureTask去包装Callable对象,FutureTask同时也封装了Callable对象的call()方法 的返回值。
- 实例化FutureTask类,将其放入到Thread()中
- Thread.start()启动callable线程,通过FutureTask.get()获取call()的返回值
-
实例
public class CallableThread implements Callable { private int ticket=30; @Override public Object call() throws Exception { //阻塞队列的接口java.util.concurrent.BlockingQueue // BlockingQueue bq=new ArrayBlockingQueue(10); int i=1; for (;i<50;i++){ if (ticket>0){ System.out.println(Thread.currentThread().getName() + "正在卖冬瓜,剩余冬瓜数量" + --ticket); } } return i; } }
public class test { public static void main(String[] args) throws InterruptedException{ //实例化Callable实现类 CallableThread ct=new CallableThread(); FutureTask futureTask=new FutureTask(ct); Thread B=new Thread(futureTask,"捞老2"); Thread A=new Thread(futureTask,"捞老1"); A.start(); B.start(); }
结果: 捞老1正在卖冬瓜,剩余冬瓜数量29 捞老1正在卖冬瓜,剩余冬瓜数量28 捞老1正在卖冬瓜,剩余冬瓜数量27 捞老1正在卖冬瓜,剩余冬瓜数量26 捞老1正在卖冬瓜,剩余冬瓜数量25 捞老1正在卖冬瓜,剩余冬瓜数量24 捞老1正在卖冬瓜,剩余冬瓜数量23 捞老1正在卖冬瓜,剩余冬瓜数量22 ... 其中线程1和2会交互的全部跑完,并没有出现同时并行状况。
四.Thread,Runnable,Callable的区别
-
Callable实质Runnable的增强版,call()方法比run()方法多了返回值,在此不做太多区别。通过创建Runnable实现类去实现创建线程要比继承Thread的子类好
理由:
-
继承Thread的子类不适合线程资源共享,而实现Runnable接口的实现类则很容易实现资源共享
实例【卖票】:
- Thread子类
public class MyThread extends Thread{ private int ticket=5; public MyThread(String name) { super(name); } //重写run方法 @Override public void run() { for (int i=1;i<10;i++){ if (this.ticket>0){ System.out.println(getName()+"正在卖票,剩余数量"+--ticket); } } } }
- Runnable接口实现类
public class RunnableThread implements Runnable{ private int ticket=5; @Override public void run() { for(int i=0;i<10;i++){ if (this.ticket>0){ System.out.println(Thread.currentThread().getName()+"正在卖票,剩余数量"+--ticket); } } } }
- 测试
public class test { public static void main(String[] args) { /* //Thread子类 MyThread a=new MyThread("窗口A:"); a.start(); MyThread b=new MyThread("窗口B:"); b.start();*/ RunnableThread runnableThread=new RunnableThread(); Thread A= new Thread(runnableThread,"窗口A"); Thread B= new Thread(runnableThread,"窗口B"); A.start(); B.start(); } }
- 测试结果
Thread子类:
窗口A:正在卖票,剩余数量4 窗口B:正在卖票,剩余数量4 窗口B:正在卖票,剩余数量3 窗口B:正在卖票,剩余数量2 窗口B:正在卖票,剩余数量1 窗口B:正在卖票,剩余数量0 窗口A:正在卖票,剩余数量3 窗口A:正在卖票,剩余数量2 窗口A:正在卖票,剩余数量1 窗口A:正在卖票,剩余数量0
Runnable接口实现类:
窗口A正在卖票,剩余数量4 窗口B正在卖票,剩余数量2 窗口B正在卖票,剩余数量1 窗口B正在卖票,剩余数量0 窗口A正在卖票,剩余数量3
由此可知,Thread对资源共享并不适合,Runnable则可以做到对资源进行共享,卖的票就是从5张表里面卖,而Thread的方法则是每个线程都有5张票,不会对创建的ticket进行共享。
-
可以避免java中的单继承的局限性,即implement和extends的区别
-
线程池不能直接放入继承Thread类中,只能放入实现Runnbale或callable类线程中
-
代码和线程低耦合,相互独立。
-