目录
jconsole 命令可以监控线程状态
创建线程的几种方式
1.继承Thread
star方法
启动star方法
进入start0——本地方法,由JVM调用
实现Runnable接口
案例
代码
package pojo;
public class Thread2{
public static void main(String[] args) {
Dog dog = new Dog();
Thread thread = new Thread(dog);
//ThreadProxy thread = new ThreadProxy(dog); //这是模拟
thread.start();
for (int i = 0; i < 10; i++) {
System.out.println("主线程:" + i + " name=" + Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class Dog implements Runnable{
int count =0;
@Override
public void run() {
while (true) {
System.out.println("我是多线程:" + Thread.currentThread().getName() +" "+ (++count));
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (count == 10){
break;
}
}
}
}
//线程代理类--模拟Thread thread = new Thread(dog) 这种设计模式
class ThreadProxy implements Runnable{
private Runnable target = null;
@Override
public void run() {
if (target != null){
target.run();
}
}
public ThreadProxy(Runnable target) {//构造函数赋值
this.target = target;
}
public void start(){
start0();//这是实现多线程的方法
}
private void start0() {
run();
}
}
多个子线程
案例
售票系统
实现
代码
package ticket;
/*
使用多线程模拟三个窗口同时售票100张
*/
public class SellTicket {
public static void main(String[] args) {
//Thread 会有超卖问题
// SellTicket01 sellTicket01 = new SellTicket01();
// SellTicket01 sellTicket02 = new SellTicket01();
// SellTicket01 sellTicket03 = new SellTicket01();
// sellTicket01.start();
// sellTicket02.start();
// sellTicket03.start();
//Runnable
SellTicket02 sellTicket02 = new SellTicket02();
new Thread(sellTicket02).start();
new Thread(sellTicket02).start();
new Thread(sellTicket02).start();
}
}
class SellTicket01 extends Thread{
private static int ticketNum = 100;//多个线程共享100
@Override
public void run() {
try {
while (true){
if (ticketNum <= 0){
System.out.println("售票结束...");
break;
}
Thread.sleep(50);//休眠50毫秒,模拟
System.out.println("窗口"+ Thread.currentThread().getName()+"售出一张票,剩余票 " + (--ticketNum));
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class SellTicket02 implements Runnable{
//这里不用是静态
private int ticketNum = 1000;//多个线程共享1000(写大一点更能看出多个线程卖票)
private boolean loop = true;
@Override
public void run() {
while (loop){
sell();
}
}
public synchronized void sell() {//加锁,同一时间只能有一个线程操作sell方法
if (ticketNum <= 0){
System.out.println("售票结束...");
loop = false;
return;
}
try {
Thread.sleep(50);//休眠50毫秒,模拟
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("窗口"+ Thread.currentThread().getName()+"售出一张票,剩余票 " + (--ticketNum));
}
}
Thread运行结果 Runnable运行结果
线程终止
说明
- 当线程完成任务后,会自动退出
- 还可以通过使用变量来控制run方法退出的方式停止线程,既通知方式
案例
代码
package ThreadExit;
public class ThreadExit {
public static void main(String[] args) throws InterruptedException {
T t = new T();
t.start();
//让 t 退出 run 方法,从而终止 t 线程 -> 通知方法 主线程通知 t 线程终止
System.out.println("主线程休眠10s,t线程不会受影响...");
Thread.sleep(10 * 1000);
t.setLoop(false);//调用方法退出run
}
}
class T extends Thread{
//设置一个控制变量
private boolean loop = true;
private int count = 0;
@Override
public void run() {
try {
while (loop){
Thread.sleep(50);
System.out.println("线程运行 " + (++count));
}
} catch (Exception e) {
e.printStackTrace();
}
}
//调用这个方法给loop赋值退出while循环
public void setLoop(boolean loop) {
this.loop = loop;
}
}
线程常用方法
常规方法
1.setName /设置线程名称,使之与参数 name 相同
2.getName //返回该线程的名称
3.start //使该线程开始执行: Java 虚拟机底层调用该线程的 start0 方法4.run //调用线程对象 run 方法
5.setPriority //更改线程的优先级
6.getPriority //获取线程的优先级
7. sleep //在指定的毫秒数内让当前正在执行的线程休眠(暂停执行)
8, interrupt //中断线程——唤醒线程
注意事项
特别方法
1.yield:线程的礼让。让出cpu,让其他线程执行,但礼让的时间不确定,所以也不一定礼让成功
2.join:线程的插队。插队的线程一旦插队成功,则肯定先执行完插入的线程所有的任务t1.join(),那么 t1 线程就先执行完再执行其他的线程。
案例
创建一个子线程,每隔1s 输出 hello,输出 20次,主线程每隔1秒,输出 hi,输出 20次.要求: 两个线程同时执行,当主线程输出 5次后,就让子线程运行完毕,主线程再继续
用户线程和守护线程
1.用户线程:也叫工作线程,当线程的任务执行完或通知方式结束
2.守护线程:一般是为工作线程服务的,当所有的用户线程结束,守护线程自动结束如果我们希望当main线程结束后,子线程自动结束只需将 子线程 设为守护线程即可
3.常见的守护线程: 垃圾回收机制
线程的生命周期
线程状态
官方文档有六种状态:
NEW
尚未启动的线程处于此状态。
RUNNABLE
在Java虚拟机中执行的线程处于此状态
BLOCKED
被阻塞等待监视器锁定的线程处于此状态
WAITING
正在等待另一个线程执行特定动作的线程处于此状态
TIMED WAITING正在等待另一个线程执行动作达到指定等待时间的线程处于此状态
TERMINATED
已退出的线程处于此状态
线程状态转换图
Synchronized
代码在 class SellTicket
互斥锁
死锁
案例
package sync;
public class DeadLock {
public static void main(String[] args) {
//通过不同的 flag 参数,模拟死锁的情况
new DeadLockDemo(true).start();
new DeadLockDemo(false).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");
}
}
}
}
}
释放锁
下面操作会释放锁
以下操作不会释放锁
案例
终止线程
(1)在main方法中启动两个线程
(2)第1个线程循环随机打印100以内的整数
(3)直到第2个线程从键盘读取了“Q”命令
实现
package homework;
import java.util.Scanner;
public class Homework01 {
public static void main(String[] args) {
A a = new A();
B b = new B(a);//一定要注意,传 a
a.start();
b.start();
}
}
class A extends Thread {
private boolean loop = true;
@Override
public void run() {//加锁之后不会超卖
while (loop) {
System.out.println("A线程: "+(int)(Math.random() * 100 +1));
//休眠
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//提供一个可以修改loop的值的方法
public void setLoop(boolean loop) {
this.loop = loop;
}
}
//这个线程用来接收用户输入 Q
class B extends Thread {
//把 A 作为字段就可以得到 A的 loop 了
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("B线程:请输入 Q 指令: ");
char key = scanner.next().toUpperCase().charAt(0);
if (key == 'Q'){
//以通知的方式
System.out.println("输入正确,将通知 A 线程终止");
a.setLoop(false);
System.out.println(" A 线程已终止");
break;
}
System.out.println("指令错误!!!");
}
}
}