java多线程入门-附死锁案例(下)

143 阅读4分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第2天,点击查看活动详情

1. 多线程的常用方法二

  1. yield:线程的让步,主动通过代码挂起,让其他程序执行,但是让步的时间不确定,所以可能成功也可能不成功

  2. join:线程插队,插队的 线程一旦插队成功,肯定执行完插入的线程的所有任务,在一个线程调用另一个线程的join,调用线程与当前线程合并,多线程变为单线程

  3. 线程优先级 ->提升 降低
            public static final int MAX_PRIORITY   //10
            public static final int MIN_PRIORITY   //1
            public static final int NORM_PRIORITY  //5  (默认)
    
    • 方法补充:getPriority、setPriority (得到优先级、设置优先级)1,5,107

image-20220809113126759.png

1.1 join的练习

需求:主线程每隔1s,输出一个你好 i ,一共输出10次,输出到你好 5 是,启动子线程,子线程进行插队,最后完成主线程的输出,程序结束

//核心代码
 public static void main(String[] args) throws InterruptedException {
        son son = new son();
        Thread td = new Thread(son);
        for (int i = 0; i < 10; i++) {
            Thread.sleep(1000);
            System.out.println("hi"+i);
            if(i==5) {
                td.start();
                td.join();
            }
        }
        System.out.println("主线程结束...");
    }
}
class son implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("hellow"+i);
        }
        System.out.println("子线程结束...");
    }
}

结果:

图片1.png

2 . 用户线程与守护线程

  1. 用户线程:也叫工作线程,当线程的任务执行完或通知方式结束
  2. 守护线程:一般是为工作线程服务,当所有的用户线程结束,守护线程会自动结束
  3. 常见的守线程:GC(垃圾回收器)
package study_hsp;
​
public class Shengmingzhouqi {
    public static void main(String[] args) throws InterruptedException {
         Thread td = new MyDaenonThread();
         td.setDaemon(true);  // 将td设置为守护线程,当所有线程结束后,td也就自动结束
         td.start();
         for (int i=0;i<100;i++) {
             Thread.sleep(100);
             System.out.println("天天敲代码,一天不看开发文档,浑身不爽...");
         }
​
    }
}
class MyDaenonThread extends Thread {
    @Override
    public void run() {
        for (;;) { // 死循环
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("我在守护着你...");
        }
    }
}

运行截图:

设置前:

守护线程1.png 设置后:

守护线程2.png

3. 线程的生命周期(补充)

声明周期.png

  • 程序在运行状态碰到synchronized,会去锁池lockpool 找锁,此时程序会释放CPU时间片,如果找到锁了之后进入就绪状态进行排队

4. 线程同步(补充)

4.1线程同步机制

在多线程中,一些数据不允许被多个线程同时访问,线程同步机制就能保证数据在同一时刻,最多又一个线程访问,以保证数据的完整性

4.2具体方法- synchronized

  1. 修饰代码块 ,非常灵活
synchronized(共享对象){} 
  1. 修饰实例方法,不灵活

    2.1 共享对象就是this不能改变

    2.2 整个方法体都变成同步代码块

    2.3 优点:安全 this可以不写(一般不写)

    2.4 使用场景:1.同步整个方法体 2. 共享对象就是this

    public synchronized(this) void copyInto(Object[] anArray) {}

5. 开发中如何解决线程安全问题

  1. 能用局部变量,去堆中开辟空间,不要共享(一个线程一个数据对象)
  2. 同居加锁-->没有其他方法的情况下
  3. static 类级别 类名
  1. 对象锁:一个对象一把锁
  1. 类锁: 一个类只有一把锁
  1. 线程安全问题出发的条件
  1. 允许多并发
  2. 共享对象
  3. 修改(增加减少)数据
  1. java中哪些变量会出现线程安全问题?
  1. 局部:栈,局部不能共享,不会出现
  2. 实例: 堆空间,能共享
  3. 静态:方法区,能共享
  4. 局部,常量: 不可能出现线程安全问题,实例、静态可能出现
  1. 什么时候使用StringBuffer,StringBuilder

StringBuffer:线程安全

StringBuilder:线程不安全

局部都用:StringBuilder

堆、 方法区:StringBuffer,StringBuilder

  1. 已经学过的线程安全与不安全API集合

StringBuffer 、Hashtable、vector、Propoties: 线程安全

StringBuilder 、arrayList、LinkedList 、 HashSet、HashMap、TreeSet、TreeMap:线程不安全

6 .死锁

代码实现:

package classes;
​
public class SiSuo {
    public static void main(String[] args) {
        Object o1 = new Object();
        Object o2 = new Object();
        Runa Ta = new Runa(o1,o2);
        Runb Tb = new Runb(o1,o2);
​
        Thread td1 = new Thread(Ta);
        Thread td2 = new Thread(Tb);
        td1.setName(" td1");
        td2.setName(" td2");
         td1.start();
         td2.start();
​
    }
}
​
class Runa implements Runnable {
    Object o1;
    Object o2;
​
    public Runa(Object o1, Object o2) {
        this.o1 = o1;
        this.o2 = o2;
    }
​
    @Override
    public void run() {
         String  name  = Thread.currentThread().getName();
        synchronized (o1) {
            System.out.println(name+ "锁住了o1... ");
            synchronized (o2) {
                System.out.println("想拿o2 把o2 锁住(关起来)...");
            }
      }
    }
}
​
class Runb implements Runnable {
    Object o1;
    Object o2;
​
    public Runb(Object o1, Object o2) {
        this.o1 = o1;
        this.o2 = o2;
    }
​
    @Override
    public void run() {
        String  name  = Thread.currentThread().getName();
        synchronized (o2) {
            System.out.println(name+"锁住了o2... ");
            synchronized (o1) {
                System.out.println("想拿o1 把o1 锁住(关起来)...");
            }
        }
    }
}
  • 死锁现象

死锁.png 死锁现象.png

7 .总结

  1. 多线程的难度在于,如何处理线程安全问题,即synchronize的使用,在实例代码块,类名,代码块上的使用,用法比较灵活,而且不能确定哪儿中方法好用,只能根据实际情况具体分析,线程安全问题也要满足三个条件才能出发,一定要牢记;
  1. 死锁问题,只是作为一个了解,在开发中一定不要去写死锁,会写死锁是为了避免在开发中写死锁,知道是什么原理

\