文章目录
- 线程状态
- 线程方法
- 线程停止
- 线程休眠——sleep
- 网络延时
- 模拟倒计时与打印当前系统时间
- 线程礼让——yield
- 线程强制执行——Join
- 线程状态
- 线程优先级
- 守护线程
- 不安全案例
- 死锁
- Lock锁
线程状态
- 新建状态:
使用 new 关键字和 Thread 类或其子类建立一个线程对象后,该线程对象就处于新建状态。它保持这个状态直到程序 start() 这个线程。 - 就绪状态:
当线程对象调用了start()方法之后,该线程就进入就绪状态。就绪状态的线程处于就绪队列中,要等待JVM里线程调度器的调度。 - 运行状态:
如果就绪状态的线程获取 CPU 资源,就可以执行 run(),此时线程便处于运行状态。处于运行状态的线程最为复杂,它可以变为阻塞状态、就绪状态和死亡状态。 - 阻塞状态:
如果一个线程执行了sleep(睡眠)、suspend(挂起)等方法,失去所占用资源之后,该线程就从运行状态进入阻塞状态。在睡眠时间已到或获得设备资源后可以重新进入就绪状态。可以分为三种: - 等待阻塞:运行状态中的线程执行 wait() 方法,使线程进入到等待阻塞状态。
- 同步阻塞:线程在获取 synchronized 同步锁失败(因为同步锁被其他线程占用)。
- 其他阻塞:通过调用线程的 sleep() 或 join() 发出了 I/O 请求时,线程就会进入到阻塞状态。当sleep() 状态超时,join() 等待线程终止或超时,或者 I/O 处理完毕,线程重新转入就绪状态。
- 死亡状态:
一个运行状态的线程完成任务或者其他终止条件发生时,该线程就切换到终止状态。
线程方法
方法 | 说明 |
---|---|
public void start() | 使该线程开始执行;Java 虚拟机调用该线程的 run 方法。 |
public void run() | 如果该线程是使用独立的 Runnable 运行对象构造的,则调用该 Runnable 对象的 run 方法;否则,该方法不执行任何操作并返回。 |
public final void setName(String name) | 改变线程名称,使之与参数 name 相同。 |
public final void setPriority(int priority) | 更改线程的优先级。 |
public final void setDaemon(boolean on) | 将该线程标记为守护线程或用户线程。 |
public final void join(long millisec) | 等待该线程终止的时间最长为 millis 毫秒。 |
public void interrupt() | 中断线程。 |
public final boolean isAlive() | 测试线程是否处于活动状态。 |
上述方法是被 Thread 对象调用的,下面表格的方法是 Thread 类的静态方法。
方法 | 描述 |
---|---|
public static void yield() | 暂停当前正在执行的线程对象,并执行其他线程。 |
public static void sleep(long millisec) | 在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。 |
public static boolean holdsLock(Object x) | 当且仅当当前线程在指定的对象上保持监视器锁时,才返回 true。 |
public static Thread currentThread() | 返回对当前正在执行的线程对象的引用。 |
public static void dumpStack() | 将当前线程的堆栈跟踪打印至标准错误流。 |
线程停止
- 建议线程正常停止–>利用次数,不建议死循环
- 建议使用标志位–>设置一个标志位
设置一个公开的方法停止线程,转换标志位 - 不要使用stop或者destory等过时或者JDK不建议使用的方法
package com.zeng.state;
//测试停止线程
//1.建议线程正常停止-->利用次数,不建议死循环
//2。 建议使用标志位-->设置一个标志位
//3. 不要使用stop或者destory等过时或者JDK不建议使用的方法
public class TestStop implements Runnable{//设置一个标志位private boolean flag=true;@Overridepublic void run() {int i=0;while(flag){//当线程flag=false,则终止线程运行System.out.println("run....Thread"+i++);}}//设置一个公开的方法停止线程,转换标志位public void stop() {this.flag = false;}public static void main(String[] args) {TestStop testStop = new TestStop();new Thread(testStop).start();for(int i=0;i<1000;i++){System.out.println("main"+i);if(i==900){testStop.stop();System.out.println("线程停止");}}}
}
线程休眠——sleep
- sleep(时间)指定当前线程阻塞的毫秒数;
- sleep存在异常InterruptedException;
- sleep时间达到后线程进入就绪状态
- sleep可以模拟网络延时,倒计时等。
- 每个对象都有一个锁,sleep不会释放锁。
网络延时
- 模拟网络延时:扩大线程的发生性
package com.zeng.state;import com.zeng.demo01.TestThread03;
//模拟网络延时:扩大线程的发生性
public class TestSleep implements Runnable{//票数private int ticketNums=10;@Overridepublic void run() {while(true){if(ticketNums<=0){break;}// 模拟延时try {Thread.sleep(200);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName()+"-->拿到了第"+ticketNums--+"票");}}public static void main(String[] args) {TestThread03 ticket = new TestThread03();new Thread(ticket,"小明").start();new Thread(ticket,"老师").start();new Thread(ticket,"黄牛党").start();}
}
模拟倒计时与打印当前系统时间
package com.zeng.state;import java.text.SimpleDateFormat;
import java.util.Date;//模拟倒计时
public class TestSleep02 {public static void main(String[] args) throws InterruptedException {//模拟倒计时(调用静态方法)// tenDown();//打印当前系统时间Date startTime = new Date(System.currentTimeMillis());while(true){try {// Date startTime = new Date(System.currentTimeMillis());Thread.sleep(1000);System.out.println(new SimpleDateFormat("HH:mm:ss").format(startTime));startTime = new Date(System.currentTimeMillis());//获取当前时间} catch (InterruptedException e) {e.printStackTrace();}}}public static /*加上static可以直接调用(静态方法)*/void tenDown() throws InterruptedException {int num=10;while(true){Thread.sleep(1000);//sleep存在异常,抛出System.out.println(num--);if(num<=0){break;}}}
}
线程礼让——yield
- 礼让线程,让正在执行的线程暂停,但不阻塞
- 将线程状态转为就绪状态
- 让CPU重新调度,礼让不一定成功!
package com.zeng.state;
//测试礼让线程
//礼让不一定成功,全看CPU心情
public class TextYield {public static void main(String[] args) {MyYield myYield = new MyYield();new Thread(myYield,"a").start();new Thread(myYield,"b").start();}
}
class MyYield implements Runnable{@Overridepublic void run() {System.out.println(Thread.currentThread().getName()+"线程开始执行");Thread.yield();//线程礼让System.out.println(Thread.currentThread().getName()+"线程停止执行");}
}
线程强制执行——Join
- Join合并线程,待此线程执行完成后,再执行其他线程,其他线程阻塞
- 可以想象成插队
package com.zeng.state;
//测试join方法 想象为插队
public class TestJoin implements Runnable{@Overridepublic void run() {for (int i = 0; i < 100; i++) {System.out.println("线程VIP来了"+i);}}public static void main(String[] args) throws InterruptedException {//启动我们的线程TestJoin testJoin = new TestJoin();Thread thread = new Thread(testJoin);thread.start();//主线程for (int i = 0; i < 1000; i++) {if(i==200){thread.join();//插队}System.out.println("main"+i);}}
}
线程状态
线程状态。 线程可以处于以下状态之一:
状态 | 说明 |
---|---|
NEW | 尚未启动的线程处于此状态。 |
RUNNABLE | 在Java虚拟机中执行的线程处于此状态。 |
BLOCKED | 被阻塞等待监视器锁定的线程处于此状态。 |
WAITING | 正在等待另一个线程执行特定动作的线程处于此状态。 |
TIMED_WAITING | 正在等待另一个线程执行动作达到指定等待时间的线程处于此状态。 |
TERMINATED | 已退出的线程处于此状态。 |
一个线程可以在给定时间点处于一个状态。 这些状态是不反映任何操作系统线程状态的虚拟机状态。
package com.zeng.state;public class TestState {public static void main(String[] args) {Thread thread=new Thread(()->{for (int i = 0; i < 5; i++) {try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}System.out.println("///");});//观察状态Thread.State state = thread.getState();System.out.println(state);//new//观察启动后thread.start();//启动线程state=thread.getState();System.out.println(state);//Runwhile(state!=Thread.State.TERMINATED){try {Thread.sleep(1000);state=thread.getState();System.out.println(state);} catch (InterruptedException e) {e.printStackTrace();}}// thread.start();线程进入死亡状态后,就不能再启动,直接报错}
}
线程优先级
- java提供一个线程调度器来监控程序中启动后进入就绪状态的所有线程,线程调度器按照优先级决定应该调度哪个线程来执行。
- 线程的优先级用数字表示,范围从1~10;
- Thread.MIN_PRIORITY=1;
- Thread.MAX_PRIORITY=10;
- Thread.NORM_PRIORITY=5;
- 使用以下方式改变或获取优先级
- getPriority().setPriority(int XXX)
- 优先级的设定建议在start()调度前(先设置优先级,再启动)
- 优先级低只意味着获得调度的概率低,并不是优先级低就不会被调用了,这都是看CPU调度
package com.zeng.state;
//测试线程的优先级
public class TestPriority {public static void main(String[] args) {//主线程默认优先级System.out.println(Thread.currentThread().getName()+"-->"+Thread.currentThread().getPriority());MyPriority myPriority = new MyPriority();Thread t1 = new Thread(myPriority);Thread t2 = new Thread(myPriority);Thread t3 = new Thread(myPriority);Thread t4 = new Thread(myPriority);Thread t5 = new Thread(myPriority);Thread t6 = new Thread(myPriority);//先设置优先级,再启动t1.start();t2.setPriority(1);t2.start();t3.setPriority(4);t3.start();t4.setPriority(Thread.MAX_PRIORITY);//MAX_PRIORITY=10t4.start();t5.setPriority(5);t5.start();t6.setPriority(7);t6.start();}
}
class MyPriority implements Runnable{@Overridepublic void run() {System.out.println(Thread.currentThread().getName()+"-->"+Thread.currentThread().getPriority());}
}
守护线程
- 线程分为用户线程和守护线程
- 虚拟机必须保护用户线程执行完毕
- 虚拟机不必等待守护线程执行完毕
- 如,后台记录日志操作,监控内存,垃圾回收等待
package com.zeng.state;
//测试守护线程
//人生只有三万天 上帝守护你
public class TestDaemon {public static void main(String[] args) {God god = new God();You you = new You();Thread thread = new Thread(god);thread.setDaemon(true);//默认false是用户线程,正常的线程都是用户线程。。。thread.start();//上帝守护线程启动new Thread(you).start();}
}
class God implements Runnable{@Overridepublic void run() {while(true){System.out.println("上帝保佑着你");}}
}class You implements Runnable{@Overridepublic void run() {for (int i = 0; i < 30000; i++) {System.out.println("你一生都在开心的活着");}System.out.println("-=====goodbye world");}
}
不安全案例
package com.zeng.syn;
//不安全的买票
//线程不安全,有负数public class TestUnsafeBuyTicket {public static void main(String[] args) {BuyTicket station = new BuyTicket();new Thread(station,"我emo了").start();new Thread(station,"你笑了").start();new Thread(station,"可恶的黄牛").start();}
}class BuyTicket implements Runnable{//票private int ticketNums=10;boolean flag=true;//外部停止方式@Overridepublic void run() {//买票while(flag){try {buy();} catch (InterruptedException e) {e.printStackTrace();}}}//synchronized 同步方法,锁的是thisprivate synchronized void buy() throws InterruptedException {//判断是否有票if(ticketNums<=0){flag=false;return;}//模拟延时Thread.sleep(100);//买票System.out.println(Thread.currentThread().getName()+"拿到"+ticketNums--);}
}
package com.zeng.syn;import java.util.ArrayList;//线程不安全的集合
public class TestUnsafeList {public static void main(String[] args) {ArrayList<String> list = new ArrayList<>();for (int i = 0; i < 10000; i++) {new Thread(()->{synchronized (list){list.add(Thread.currentThread().getName());}}).start();}try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(list.size());}
}
package com.zeng.syn;import java.util.concurrent.CopyOnWriteArrayList;
//测试JUC安全类型的集合
public class TestJUc {public static void main(String[] args) {CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();for (int i = 0; i < 1000; i++) {new Thread(()->{list.add(Thread.currentThread().getName());});}try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(list.size());}
}
死锁
- 产生死锁的四个必要条件
- 互斥条件:一个资源每次只能被一个进程使用。
- 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
- 不剥夺条件:进程已获得的资源,在未使用完之前,不能强行剥夺。
- 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
package com.zeng.thread;
//死锁:多个线程互相抱着对方的资源,然后形成僵持。
public class DeadLock {public static void main(String[] args) {Makeup s1 = new Makeup(0, "灰姑娘");Makeup s2 = new Makeup(0, "白雪公主");s1.start();s2.start();}
}
//口红
class Lipstick{}//镜子
class Mirror{}
class Makeup extends Thread{//需要的资源只有一份,用static来保证只有一份static Lipstick lipstick=new Lipstick();static Mirror mirror=new Mirror();int choice;//选择String girlName;//使用化妆品的人Makeup(int choice,String girlName){this.choice=choice;this.girlName=girlName;}@Overridepublic void run() {//化妆try {makeup();} catch (InterruptedException e) {e.printStackTrace();}}//化妆,互相持有对方的锁,就是需要拿到对方的资源private void makeup() throws InterruptedException {if(choice==0){synchronized (lipstick){//获得口红的锁System.out.println(this.girlName+"获得口红的锁");Thread.sleep(1000);
// synchronized (mirror){//一秒钟获得镜子
// System.out.println(this.girlName+"获得镜子的锁");
// }}synchronized (mirror){//一秒钟获得镜子System.out.println(this.girlName+"获得镜子的锁");}}else{synchronized (mirror){//一秒钟获得镜子System.out.println(this.girlName+"获得镜子的锁");Thread.sleep(1000);
// synchronized (lipstick){//获得口红的锁
// System.out.println(this.girlName+"获得口红的锁");
// }}synchronized (lipstick){//获得口红的锁System.out.println(this.girlName+"获得口红的锁");}}}
}
Lock锁
- Lock是显式锁(手动开启和关闭锁,别忘记关闭锁)synchronized是隐式锁,出了作用域自动释放。
- Lock只有代码块锁,synchronized有代码块锁和方法锁
- 使用Lock锁,JVM花费较少的时间来调度线程,性能更好。并且具有更好的扩展性(提供更多的子类)
- 优先使用顺序
- Lock>同步代码块(已经进入方法体,分配了相应资源)>同步方法(在方法体之外)
package com.zeng.thread;import java.util.concurrent.locks.ReentrantLock;public class TestLock {public static void main(String[] args) {TestLock2 testLock2 = new TestLock2();new Thread(testLock2).start();new Thread(testLock2).start();new Thread(testLock2).start();}
}class TestLock2 implements Runnable{int ticketNums=10;//定义Lock锁private final ReentrantLock lock=new ReentrantLock();@Overridepublic void run() {while(true){try {lock.lock();if(ticketNums>0){try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(ticketNums--);}else{break;}} finally {//解锁lock.unlock();//如果同步代码有异常,要将unlock()写入finally语句块}}}
}