1.线程终止
线程完成任务后会自动退出
或者可以使用变量来控制run方法退出的方式终止线程,即通知方式
【例】Threadexit
启动一个线程T。要求在main方法中终止线程T
public class ThreadExit_ {public static void main(String[] args) throws InterruptedException {T t1 = new T();t1.start();//如果希望main线程去控制t1 线程的终止, 必须可以修改 loop//让t1 退出run方法,从而终止 t1线程 -> 通知方式//让主线程休眠 10 秒,再通知 t1线程退出System.out.println("main线程休眠10s...");Thread.sleep(10 * 1000);t1.setLoop(false);}
}class T extends Thread {private int count = 0;//设置一个控制变量private boolean loop = true;@Overridepublic void run() {while (loop) {try {Thread.sleep(50);// 让当前线程休眠50ms} catch (InterruptedException e) {e.printStackTrace();}System.out.println("T 运行中...." + (++count));}}public void setLoop(boolean loop) {this.loop = loop;}
}
注意:在哪个线程里面调用sleep()方法就阻塞哪个线程。
参考:
https://blog.csdn.net/Weixiaohuai/article/details/83994146
2.线程常用方法
yield()从未导致线程转到等待/睡眠/阻塞状态。在大多数情况下,yield()将导致线程从运行状态转到可运行状态,但有可能没有效果。
jion()方法:线程实例的join()方法可以使得一个线程在另一个线程结束后再执行,即也就是说使得当前线程可以阻塞其他线程执行;
public class ThreadMethod02 {public static void main(String[] args) throws InterruptedException {T2 t2 = new T2();t2.start();for(int i = 1; i <= 20; i++) {Thread.sleep(1000);System.out.println("主线程(小弟) 吃了 " + i + " 包子");if(i == 5) {System.out.println("主线程(小弟) 让 子线程(老大) 先吃");//join, 线程插队//t2.join();// 这里相当于让t2 线程先执行完毕Thread.yield();//礼让,不一定成功..System.out.println("线程(老大) 吃完了 主线程(小弟) 接着吃..");}}}
}class T2 extends Thread {@Overridepublic void run() {for (int i = 1; i <= 20; i++) {try {Thread.sleep(1000);//休眠1秒} catch (InterruptedException e) {e.printStackTrace();}System.out.println("子线程(老大) 吃了 " + i + " 包子");}}
}
3.Synchronized
并发(concurrency) 并行(parallelism)
package com.hspedu.syn;public class temp {public static void main(String[] args) {//测试一把SellTicket09 sellTicket09 = new SellTicket09();new Thread(sellTicket09).start();//第1个线程-窗口new Thread(sellTicket09).start();//第2个线程-窗口new Thread(sellTicket09).start();//第3个线程-窗口}
}//实现接口方式, 使用synchronized实现线程同步
class SellTicket09 implements Runnable {private int ticketNum = 100;//让多个线程共享 ticketNumprivate boolean loop = true;//控制run方法变量public synchronized void sell() { //同步方法, 在同一时刻, 只能有一个线程来执行sell方法if (ticketNum <= 0) {System.out.println("售票结束...");loop = false;return;}//休眠50毫秒, 模拟try {Thread.sleep(50);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("窗口 " + Thread.currentThread().getName() + " 售出一张票"+ " 剩余票数=" + (--ticketNum));//1 - 0 - -1 - -2}@Overridepublic void run() {while (loop) {sell();//sell方法是一共同步方法}}
}
参考
https://cloud.tencent.com/developer/article/2330704
4.互斥锁(死锁)
public class DeadLock_ {public static void main(String[] args) {//模拟死锁现象DeadLockDemo A = new DeadLockDemo(true);A.setName("A线程");DeadLockDemo B = new DeadLockDemo(false);B.setName("B线程");A.start();B.start();}
}//线程
class DeadLockDemo extends Thread {static Object o1 = new Object();// 保证多线程,共享一个对象,这里使用staticstatic Object o2 = new Object();boolean flag;public DeadLockDemo(boolean flag) {//构造器this.flag = flag;}@Overridepublic void run() {//下面业务逻辑的分析//1. 如果flag 为 T, 线程A 就会先得到/持有 o1 对象锁, 然后尝试去获取 o2 对象锁//2. 如果线程A 得不到 o2 对象锁,就会Blocked//3. 如果flag 为 F, 线程B 就会先得到/持有 o2 对象锁, 然后尝试去获取 o1 对象锁//4. 如果线程B 得不到 o1 对象锁,就会Blockedif (flag) {synchronized (o1) {//对象互斥锁, 下面就是同步代码System.out.println(Thread.currentThread().getName() + " 进入1");synchronized (o2) { // 这里获得li对象的监视权System.out.println(Thread.currentThread().getName() + " 进入2");}}} else {synchronized (o2) {System.out.println(Thread.currentThread().getName() + " 进入3");synchronized (o1) { // 这里获得li对象的监视权System.out.println(Thread.currentThread().getName() + " 进入4");}}}}
}
5.homework
package com.hspedu.homework;import java.util.Scanner;/*** @author 韩顺平* @version 1.0*/
public class Homework01 {public static void main(String[] args) {A a = new A();B b = new B(a);//一定要注意.a.start();b.start();}
}//创建A线程类
class A extends Thread {private boolean loop = true;@Overridepublic void run() {//输出1-100数字while (loop) {System.out.println((int)(Math.random() * 100 + 1));//休眠try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}System.out.println("a线程退出...");}public void setLoop(boolean loop) {//可以修改loop变量this.loop = loop;}
}//直到第2个线程从键盘读取了“Q”命令
class B extends Thread {private A a;private Scanner scanner = new Scanner(System.in);public B(A a) {//构造器中,直接传入A类对象this.a = a;}@Overridepublic void run() {while (true) {//接收到用户的输入System.out.println("请输入你指令(Q)表示退出:");char key = scanner.next().toUpperCase().charAt(0);if(key == 'Q') {//以通知的方式结束a线程a.setLoop(false);System.out.println("b线程退出.");break;}}}
}
package com.hspedu.homework;/*** @author 韩顺平* @version 1.0*/
public class Homework02 {public static void main(String[] args) {T t = new T();Thread thread1 = new Thread(t);thread1.setName("t1");Thread thread2 = new Thread(t);thread2.setName("t2");thread1.start();thread2.start();}
}//编程取款的线程
//1.因为这里涉及到多个线程共享资源,所以我们使用实现Runnable方式
//2. 每次取出 1000
class T implements Runnable {private int money = 10000;@Overridepublic void run() {while (true) {//解读//1. 这里使用 synchronized 实现了线程同步//2. 当多个线程执行到这里时,就会去争夺 this对象锁//3. 哪个线程争夺到(获取)this对象锁,就执行 synchronized 代码块, 执行完后,会释放this对象锁//4. 争夺不到this对象锁,就blocked ,准备继续争夺//5. this对象锁是非公平锁.synchronized (this) {////判断余额是否够if (money < 1000) {System.out.println("余额不足");break;}money -= 1000;System.out.println(Thread.currentThread().getName() + " 取出了1000 当前余额=" + money);}//休眠1stry {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}
}
常见的线程安全相关的面试题