java命令执行一个java程序的过程
java命令启动JVM进程
JVM加载主类,创建一个线程,在该线程中,执行我们主类中的main()方法(main线程)
直到main方法执行完毕
JVM是单线程还是多线程:多线程
byte[] bytes = null;
/*
在无限循环中,我们不停的从堆内存申请内存空间,创建字节数组,
按道理来讲,应该用不了多久,堆内存就会被字节数组占满,从而有一个
内存溢出错误,但是实际并未发生。
原因在于:
1. 当没有一个引用变量指向,堆内存中的数据对象的时候,此时数组对象
实际上已经变成垃圾。
2. 其实在jvm中,还有另外一个线程,同时运行着,垃圾回收器的代码,不停的回收
堆空间上的垃圾对象,占用的内存空间
*/
while (true) {
bytes = new byte[4096];
bytes = null;
}
Thread实现方式一
- 继承Thread
- 重写/覆盖,子类中的run方法
- 创建该子类对象
- 启动线程start()//让线程中代码开始执行
注:
- 一个Thread类或Thread子类的一个对象,就代表一个线程(一条独立的执行路径)
- 只有run方法中的代码才会运行在子线程中,所以需要覆盖父类Thread的run方法
- 在run方法中调用其他方法也能运行
- 方法运行在哪个线程中,需要看该方法在哪个线程中被调用
- 在执行子线程代(run方法代码)的是时候,不能线程对象.run(), 这种方式可以run()方法的代码执行,但此时效果只是相当于普通方法调用,不会有多线程的效果
- 一个线程对象只能被启动一次,想要让代码在两个线程中执行,必须创建两个线程对象,分别启动这两个线程
Thread实现方式二
- 定义实现Runnable接口的子类
- 实现Runnable接口的run方法
- 创建该子类对象,在创建Thread时,将创建好的Runnable子类对象作为初始化参数,传递给Thread对象
- 启动Thread对象
注: - Runnable接口子类运行的run方法,会运行在子线程中
- 所以在该方式中,自己定义子类,实现Runnable接口的run方法
- 故Runnable子类对象,不代表线程,只代表要在线程中执行的任务;、逻辑上说该方法更好,因为线程只代表一条路径,并不关心执行的代码,该方法将任务(Runnable子类对象)与线程(Thread)分开
public class demo {
public static void main(String[] args) {
MyRunnable myRunnable = new MyRunnable();
Thread thread = new Thread(myRunnable);//将线程与任务分开
thread.start();
}
}
class MyRunnable implements Runnable{
@Override
public void run() {
System.out.println("hello");
}
}
方式对比
- 方式一步骤少一步
- 第二种将任务与线程解耦
- 方式一存在单重继承的局限性
- 方式二便于多线程的数据共享
电影票示例
/**
* @author zhangshuai@Cskaoyan.onaliyun.com on 2020/3/26.
* @version 1.0
*
* 假设A电影院正在上映某电影,该电影有100张电影票可供出售,
* 现在假设有3个窗口售票。请设计程序模拟窗口售票的场景。
*
* 此时,我们直接使用线程的第一种方式来模拟售票,其实并没有成功,我们实现的效果,相当于
* 每个窗口各自有各自的100票
*/
public class SalesDemo {
public static void main(String[] args) {
//线程的第一种方式来模拟
SalesWindow salesWindow1 = new SalesWindow();
SalesWindow salesWindow2 = new SalesWindow();
SalesWindow salesWindow3 = new SalesWindow();
salesWindow1.start();
salesWindow2.start();
salesWindow3.start();
//线程的第二种方式模拟 , 它基本可以模拟我们的售票场景(数据共享,)
SalesTask salesTask = new SalesTask();
new Thread(salesTask).start();
new Thread(salesTask).start();
new Thread(salesTask).start();
}
}
//第一种
class SalesWindow extends Thread {
// 加static关键字,才能实现,3个线程共享这100张票
//static int tickets = 100;
int tickets = 100;
@Override
public void run() {
//执行售票的动作
System.out.println(getName() + "售卖除了第" + tickets-- + "张票");
}
}
//第二种
class SalesTask implements Runnable {
int tickets = 100;
@Override
public void run() {
//执行售票的动作
while (tickets > 0) {
System.out.println(Thread.currentThread().getName() + "售卖除了第" + tickets-- + "张票");
}
}
}
设置或获取线程的名称
public final String getName()
public final void setName(String name)
都是Thread的成员方法
注:获取主线程的方法:static Thread currentThread(),返回对当前 正在执行的 线程对象 的引用。
线程的调度
单核情况下,主要调度有两种
- 协同式:当一个线程执行完毕,他会通知系统,由系统选择另外一个线程执行。特点是容易实现,线程执行时间由系统自己决定,但不太稳定
- 抢占式:线程执行时间和线程调度都由系统决定
设置线程优先级
public final int getPriority()
public final void setPriority(int priority)
注:
1. java语言中的Thread表示的线程,优先级取值范围 1-10
2. 最低优先级是1,最高优先级是10
3. 线程的默认优先级是5
4. 我们给设置的优先级,没有起到作用, 因为线程调度的工作主要是由操作系统内核来完成,在操作系统中,操作系统对于线程有自己的复杂一套优先级机制:静态 结合 动态优先级。故不能依靠设置优先级来控制线程执行顺序。(优先级仍然是有意义的(统计意义),从统计的角度来看,优先级高的线程,运行的时间多一点)
线程控制API
public static native void sleep(long millis)在指定的 毫秒 数内让当前正在执行的线程休眠
TimeUnit.SECONDS.sleep() 休眠,单位为妙
public final void join()等待该线程终止,方法调用所在线程等待
public static native void yield()暂停当前正在执行的线程对象,并执行其他线程(实质上,yield只能做到暂停自己一瞬间,无法指定其他线程执行,没有调度功能)
public final void setDaemon(boolean on)将该线程标记为守护线程或用户线程(当JVM进程正在运行的线程都是守护线程时,JAVA虚拟机退出)
public void interrupt()打断线程休眠状态
线程的生命周期