多线程中常用的方法
线程的终止stop
- 不使用JDK提供的stop方法/destroy方法(它们本身也被JDK给废弃了)
- 现在提供了一个boolean型的终止变量,当这个变量置为false,则线程就终止运行。
- 终止线程的方式
- 线程运行完毕正常终止。
- 外部干涉线程终止--->需要加入标识
- 不要使用stop和destroy这样过时的不安全的线程终止方法
示例
public class ThreadStop implements Runnable{
private String name;
//加入标识 标记线程是否可以运行
private boolean flag=true;
public ThreadStop(String name) {
this.name = name;
}
@Override
public void run() {
int i=0;
//线程的终止取决于flag的状态
while (flag){
System.out.print("\t"+name+"---"+i++);
}
}
//对外提供方法改变标识
public void terminate(){
this.flag=false;
}
public static void main(String[] args) {
ThreadStop t=new ThreadStop("李清照");
new Thread(t).start();
for (int i = 0; i < 20; i++) {
if(i==10){
//外部控制线程的终止
t.terminate();
System.out.println();
System.out.println("t is game over");
}
System.out.print("main\t"+i);
}
}
}
线程的暂停 sleep
- sleep方法:指定当前线程阻塞的毫秒数(1000毫秒==1秒)
- sleep存在异常InterruptedException
- sleep指定的时间过去后,线程进入就绪状态。
- sleep可以用来模拟网络延时,倒计时等
- 每一个对象都有一个锁,sleep不会释放锁。
示例一:倒计时
public class ThreadSleep01 {
public static void main(String[] args) {
try {
countDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//模拟倒计时
public static void countDown() throws InterruptedException {
int num = 10;//10秒
while (true) {
Thread.sleep(1000);
System.out.println(num--);
if (num <= 0) {
break;
}
}
}
}
示例二:每一秒获取一次当前时间
public class ThreadSleep02 {
public static void main(String[] args) {
//获取系统当前时间
Date startTime = new Date(System.currentTimeMillis());
while (true) {
try {
Thread.sleep(1000);
//更新系统时间
System.out.println(new SimpleDateFormat("HH:mm:ss").format(startTime));
startTime = new Date(System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
线程的礼让 yield
- 线程的礼让就是让当前正在执行的线程暂停。
- 不是阻塞线程,而是将线程的运行状态转入就绪状态。让cpu调度器重新调度。
- 礼让不一定成功,因为礼让后任然为就绪状态,还是会进行竞争cpu资源。
- 写在那个线程体中那个线程就进行礼让。
示例
public class ThreadYield01 implements Runnable{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("yield"+i);
}
}
public static void main(String[] args) {
ThreadYield01 yield01=new ThreadYield01();
new Thread(yield01).start();
for (int i = 0; i < 100; i++) {
if(i%20==0){
//主线程进行礼让
Thread.yield();
}
System.out.println("main"+i);
}
}
}
//礼让就是让线程从运行状态到就绪状态
//上面的程序是在主线程中每当i是20的倍数就礼让上面run方法中的线程。
//但是礼让不一定成功,因为线程的调度不是人为的,而是cup的调度决定的。
线程的合并join
- join合并线程,待此线程执行完成后,再执行其它的线程。其它的线程就处于阻塞状态
- 可以理解为线程的插队,当一辆车(线程)插队后,被插队的车辆(其它线程)就只能让插队车辆先行,因为被插队,所以在插队的过程中,被插队的车辆处于阻塞的状态(线程阻塞),当插队完成(线程执行完毕),其它车辆继续驾驶(执行其它线程)。
- 写在那个线程体中那个线程就被阻塞了。
示例
package com.cn.Callable;
public class ThreadJoin implements Runnable {
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println("线程vip" + i);
}
}
public static void main(String[] args) throws InterruptedException {
//启动我们的线程
ThreadJoin joinThread = new ThreadJoin();
Thread thread = new Thread(joinThread);
thread.start();
//主线程
for (int i = 0; i < 20; i++) {
if (i == 5) {
thread.join();//插队(main线程被插队)
}
System.out.println("main" + i);
}
}
}
获取线程的状态 getState
线程状态。线程可以处于以下状态之一:
- NEW:尚未启动的线程处于此状态。
- RUNNABLE:在Java虚拟机中执行的线程处于此状态。
- BLOCKED:被阻塞等待监视器锁定的线程处于此状态。
- WAITING:没有时间限制地等待其他线程执行特定操作。
- object.wait()
- thread.join()
- LockSupport.park()
- TIMED_WAITING:在指定时间段内等待其他线程执行特定操作。
- thread.sleep(long millis)
- wait(int timeout) or wait(int timeout, int nanos)
- thread.join(long millis)
- LockSupport.parkNanos
- LockSupport.parkUntil
- TERMINATED:已退出的线程处于此状态。
示例
public class ThreadState {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(() -> {
for (int i = 0; i < 5; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("run方法执行中");
});
//观察线程启动前 NEW
Thread.State state = thread.getState();
System.out.println(state);
//观察启动后 RUNNABLE
thread.start();
state = thread.getState();
System.out.println(state);
while (state != Thread.State.TERMINATED) {//只要现成不终止,就一直输出状态
Thread.sleep(100);
state = thread.getState();//更新线程状态
System.out.println(state);
}
//死亡后的线程不能再启动了,启动会报异常
//thread.start();
}
}
线程优先级 setPriority getPriority()
- java提供一个线程调度器来监控程序启动后进入就绪状态的所有线程,线程调度器按照线程的优先级决定应调度那个线程来执行。
- 优先级的设定建议在start()调用前。
- 优先级低只意味获得调度的概率低,并不是绝对先调用优先级高的线程,后调用优先级低的线程。
- 线程的优先级是从1到10
- NORM_PRIORITY 5
- MIN-PRIORITY 1
- MAX-PRIORITY 10
- 所有的线程的优先级都默认是5
示例
public class ThreadPriority {
public static void main(String[] args) {
//主线程默认优先级为5 不可更改
System.out.println(Thread.currentThread().getName()+"-->"+Thread.currentThread().getPriority());
MyPriority myPriority = new MyPriority();
Thread thread1 = new Thread(myPriority);
Thread thread2 = new Thread(myPriority);
Thread thread3 = new Thread(myPriority);
Thread thread4 = new Thread(myPriority);
Thread thread5 = new Thread(myPriority);
//先设置优先级,再启动
thread1.start();
thread2.setPriority(1);
thread2.start();
thread3.setPriority(4);
thread3.start();
thread4.setPriority(Thread.MAX_PRIORITY);//MAX_PRIORITY=10
thread4.start();
thread5.setPriority(8);
thread5.start();
}
}
class MyPriority implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"-->"+Thread.currentThread().getPriority());
}
}
设置线程为守护线程 setDaemon
- 线程分为守护线程和用户线程。
- 虚拟机必须确定用户线程执行完成,不用等待守护线程是否执行完毕。
- 使用场景:后台记录操作日志,监控内存,垃圾回收等
示例 上帝守护你
public class ThreadDaemon {
public static void main(String[] args) {
God god = new God();
You you = new You();
Thread thread = new Thread(god);
//默认false表示是用户线程,正常的线程都是用户线程
thread.setDaemon(true);
//上帝守护线程启动
thread.start();
//你 用户线程启动
new Thread(you).start();
}
}
//上帝
class God implements Runnable{
@Override
public void run() {
while (true){
System.out.println("上帝保佑着你");
}
}
}
//你
class You implements Runnable{
@Override
public void run() {
for (int i = 0; i < 36500; i++) {
System.out.println("你一生都开心的活着");
}
System.out.println("====goodbye!world====");
}
}
//结果我们发现用户线程执行完毕后一段时间,我们发现守护线程也结束执行。
//守护线程我们没有设置结束条件,为啥还是结束了呢?
//这是因为虚拟机并不会等待守护线程执行结束。守护线程服务于用户线程
参考教程:狂神学java