1、多线程概述
/* * 1、什么是进程?什么是线程? * 进程是一个应用程序(1个进程是一个软件) * 线程是一个进程中的执行场景(执行单元) * 一个进程可以启动多个线程 * 2、对于java程序来说,从dos窗口中输入: * java helloWord 回车之后 * 会先启动JVM,,而JVM就是一个进程 * 同时再启动一个主线程调用main方法 * 同时再启动一个垃圾回收线程负责看护,回收垃圾 * 最起码,现在的java程序中至少有两个线程并发 * 一个是垃圾回收线程,一个是执行main方法的主线程 * 3、进程和线程是什么关系? * 阿里巴巴:进程 * 童文红:阿里巴巴的一个线程 * 马云:阿里巴巴的一个线程 * 京东:进程 * 强东:京东的一个线程 * 奶茶:京东的一个线程 * 进程可以看做现实生活中的公司 * 线程可以看做公司的某个员工 * 注意: * 进程A和进程B内存独立不共享资源 * 线程A和线程B呢? * 线程A和线程B,堆内存和方法区内存共享 * 当时栈内独立,一个线程一个栈 * 假设启动10个线程,会有10个栈,每个栈和每个栈之间,互不干扰,各自执行各自的,这就是多线程并发 * 火车站:进程 * 火车站窗口:一个售票窗口是一个线程 再每个售票窗口,你不等我,我不等你,提高效率 * 4、思考一个问题? * 使用了多线程机制,main方法结束,是不是有可能程序结束 * main方法结束,只是主线程结束了,其他的栈(线程)可能还在压栈弹栈 * */
2、堆和方法区共享内存
/* * 1、分析一个问题,对于单核的CPU来说,真的可以做到真正的多线程并发吗? * 对于多核的CPU电脑来说,真正的多线程并发没有问题。4核CPU表示同一个时间点上,可以真正的4个进程并发执行 * 什么是真正的多线程并发? * t1线程执行t1的 * t2线程执行t2的 * t1不会影响t2,t2也不会影响t1,这叫真正的多线程并发 * 单核的CPU只有一个大脑: * 能够做到真正的多线程并发,但是可以做到给人一种“多线程并发”的感觉 * 对于单核的CPU来说,在某个时间点上实际上只能处理一件事情,但是由于CPU的执行速度极快,多个线程之间频繁切换执行,跟人来的感觉就是, * 多个事情同时在做 * 线程A:播放音乐 * 线程B:运行魔兽游戏 * 线程A和线程B频繁切换,给人的感觉就是一会儿播放音乐,一会儿运行游戏 * 电影院采用胶卷播放电影,一个胶卷一个胶卷播放速度达到一定程度之后 * 人类的眼睛产生了错觉,感觉是动画的,这说明人类的反应速度很慢,就像一根钢针扎到手上,到最终感觉到疼,这个过程是需要“很长的”时间,在这个期间计算机可以 * 进行亿万次的循环,所以计算机的执行速度很快 * */
3、分析程序存在几个线程
/* * 大家分析有几个线程? * 1、一个线程,垃圾回收器 * 2、只有一个主线程,没有启动分支栈,没有启动分支栈,一个栈中,自上而下的顺序执行 * */ public class HashSet01 { public static void main(String[] args) throws IOException, ClassNotFoundException { System.out.println("main begin"); m1(); System.out.println("main over"); } private static void m1(){ System.out.println("m1 begin"); m2(); System.out.println("m2 over"); } private static void m2(){ System.out.println("m2 begin"); m3(); System.out.println("m2 over"); } private static void m3(){ System.out.println("m3 execute"); }
4、实现线程的两种方式
/* * 实现线程的第一种方式: * 编写一个类,直接继承java.lang.Thread 重写run方法 * 怎么创建线程对象?new就可以了 * 怎么启动线程呢?调用线程对象的start方法 * 注意:亘古不变的道理:方法体中的代码永远都是自上而下的顺序依次逐行执行 * 以下程序的输出结果有这样的特点: * 有先有后 * 有多有少 * 第一种方式: * 定义线程类 * public class MyTread extends Thread{ * public void run(){ * * } * //创建线程对象 * MyThread my = new MyThread(); * //启动线程 * my.start(); * 第二种方式:编写一个类,实现java.lang.Runnable接口,实现run方法 * //定义一个可运行的类 * public class MyRunnable implements Runnable { * public void run(){ * } * } * //创建线程对象 * Thread t = new Thread(new MyRunnable()); * //启动线程 * t.start() * } * 注意:第二种方式实现接口比较常用,因为一个类实现了接口,它还可以继承其他的类,更灵活 * * * * * * */ public class ThreadTest { public static void main(String[] args) { //这里是main方法,这里的代码属于主线程,在主栈中运行 //新建一个分支线程对象 MyThread myThread = new MyThread(); // myThread.run(); //不会启动线程,不会分配新的分支栈(这种方式是分支线程) //启动线程 //start()方法的作用是:启动一个分支线程,在JVM中开辟一个新的栈空间,这段代码任务完成之后,瞬间就结束了 //这段代码的任务只是为了开启一个新的栈空间,只要新的栈空间开辟出来,start()方法就结束了,线程就启动成功了 //启动成功的线程会自动调用run方法,并且run方法在分支栈的栈底部(压栈) //run方法在分支栈的底部,main方法在主栈的栈底部,run和main是平级的 myThread.start(); //开辟分支栈 //这里的代码还是运行在主线程中 for(int i = 0; i < 1000; i++){ System.out.println("主线程----》" + i); } //创建线程对象,采用匿名内部类方式 //这是通过一个没有名字的类,new出来的对象 Thread t = new Thread(new Runnable() { @Override public void run() { for(int i = 0; i < 100; i++){ System.out.println("分支线程》》》》》》》》" + i); } } }); //启动线程 t.start(); for(int i = 0; i < 100; i++){ System.out.println("主线程》》》》》》》》》》》" + i); } } } class MyThread extends Thread{ public void run(){ //编写程序,这段程序运行在分支线程中(分支栈) for(int i = 0; i < 1000; i++){ System.out.println("分支线程-----》" + i); } } }
/** * Created by Mike */ public class ThreadTest01 { /* * * 实现java.lang.Runnable接口 * */ public static void main(String[] args) { //创建一个可运行的对象 MyRunnnable r = new MyRunnnable(); //将可运行的对象封装为一个线程对象 Thread t = new Thread(r); //启动线程 t.start(); //主线程 for(int i = 0; i < 1000; i++){ System.out.println("主线程》》》》》》》》》》" + i); } } } // 这并不是一个线程,而是一个可运行的类,它还不是一个线程 class MyRunnnable implements Runnable{ @Override public void run() { for(int i = 0; i < 1000; i++){ System.out.println("分支线程》》》》》》》》》》" + i); } } }
5、获取当前线程对象
/* * * 1、怎么获取当前线程对象? * static Thread currentThread() * 2、获取线程对象的名字 * String name = 线程对象.getName() * 3、修改线程对象的名字 * "线程对象".setName(newName) * 4、当线程没有设置名字的时候,默认名字有什么规律 * Thread-0 * Thread-1 * */
//创建线程对象 MyThread t = new MyThread(); //设置线程名字 t.setName("tttt"); //获取线程名字 String tName = t.getName(); System.out.println(tName); //Thread 0 //启动线程 t.start(); System.out.println();
for(int i = 0; i < 1000; i++){ //currentThread 就是当前线程对象,出现在哪就是当前线程对象 Thread currentThread = Thread.currentThread(); System.out.println("分支线程》》》》》》》》》》" + i); }
6、终断线程的睡眠
public static void main(String[] args) { /* * 关于线程的sleep方法 * static void sleep(long millis) * 1、静态方法:Thread.sleep(100); * 2、参数是毫秒 * 3、作用:让当前线程进入休眠,进入“阻塞状态”,放弃占有CPU时间片,让给其他线程使用 * 这行代码出现在A线程中,A线程就会进入休眠 * 这行代码出现在B线程中,B线程就会进入休眠 * 4、Thread.sleep()方法,可以做到这种效果,间隔特定的时间,去执行一段特定的代码,每隔多久执行一次 * 5、sleep睡眠太久了,如果希望半道上醒来,要终止线程的睡眠,而不是中断线程的执行 "线程对象".interrupt() * * * */ //让当前线程进入休眠,睡眠5秒 //当前线程是主线程 Thread t = new Thread(new MyRunnnable()); t.start(); t.interrupt(); System.out.println(Thread.currentThread().getName()); } } // 这并不是一个线程,而是一个可运行的类,它还不是一个线程 class MyRunnnable implements Runnable{ //重点:run()当中的异常不能throws,只能try catch //因为run()方法在父类中没有抛出任何异常,子类不能比父类抛出更多的异常 @Override public void run() { System.out.println("分支线程》》》》》》》》》》"); try { Thread.sleep(1000 * 60 * 60); } catch (InterruptedException e) { e.printStackTrace(); } //终断t线程的睡眠(这种终断睡眠的方式,依靠了java的异常处理机制 Thread.interrupted(); System.out.println("线程终断"); } }