- 定义
进程:正在执行的应用程序
线程:进程的执行单元,执行路径
单线程:一个应用程序只有一条执行路径
多线程:一个应用程序有多条执行路径
多进程的意义? 提高CPU的使用率
多线程的意义? 提高应用程序的使用率 - Java程序的运行原理及JVM的启动是多线程的吗?
A:Java命令去启动JVM,JVM会启动一个进程,该进程会启动一个主线程。
B:JVM的启动是多线程的,因为它最低有两个线程启动了,主线程和垃圾回收线程。 - 多线程的实现方案
3.1继承Thread类
/* 方式1:继承Thread类。
* 步骤
* A:自定义类MyThread继承Thread类。
* B:MyThread类里面重写run()?
* 为什么是run()方法呢?
* C:创建对象
* D:启动线程
*/
public class MyThreadDemo {
public static void main(String[] args) {
// 创建两个线程对象
MyThread my1=new MyThread();
MyThread my2=new MyThread();
my1.start();
my2.start();
}
}
public class MyThread extends Thread{
@Override
public void run() {
// 自己写代码
for (int x = 0; x < 200; x++) {
System.out.println(x);
}
}
}
3.2 实现Runnable接口
/*
* 方式2:实现Runnable接口
* 步骤:
* A:自定义类MyRunnable实现Runnable接口
* B:重写run()方法
* C:创建MyRunnable类的对象
* D:创建Thread类的对象,并把C步骤的对象作为构造参数传递
*/
public class MyRunnableDemo {
public static void main(String[] args) {
// 创建MyRunnable类的对象
MyRunnable my = new MyRunnable();
// 创建Thread类的对象,并把C步骤的对象作为构造参数传递
// Thread(Runnable target)
// Thread t1 = new Thread(my);
// Thread t2 = new Thread(my);
// t1.setName("林青霞");
// t2.setName("刘意");
// Thread(Runnable target, String name)
Thread t1 = new Thread(my, "林青霞");
Thread t2 = new Thread(my, "刘意");
t1.start();
t2.start();
}
}
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 SellTicket implements Runnable {
// 定义100张票
private int tickets = 100;
@Override
public void run() {
while (true) {
// t1,t2,t3三个线程
// 这一次的tickets = 1;
if (tickets > 0) {
// 为了模拟更真实的场景,我们稍作休息
try {
Thread.sleep(100); //t1进来了并休息,t2进来了并休息,t3进来了并休息,
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "正在出售第"
+ (tickets--) + "张票");
//窗口1正在出售第1张票,tickets=0
//窗口2正在出售第0张票,tickets=-1
//窗口3正在出售第-1张票,tickets=-2
}
}
}
}
/*
* 实现Runnable接口的方式实现
*
* 通过加入延迟后,就产生了连个问题:
* A:相同的票卖了多次
* CPU的一次操作必须是原子性的
* B:出现了负数票
* 随机性和延迟导致的
*/
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:是否有共享数据
C:是否有多条语句操作共享数据
多线程安全问题解决方案(三种方式)
A:同步代码块
synchronized(对象) {
需要被同步的代码;
}
这里的锁对象可以是任意对象。
/*注意:
* 同步可以解决安全问题的根本原因就在那个对象上。该对象如同锁的功能。
* 多个线程必须是同一把锁。*/
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();
}
}
B:同步方法:把同步加在方法上,这里的锁对象是this关键字
C:静态同步方法:把同步加在方法上。
- 线程安全类:StringBuffer Vector Hashtable 这些类里面都有Synchronized关键字保证代码同步!