目录
程序:是为完成特定任务 用某种语言编写的一组指令的集合(也就是我们常说的代码)
线程:线程是由进程创建的实体 一个进程可以拥有一个或者多个线程
并发:同一个时刻 多个任务交替执行 造成一种“貌似同时”的错觉(hh)
前言
本文是博主学习韩顺平老师java课程后的笔记 韩老师yyds!
基本概念
程序:是为完成特定任务 用某种语言编写的一组指令的集合(也就是我们常说的代码)
进程:
是指运行中的程序 进程是程序的一次执行过程 或是正在运行的一个程序 是动态过程 有它自身的产生 生存 和 消亡的过程 比如我们打开qq 是一个进程 系统会为此分配资源和空间 再比如我们打开浏览器又是一个进程 系统会为它再次分配空间
线程:线程是由进程创建的实体 一个进程可以拥有一个或者多个线程
单线程 同一时刻只允许执行一个线程
我们打开qq音乐播放一首歌 发现自己只能听一首 切换了就是下一首了 这就相当于单线程的一种体现
多线程 同一时刻可以执行多个线程
比如我们打开qq 群发消息就是多线程的一种体现 同时发送给了不同的人
并发:同一个时刻 多个任务交替执行 造成一种“貌似同时”的错觉(hh)
比如 典型的单核cpu行的多任务就是并发
并发具体一点就是说 左手画圆右手画三角形 其实中间是有停顿的 但是速度一快就像是同时执行的
并行:同一时刻 多个任务同时进行 多核cpu实现并行
具体一点就是说 jack在开车 jack's wife lucy在接电话 怎么样具体不具体
线程的使用
创建线程
有两种方法
第一种继承Thread类 重写run方法
public class Pre01 {
public static void main(String[] args) throws InterruptedException {
P p = new P();
//启动线程
p.start();
for (int i = 0; i < 80; i++) {
//线程休眠 一秒钟 此处会有异常需要我们处理
Thread.sleep(1000);
System.out.println("主线程"+i);
}
System.out.println("主线程继续执行");
}
}
class P extends Thread{
@Override
public void run() {
for (int i = 0; i < 80; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"线程被调用"+i);
}
}
}
执行结果如下(部分)
Thread-0线程被调用9
主线程9
Thread-0线程被调用10
主线程10
主线程11
Thread-0线程被调用11
Thread-0线程被调用12
主线程12
Thread-0线程被调用13
总结
1.可以清晰得出两个线程是同时交替执行的
2.为什么p不直接调用run方法而是调用start方法呢
请看源码 start源码在其中调用了start0方法
/* 1.start方法
public synchronized void start() {
start0();
}
start0方法中重写并调用了run方法
2.start0方法
//真正实现多线程的效果是start0() 而不是run
//start方法调用start0方法后 该线程并不一定会马上执行 只是将线程变成了可运行状态 具体什么时候 取决于cpu
private native void start0();
@Override
public void run() {
if (target != null) {
target.run();
}
}
如果你单纯调用run方法的话 相当于调用了一个普通方法 并不会创建线程 这样会使得先执行run方法 再去执行主线程 本质上还是一个线程
第二种方法:实现Runnable接口
第二种方法更好的满足适应了java单继承的特性 因为java只能单继承如果利用了第一种方法 那么就无法再继承其他的类了 所以我们一般来说更为推荐第二种方法 即通过实现接口来创建线程
public class Pre02 {
public static void main(String[] args) throws InterruptedException{
P1 p1 = new P1();
//此处可以认为p1 转换成了Thread类 因为两者其实都实现了Runnable接口
Thread thread = new Thread(p1);
thread.start();
for (int i = 0; i < 80; i++) {
Thread.sleep(1000);
System.out.println("主线程"+i);
}
}
}
class P1 implements Runnable{
@Override
public void run() {
for (int i = 0; i < 80; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
System.out.println("子线程"+i);
}
}
}
总结:
1.两个方法没有本质上的区别 都实现了Runnable接口
2.关于P1那段描述可能不太准确 具体的底层机制我也还不了解 只是参考前面所学做出的推测
多线程售票问题
三个窗口共同卖一百张火车票 运用多线程实现
public class Pre03 {
public static void main(String[] args) {
D d = new D();
D d1 = new D();
D d2 = new D();
d.start();
d1.start();
d2.start();
}
}
class D extends Thread{
private static int nums = 100;
//一共一百张票
@Override
public void run() {
while(nums>=0){
System.out.println(Thread.currentThread().getName()+"线程(窗口)售出了一张还有"+nums--+"张");
}
}
}
最后可能会出现超发的结果 这就牵扯到后面所说的锁的问题
线程终止
public class Thread05 {
public static void main(String[] args) {
T t = new T();
Thread thread = new Thread(t);
thread.start();
//主线程控制t1线程的终止 必须可以修改loop
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
t.setLoop(false);
}
}
class T implements Runnable{
private int count = 0;
//设置一个控制变量
private boolean loop = true;
@Override
public void run() {
while(loop){
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("ok"+count);
count++;
}
}
public void setLoop(boolean loop) {
this.loop = loop;
}
public int getCount() {
return count;
}
}
1.当线程完成任务后 它会自动退出
2.还可以通过使用变量来控制run方法退出的方式停止线程 即通知方式
只要在努力 没有虚度时光 就没有什么好焦虑的 共勉!