- 线程状态
- 线程方法
- 线程停止
- 线程休眠——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;
@Override
public 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;
@Override
public 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 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{
@Override
public 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{
@Override
public 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);//Run
while(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=10
t4.start();
t5.setPriority(5);
t5.start();
t6.setPriority(7);
t6.start();
}
}
class MyPriority implements Runnable{
@Override
public 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{
@Override
public void run() {
while(true){
System.out.println("上帝保佑着你");
}
}
}
class You implements Runnable{
@Override
public 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;//外部停止方式
@Override
public void run() {
//买票
while(flag){
try {
buy();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//synchronized 同步方法,锁的是this
private 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 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 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;
}
@Override
public 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();
@Override
public 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语句块
}
}
}
}



