携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第2天,点击查看活动详情
1. 多线程的常用方法二
yield:线程的让步,主动通过代码挂起,让其他程序执行,但是让步的时间不确定,所以可能成功也可能不成功
join:线程插队,插队的 线程一旦插队成功,肯定执行完插入的线程的所有任务,在一个线程调用另一个线程的join,调用线程与当前线程合并,多线程变为单线程
线程优先级 ->提升 降低 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
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("子线程结束...");
}
}
结果:
2 . 用户线程与守护线程
- 用户线程:也叫工作线程,当线程的任务执行完或通知方式结束
- 守护线程:一般是为工作线程服务,当所有的用户线程结束,守护线程会自动结束
- 常见的守线程: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("我在守护着你...");
}
}
}
运行截图:
设置前:
设置后:
3. 线程的生命周期(补充)
- 程序在运行状态碰到synchronized,会去锁池lockpool 找锁,此时程序会释放CPU时间片,如果找到锁了之后进入就绪状态进行排队
4. 线程同步(补充)
4.1线程同步机制
在多线程中,一些数据不允许被多个线程同时访问,线程同步机制就能保证数据在同一时刻,最多又一个线程访问,以保证数据的完整性
4.2具体方法- synchronized
- 修饰代码块 ,非常灵活
synchronized(共享对象){}
修饰实例方法,不灵活
2.1 共享对象就是this不能改变
2.2 整个方法体都变成同步代码块
2.3 优点:安全 this可以不写(一般不写)
2.4 使用场景:1.同步整个方法体 2. 共享对象就是this
public synchronized(this) void copyInto(Object[] anArray) {}
5. 开发中如何解决线程安全问题
- 能用局部变量,去堆中开辟空间,不要共享(一个线程一个数据对象)
- 同居加锁-->没有其他方法的情况下
- static 类级别 类名
- 对象锁:一个对象一把锁
- 类锁: 一个类只有一把锁
- 线程安全问题出发的条件
- 允许多并发
- 共享对象
- 修改(增加减少)数据
- java中哪些变量会出现线程安全问题?
- 局部:栈,局部不能共享,不会出现
- 实例: 堆空间,能共享
- 静态:方法区,能共享
- 局部,常量: 不可能出现线程安全问题,实例、静态可能出现
- 什么时候使用StringBuffer,StringBuilder
StringBuffer:线程安全
StringBuilder:线程不安全
局部都用:StringBuilder
堆、 方法区:StringBuffer,StringBuilder
- 已经学过的线程安全与不安全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 锁住(关起来)...");
}
}
}
}
- 死锁现象
7 .总结
- 多线程的难度在于,如何处理线程安全问题,即synchronize的使用,在实例代码块,类名,代码块上的使用,用法比较灵活,而且不能确定哪儿中方法好用,只能根据实际情况具体分析,线程安全问题也要满足三个条件才能出发,一定要牢记;
- 死锁问题,只是作为一个了解,在开发中一定不要去写死锁,会写死锁是为了避免在开发中写死锁,知道是什么原理
\