线程
(接上篇)
static void yield(): 线程让步
暂停当前正在执行线程,把执行机会给优先级相同或更高的线程
若队列中没有同优先级的线程,忽略此方法
Thread.yield();//利用Thread.yield的方式进行调用一般写在对应类的run方法中
join(): 当某个程序执行流中调用其他线程的join()方法时,调用线程将被阻塞,直到join()方法假如
的join线程执行完为止。
t0.start();
t1.start();
try {
t0.join();//阻塞t0进程
} catch (InterruptedException e) {
e.printStackTrace();
}
static void sleep(long millis): 可以让线程睡眠,后面的参数可以是毫秒数,1000 ms=1 s
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
stop(): 强制线程生命周期结束。
t0.stop();//利用线程.stop()的方式来结束线程
boolean isAlive(): 返回boolean,用来判断线程是否还存活。
Thread t0 = new Thread(run0);//定义线程
System.out.println("t0是否存活:" + t0.isAlive())//第一次
t0.start();
System.out.println("t0是否存活:" + t0.isAlive());//第二次
try {
t0.join();//阻塞t0进程
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("t0是否存活:" + t0.isAlive());//第三次
结果为:
t0是否存活:false
t0是否存活:true
t0是否存活:false
通过以上结果可以看出:一个线程只有在运行时才存活,他的生命周期并不是从定义的瞬间就开始了,只有在start之后才开始,当他运行结束,他的生命周期便结束。
5.线程生命周期
生命周期的五种状态
新建(new Thread)
当创建Thread类的一个实例(对象)时,此线程进入新建状态(未被启动)。
例如:
Thread t1=new Thread();
就绪(runnable)
线程已经被启动,正在等待被分配给CPU时间片,也就是说此时线程正在就绪队列中排队等候得到CPU资源
例如:
t1.start();
运行(running)
线程获得CPU资源正在执行任务(run()方法),此时除非此线程自动放弃CPU资源或者有优先级更高的线程进入,线程将一直运行到结束。
堵塞(blocked)
由于某种原因导致正在运行的线程让出CPU并暂停自己的执行,即进入堵塞状态。
正在睡眠:用sleep(long t) 方法可使线程进入睡眠方式。一个睡眠着的线程在指定的时间过去可进入就绪状态。
死亡(dead)
当线程执行完毕或被其它线程杀死,线程就进入死亡状态,这时线程不可能再进入就绪状态等待执行。
自然终止:正常运行run()方法后终止
异常终止:调用stop()方法让一个线程终止运行
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PDfBQ1sX-1618500489085)(笔记图片\线程的生命周期.jpg)]
6.线程同步(排队)
线程同步就是线程排队了,线程排队了就会牺牲一部分效率,但可以获得数据安全。
例子:当两个线程对同一个银行账户进行操作时,两个同时并发了,那么结果就会对其中的银行余额不安全
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EhG1jX8s-1618500489088)(笔记图片\线程同步.png)]
6.1 通过同步代码块synchronized(排它锁)
/*synchronized后面小括号内的数据非常重要,必须是你想要排队线程所共享的数据
*/
synchronized (this) {
try {
Thread.sleep(1000);//进程睡眠1s,模拟网络延迟
} catch (InterruptedException e) {
e.printStackTrace();
}
if (money > getBalance()) {
System.out.println("余额不足,还剩:" + getBalance());
} else {
setBalance(getBalance() - money);
}
System.out.println("线程名:" + Thread.currentThread().getName() + " 本次取款金额为:" + money + "当前余额为: " + getBalance());
}
运行截图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-64jFN6bF-1618500489089)(笔记图片\线程同步2.jpg)]
局部变量永远不会有线程安全问题,因为局部变量不共享。
6.2 synchronized用在实例方法上
/*Synchronize出现在实例方法上
* 这种方式一定锁的是this,不能是其他对象
* 所以这种方式没得挑,所以不灵活
* 出现在方法上表示同步整个方法体(有可能扩大了程序的执行范围,导致程序执行效率降低)
*
* 优点:代码更加精简,
* 如果共享对象就是this,并且需要同步的代码块就是整个方法体,
* 推荐使用这种方法
* */
public synchronized void Draw(double money) {
try {
Thread.sleep(1000);//进程睡眠1s,模拟网络延迟
} catch (InterruptedException e) {
e.printStackTrace();
}
if (money > getBalance()) {
System.out.println("余额不足,还剩:" + getBalance());
} else {
setBalance(getBalance() - money);
}
System.out.println("线程名:" + Thread.currentThread().getName() + " 本次取款金额为:" + money + "当前余额为: " + getBalance());
}
6.3 synchronized用在静态方法上(用来保证静态变量安全)
public synchronized static void Draw(double money) {
}
表示找类锁,类锁永远只有一把
就算创建了一百个对象,那类锁也只有一把
当方法上没有synchronized时,就不会出现排队现象
7.死锁
死锁代码
public class DeadLock {
public static void main(String[] args) {
Object obj1 = new Object();
Object obj2 = new Object();
Thread t0 = new MyThread1(obj1, obj2);
Thread t1 = new MyThread2(obj1, obj2);
t0.start();
t1.start();
}
}
class MyThread1 extends Thread {
Object o1;
Object o2;
public MyThread1(Object o1, Object o2) {
this.o1 = o1;
this.o2 = o2;
}
public void run() {
synchronized (o1) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (o2) {
}
}
}
}
class MyThread2 extends Thread {
Object o1;
Object o2;
public MyThread2(Object o1, Object o2) {
this.o1 = o1;
this.o2 = o2;
}
public void run() {
synchronized (o2) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (o1) {
}
}
}
}