多线程
首先本文是建立在大家对进程和线程有一点了解的基础上的。如进程线程了解不多的话,可以参考
[进程、线程、多线程相关总结 ]。
线程的几种创建方式
- 1.继承Thread类
// 创建线程类的子类
class SubThread extends Thread{
// 重写run方法
@Override
public void run() {
System.out.println(10/0);
for (int i = 0; i < 50; i++) {
System.out.println("run--" + i);
}
}
}
- 2.Runnable接口创建线程
//线程创建方式2(Runnable接口创建线程)
class RunnableImpl implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}
- 3.匿名内部类创建线程
其中又分为继承Thread子类和实现Runnable接口两种方式
// 线程类的子类创建
Thread t1 = new Thread() {
@Override
public void run() {
System.out.println("线程子类的run方法");
}
};
t1.start();
// 接口的实现类创建线程
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println("接口类的run方法");
}
};
// 创建线程对象
Thread thread = new Thread(runnable);
thread.start();
线程的六种状态
- NEW
至今尚未启动的线程处于这种状态。 - RUNNABLE
正在 Java 虚拟机中执行的线程处于这种状态。 - BLOCKED
受阻塞并等待某个监视器锁的线程处于这种状态。 - WAITING
无限期地等待另一个线程来执行某一特定操作的线程处于这种状态。 - TIMED_WAITING
等待另一个线程来执行取决于指定等待时间的操作的线程处于这种状态。 - TERMINATED
已退出的线程处于这种状态。
三种给线程上锁的方法
- 1.声明锁对象,使用synchronized(锁对象)
// 利用接口方法 来保证 访问共享资源
class Tickets implements Runnable {
private int tickets = 100;
// 声明锁对象(保证锁也是线程共享的)
private final Object obj = new Object();
@Override
public void run() {
// 利用循环 保证票都能卖出去、 进行卖票
while (true) {
synchronized (obj) {
// 判断票
if (tickets > 0) {
// 可以卖
System.out.println(Thread.currentThread().getName() + "--" + tickets);
// 卖票
tickets--;
} else {
// 卖完了
break;
}
}
}
}
}
- 2.在方法上加上synchronized关键字
private static synchronized boolean sellTickets() {
if (tickets > 0) {
System.out.println(Thread.currentThread().getName() + "--" + tickets);
// 卖票
tickets--;
return true;
} else {
// 卖完了
return false;
}
}
- 3.使用Lock接口
class Tickets2 implements Runnable {
private int tickets = 100;
// 参数true可以让线程尽量平均的进入锁
private final ReentrantLock lock = new ReentrantLock(true);
@Override
public void run() {
// 利用循环 保证票都能卖出去、 进行卖票
while (true) {
// 使用lock锁
lock.lock();
try {
if (tickets > 0) {
// 可以卖
System.out.println(Thread.currentThread().getName() + "--" + tickets);
// 卖票
tickets--;
} else {
// 卖完了
break;
}
} finally {
// 释放锁
lock.unlock();
}
// 让线程让出cpu的执行资源(可能让出 可能不让出 增加让出几率)
Thread.yield();
}
}
}
死锁
前提:
- 1.至少两个线程
- 2.必须要有锁的嵌套
- 3.两把锁
简单来说就是:
线程1和线程2同时访问有嵌套的同步代码块程序,并且有两个锁:A和B。线程1拿到了A ,向进入下一个代码块需要B锁。线程2拿到了B ,向进入下一个代码块需要A锁。这时谁也进不去,线程进入相互等待的状态,导致程序卡住。