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;
@Override
public 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 {
@Override
public 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;//让多个线程共享 ticketNum
private 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
}
@Override
public 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();// 保证多线程,共享一个对象,这里使用static
static Object o2 = new Object();
boolean flag;
public DeadLockDemo(boolean flag) {//构造器
this.flag = flag;
}
@Override
public void run() {
//下面业务逻辑的分析
//1. 如果flag 为 T, 线程A 就会先得到/持有 o1 对象锁, 然后尝试去获取 o2 对象锁
//2. 如果线程A 得不到 o2 对象锁,就会Blocked
//3. 如果flag 为 F, 线程B 就会先得到/持有 o2 对象锁, 然后尝试去获取 o1 对象锁
//4. 如果线程B 得不到 o1 对象锁,就会Blocked
if (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;
@Override
public 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;
}
@Override
public 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;
@Override
public 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);
}
//休眠1s
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
常见的线程安全相关的面试题