多线程
1. 多线程实现
1.1 继承Thread类
-
继承Thread类
-
重写run方法
-
创建实例
-
启动线程 start()
-
先创建一个线程类,然后在另一个类中,创建该对象,然后run即可
-
getName()可以获取线程名称
-
setName()可以设置线程名称
public class Demo01 {
public static void main(String[] args) {
MyThread1 t1 = new MyThread1();
MyThread2 t2 = new MyThread2();
t1.start();
t2.start();
}
}
class MyThread1 extends Thread{
@Override
public void run() {
int i = 0;
for (i = 0; i<=1000; i++){
System.out.println(getName()+i);
}
}
}
class MyThread2 extends Thread{
@Override
public void run() {
int i = 0;
for (i = 0; i<=1000; i++){
System.out.println(getName()+i);
}
}
}
- 也可以使用有参构造方法直接设置线程名称,但是要重写有参构造方法。
- MyThread t1 = new MyThread(“thread 01”)
- Thread.currentThread().getName();可以获取当前线程的名字(比如main函数的)
1.2 实现Runnable接口
public class Demo02 {
public static void main(String[] args) {
// new Thread(new Myrunnable()).start();
// new Thread(new Myrunnable()).start();
Myrunnable m = new Myrunnable();
Thread t1 = new Thread(m);
Thread t2 = new Thread(m);
t1.start();
t2.start();
}
}
class Myrunnable implements Runnable{
@Override
public void run() {
for (int i=0;i<=1000;i++){
System.out.println(Thread.currentThread().getName()+"---"+i);
}
}
}
1.3 线程池搭配Callable泛型接口(返回值也是泛型)
1.4 匿名内部类实现
- 下面的同时又Thread子类和Runnable接口,最后只会执行world线程,多次覆盖只最后一次有效
2. 线程调度和控制
2.1 调度
- getPriority获取线程优先级,默认5,最大10,最小1
- 由于线程调度随机性,不是每一次都是高优先级先于低优先级运行,多次运行满足概率 高>低。
MyThread1 t1 = new MyThread1();
MyThread2 t2 = new MyThread2();
t1.start();
t2.start();
System.out.println(t1.getPriority());
2.2 控制
-
sleep线程休眠,单位毫秒,1s=1000ms;静态方法Thread.sleep(1000)
-
join,等待该线程结束在执行其他线程。实例方法。
-
yield,线程手动切换,Thread.yield(),让多个线程更加和谐,但是不能保证每个线程一个运行一次,还是有几率随机调度。
-
守护线程,设置守护线程后,如果主线程结束,那么守护线程将不会继续执行。就像老家没了,坦克就输了。
-
在JVM中,所有非守护线程都执行完毕后,无论有没有守护线程,虚拟机都会自动退出。因此,JVM退出时,不必关心守护线程是否已结束。
3. 线程生命周期
4. 线程同步
- 解决多线程读写同一变量
- 像下面这样,每次创建的锁对象就不会是一个了,所以才能成功锁住。
4.2 同步方法
- 同步方法的锁对象就是this,自身对象
4.3 线程安全的集合类
- Collection的静态方法,直接返回一个线程安全集合对象。
4.4 Lock
5. 死锁
5.2 代码示例
public class Demo04 {
public static void main(String[] args) {
DeadLock d1 = new DeadLock(true);
DeadLock d2 = new DeadLock(false);
d1.start();
d2.start();
}
}
class DeadLock extends Thread {
private boolean flag;
public DeadLock(boolean flag){
this.flag = flag;
}
// 下面代码,如果thread0获取锁a,打印if obja,然后系统调度thread1,thread1获取锁b,打印else objb
// 然后系统再次切换回thread0,此时thread0想要获取锁b,但是此时锁b还没有释放,就会形成死锁
@Override
public void run() {
if (flag){
synchronized (MyLock.obja){
System.out.println("if obja");
synchronized (MyLock.objb){
System.out.println("if objb");
}
}
}else {
synchronized (MyLock.objb){
System.out.println("else objb");
synchronized (MyLock.obja){
System.out.println("else obja");
}
}
}
}
}
class MyLock{
// 定义2把锁,给synchronized作为参数锁
public static final Object obja = new Object();
public static final Object objb = new Object();
}
6. 线程间通信
6.1 等待唤醒
7. 线程组
- 默认主线程组
8. 线程池
8. 定时器使用
import java.util.Timer;
import java.util.TimerTask;
public class Demo06 {
public static void main(String[] args) {
Timer t = new Timer();
// 3秒后开始执行,然后每隔2秒钟,调度一次
t.schedule(new MyTimerTask(), 3000, 2000);
}
}
class MyTimerTask extends TimerTask{
@Override
public void run() {
System.out.println("beng beng");
}
}
9. 常见面试题
9.1 多线程几种实现方案
- 继承Thread类
- 实现Runnable接口
- 泛型接口Callable搭配线程池
9.2 同步几种方式
- 同步代码块 synchronized
- 同步方法
9.3 线程run和start方法区别
- run就是普通函数调用
- start,jvm开启一个新线程,然后调用run方法
9.4 sleep和wait区别
- sleep必须指定时间,不释放锁(Thread.sleep() )
- wait可以也可以不指定时间,自动释放锁( Object.wait() )
9.5 为什么wait(),notify(),notifyAll()都定义在Object类中
- 因为这些方法调用依赖锁对象,而同步代码块的锁对象是任意锁。Object代表任意锁。
9.6 线程状态和生命周期
- 参考前面章节!