1.什么是进程,线程,多线程
进程是系统中正在运行的程序
线程是进程的执行单元,执行路径
多线程是多个线程并发执行的技术.
2.什么情况下使用多线程:
(1).单线程不能满足业务需要.比如火车票售票系统,在同一时间可能有多个请求,这些请求之间是相互独立的,没有依赖关系,后台必须通过多线程保证对用户的及时响应.
(2).单线程不能满足性能要求,通过多线程提高效率.比如,多线程可以用一个线程专门用于读写文件,其他的线程用于对读取数据进行处理,这样才有可能更好地利用 CPU 资源。
3.什么是线程安全
如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码,如果每次运行结果和单线程运行的结果是一样的,那就是线程安全的。如果运行结果和单线程的运行结果不同,就为线程不安全.
4.多线程的两种实现方式
(1)继承Thread,重写run()方法
public class MyThread extends Thread {
@Override
public void run() {
for (int x = 0; x < 100; x++) {
System.out.println(getName()+ x);
}
}
}
(2)实现Runnable,重写run()方法
public class MyRunnable implements Runnable {
@Override
public void run() {
for (int x = 0; x < 100; x++) {
// getName()方法是Thread类的,而MyRunnable只实现了
Runnable接口,本身没有getName(),所以不能使用。
System.out.println(Thread.currentThread().getName()
+ "---hello"+ x);
}
}
}
一般使用第二种方式,因为方式一只能单继承,有一定的局限性,而方式二只创建一个对象,实现了数据和操作更好的分离.
5.通过火车票案例了解多线程(四个窗口同时出售火车票)
public class TicketRunnable implements Runnable {
private static int tickets = 100;
@Override
public void run() {
while (true) {
//通过加锁,保证线程安全
synchronized (this) {
if (tickets > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()
+ "正在出售第" + (tickets--) + "张票");
}
else {
break;
}
}
}
}
}
主函数
public class RunnableTest {
public static void main(String[] args) {
TicketRunnable tr = new TicketRunnable();
Thread t1 = new Thread(tr,"窗口1");
Thread t2 = new Thread(tr,"窗口2");
Thread t3 = new Thread(tr,"窗口3");
Thread t4 = new Thread(tr,"窗口4");
t1.start();
t2.start();
t3.start();
t4.start();
}
}
注:(1).多线程中的锁对象可以为任意对象,但必须为同一对象
静态方法锁对象:是当前类的字节码文件对象 类名.class
(2).解决线程同步问题的两种方式
a、同步代码块
synchronized(锁对象) {....} //锁对象可以是任意类型
b、同步方法
格式:public synchronized 返回值 方法名(){....}
锁对象:this
(3)如何判断线程是否安全
a、看有没有共享数据
b、看操作共享数据的语句是不是多条语句
c、看是不是在多线程的环境中
6.线程死锁问题
public class DieLock extends Thread {
private boolean flag;
public DieLock(boolean flag) {
this.flag = flag;
}
@Override
public void run() {
if (flag) {
synchronized (MyLock.objA) {
System.out.println("true -- objA");
synchronized (MyLock.objB) {
System.out.println("true -- objB");
}
}
} else {
synchronized (MyLock.objB) {
System.out.println("false -- objB");
synchronized (MyLock.objA) {
System.out.println("false -- objA");
}
}
}
}
}
死锁原因总结
线程1自身拿着一个锁:A锁,线程2自身拿着一个锁:B锁
当线程1要用B锁,线程B要用A锁的时候就会发生死锁
如何避免死锁:
不嵌套使用锁
7.线程的优先级
1、线程优先级级别
线程默认优先级是5。范围是1-10
Thread.MAX_PRIORITY //10
Thread.MIN_PRIORITY //1
Thread.NORM_PRIORITY //5
2、方法
public final int getPriority():获取线程优先级
public final void setPriority(int newPriority):更改线程的优先级
3、注意
优先级可以在一定的程度上,让线程获较多的执行机会
4、举例
MyThread t = new MyThread();
System.out.println(t.getPriority());
t.setPriority(Thread.MAX_PRIORITY);
8.线程的等待唤醒机制
(1).前提: 两个线程共用一把锁,此时可以调用该锁的wait和notify方法,实现等待唤醒机制
(2)sleep和wait的区别
wait:是Object类的方法,可以不用传递参数,释放锁对象
sleep:是Thread类的静态方法,需要传递参数
案例:多线程实现设置和获取学生信息
Student类
public class Student {
private String name;
private int age;
private boolean flag = false;
public synchronized void set(String name, int age) {
// 如果有数据就等待
if (this.flag) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 设置值
this.name = name;
this.age = age;
// 修改标记
this.flag = true;
this.notify();
}
public synchronized void get() {
// 如果没有数据就等待
if (!this.flag) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 获取值
System.out.println(this.name + "***" + this.age);
// 修改标记
this.flag = false;
this.notify();
}
}
SetStudent类
public class SetStudent implements Runnable {
private Student s;
public SetStudent(Student s) {
this.s = s;
}
@Override
public void run() {
int x = 0;
while (true) {
if (x % 2 == 0) {
s.set("林青霞", 26);
} else {
s.set("刘意", 29);
}
x++;
}
}
}
GetStudent类
public class GetStudent implements Runnable {
private Student s;
public GetStudent(Student s) {
this.s = s;
}
@Override
public void run() {
while (true) {
s.get();
}
}
}
StudentTest测试类
public class StudentTest {
public static void main(String[] args) {
Student s = new Student();// 资源类
SetStudent ss = new SetStudent(s);
GetStudent gs = new GetStudent(s);
Thread t1 = new Thread(ss);
Thread t2 = new Thread(gs);
t1.start();
t2.start();
}
}