1.概念
/*
* 进程:
* 正在运行的程序,是系统进行资源分配和调用的独立单位。
* 每一个进程都有它自己的内存空间和系统资源。
* 线程:
* 是进程中的单个顺序控制流,是一条执行路径
* 一个进程如果只有一条执行路径,则称为单线程程序。
* 一个进程如果有多条执行路径,则称为多线程程序。
*
* 举例:
* 扫雷程序,迅雷下载
*
* 大家注意两个词汇的区别:并行和并发。
* 前者是逻辑上同时发生,指在某一个时间内同时运行多个程序。
* 后者是物理上同时发生,指在某一个时间点同时运行多个程序。
*
* Java程序的运行原理:
* 由java命令启动JVM,JVM启动就相当于启动了一个进程。
* 接着有该进程创建了一个主线程去调用main方法。
*
* 思考题:
* jvm虚拟机的启动是单线程的还是多线程的?
* 多线程的。
* 原因是垃圾回收线程也要先启动,否则很容易会出现内存溢出。
* 现在的垃圾回收线程加上前面的主线程,最低启动了两个线程,所以,jvm的启动其实是多线程的。
*/
2.多线程的实现
01.继承Thread类
步骤
A:自定义类MyThread继承Thread类。
B:MyThread类里面重写run()?
为什么是run()方法呢?
不是类中的所有代码都需要被线程执行的。 而这个时候,为了区分哪些代码能够被线程执行,java提供了Thread类中的run()用来包含那些被线程执行的代码。
C:创建对象
D:启动线程
02.调用run()方法为什么是单线程的呢?
因为run()方法直接调用其实就相当于普通的方法调用,所以你看到的是单线程的效果
要想看到多线程的效果,就必须说说另一个方法:start()
面试题:run()和start()的区别?
run():仅仅是封装被线程执行的代码,直接调用是普通方法
start():首先启动了线程,然后再由jvm去调用该线程的run()方法。
创建两个线程对象,启用这两个对象的start()方法才算进行的多线程,如果一个对象调用两次start方法会出现异常IllegalThreadStateException:非法的线程状态异常。
03.如何获取或设置线程名称
public final String getName():获取线程的名称。
public final void setName(String name):获取线程的名称。
public static Thread currentThread():返回当前正在执行的线程对象 Thread.currentThread().getName()
带参构造方法给线程起名字
MyThread my1 = new MyThread("线程一");记得给子类添加构造方法。
04.线程的调度
public final int getPriority():返回线程对象的优先级
public final void setPriority(int newPriority):更改线程的优先级
注意:
线程默认优先级是5。
线程优先级的范围是:1-10。
05.线程控制
public static void sleep(long millis) 线程休眠
public final void join():等待该线程终止
public static void yield():暂停当前正在执行的线程对象,并执行其他线程。
线程守护
public final void setDaemon(boolean on):将该线程标记为守护线程或用户线程。
当正在运行的线程都是守护线程时,Java 虚拟机退出。 该方法必须在启动线程前调用。
public class ThreadDaemon extends Thread {
@Override
public void run() {
for (int x = 0; x < 100; x++) {
System.out.println(getName() + ":" + x);
}
}
}
public class ThreadDaemonDemo {
public static void main(String[] args) {
ThreadDaemon td1 = new ThreadDaemon();
ThreadDaemon td2 = new ThreadDaemon();
td1.setName("关羽");
td2.setName("张飞");
// 设置收获线程
td1.setDaemon(true);
td2.setDaemon(true);
td1.start();
td2.start();
Thread.currentThread().setName("刘备");
for (int x = 0; x < 5; x++) {
System.out.println(Thread.currentThread().getName() + ":" + x);
}
}
}
06.线程控制
线程中断
public final void stop():让线程停止,过时了,但是还可以使用。
public void interrupt():中断线程。 把线程的状态终止,并抛出一个InterruptedException。
public class ThreadStopDemo {
public static void main(String[] args) {
ThreadStop ts = new ThreadStop();
ts.start();
// 你超过三秒不醒过来,我就干死你
try {
Thread.sleep(3000);
// ts.stop();
ts.interrupt();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class ThreadStop extends Thread {
@Override
public void run() {
System.out.println("开始执行:" + new Date());
// 我要休息10秒钟,亲,不要打扰我哦
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
// e.printStackTrace();
System.out.println("线程被终止了");
}
System.out.println("结束执行:" + new Date());
}
}
07.面试题:线程的生命周期
1.新建:创建线程对象
2.就绪:有执行资格,没有执行权
3.运行:有执行资格,有执行权
可能碰到阻塞,此时没有执行资格没有执行权,当被激活时重新回到就续状态
4.死亡:对象变为垃圾,等待回收
![线程周期](https://img-blog.csdn.net/20170814151129641?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvZ2lybF9uaW5l/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
08.线程实现的第二种方式:实现Runable接口
步骤:
A:自定义类MyRunnable实现Runnable接口
B:重写run()方法
C:创建MyRunnable类的对象
D:创建Thread类的对象,并把C步骤的对象作为构造参数传递
public class MyRunnable implements Runnable {
@Override
public void run() {
for (int x = 0; x < 100; x++) {
// 由于实现接口的方式就不能直接使用Thread类的方法了,但是可以间接的使用
System.out.println(Thread.currentThread().getName() + ":" + x);
}
}
}
public class MyRunnableDemo {
public static void main(String[] args) {
// 创建MyRunnable类的对象
MyRunnable my = new MyRunnable();
// Thread(Runnable target, String name)
Thread t1 = new Thread(my, "林青霞");
Thread t2 = new Thread(my, "刘意");
t1.start();
t2.start();
}
}
两种方式的比较
09.如何解决线程安全呢?
要想解决问题,就要知道哪些原因会导致出问题:(而且这些原因也是以后我们判断一个程序是否会有线程安全问题的标准)
A:是否是多线程环境
B:是否有共享数据
C:是否有多条语句操作共享数据
A和B的问题我们改变不了,我们只能想办法去把C改变一下。
思想:
把多条语句操作共享数据的代码给包成一个整体,让某个线程在执行的时候,别人不能来执行。
问题是我们不知道怎么包啊?其实我也不知道,但是Java给我们提供了:同步机制。
同步代码块:
synchronized(对象){
需要同步的代码;
}
A:对象是什么呢?
我们可以随便创建一个对象试试。
B:需要同步的代码是哪些呢?
把多条语句操作共享数据的代码的部分给包起来
注意:
同步可以解决安全问题的根本原因就在那个对象上。该对象如同锁的功能。
多个线程必须是同一把锁
public class SellTicket implements Runnable {
// 定义100张票
private int tickets = 100;
//创建锁对象
private Object obj = new Object();
@Override
public void run() {
while (true) {
synchronized (obj) {
if (tickets > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()
+ "正在出售第" + (tickets--) + "张票");
}
}
}
}
}
public class SellTicketDemo {
public static void main(String[] args) {
// 创建资源对象
SellTicket st = new SellTicket();
// 创建三个线程对象
Thread t1 = new Thread(st, "窗口1");
Thread t2 = new Thread(st, "窗口2");
Thread t3 = new Thread(st, "窗口3");
// 启动线程
t1.start();
t2.start();
t3.start();
}
}
A:同步代码块的锁对象是谁呢?
* 任意对象。
*
* B:同步方法的格式及锁对象问题?
* 把同步关键字加在方法上。
*
* 同步方法是谁呢?
* this
*
* C:静态方法及锁对象问题?
* 静态方法的锁对象是谁呢?
* 类的字节码文件对象。