一、线程介绍
1、线程相关概念
1)程序
是为完成特定任务、用某种语言编写的一组指令的集合(简单说就是写的代码)。
2)进程
① 进程是指运行中的程序,比如使用的QQ,就启动了一个进程,操作系统会为该进程分配内存空间。当我们使用迅雷,又启动了一个进程,操作系统将会为迅雷分配新的内存空间。
② 进程是程序的一次执行过程,或是正在运行的一个程序。是动态过程:有它自身的产生、存在和消亡的过程。
3)什么是线程
① 线程是由进程创建的,是进程的一个实体;
② 一个进程可以拥有多个线程,就比如迅雷可以同时(包括并发和并行)下载多个文件。
③进程是资源分配的基本单位,线程是调度的基本单位
4)其他相关概念
① 单线程:同一个时刻,只允许执行一个线程;
② 多线程:同一个时刻,可以执行多个线程,比如:一个qq进程,可以同时打开多个聊天窗口,一个迅雷进程,可以同时下载多个文件
③ 并发:同一个时刻,多个任务交替执行,造成一种“貌似同时”的感觉,简单的说,单核cpu实现的多任务就是并发。
④ 并行:同一个时刻,多个任务同时执行。多核cpu可以实现并行。
⑤ 并发和并行可能同时存在。
5)查看电脑有多少个cpu核
public class CpuNums {
public static void main(String[] args) {
Runtime runtime = Runtime.getRuntime();
//获取当前电脑cpu的数量(核心数)
int nums = runtime.availableProcessors();
System.out.println(nums );
}
}
二、线程的使用
可以看到,Thread类是实现了Runnable接口的
1、线程的创建
1)继承Thread类,重写run方法
a)核心代码
b)案例
public class Thread01 {
public static void main(String[] args) throws InterruptedException {
//创建Cat对象,可以当做线程使用
Cat cat = new Cat();
cat.start();//启动一个子线程 Thread-0 -> 最终会执行cat的run方法
//说明:当main线程启动一个子线程 Thread-0,主线程不会阻塞,会继续执行
System.out.println("主线程继续执行" + Thread.currentThread().getName());
for (int i = 0; i < 60; i++) {
System.out.println("主线程 i=" + i);
//让主线程休眠
Thread.sleep(1000);
}
}
}
//run Thread 类 实现了 Runnable 接口的run方法
/*
@Override
public void run() {
if (target != null) {
target.run();
}
}
*/
class Cat extends Thread {
int times = 0; //记录输出的次数
@Override
public void run() {//重写run方法,写上自己的业务逻辑
while (true) {
//该线程每隔1秒。在控制台输出 “喵喵, 我是小猫咪”
System.out.println("喵喵, 我是小猫咪" + (++times) + " 线程名=" + Thread.currentThread().getName());
//让该线程休眠1秒
try {
Thread.sleep(1000); //毫秒
} catch (InterruptedException e) {
e.printStackTrace();
}
if (times == 80) {
break;//当times 到 80, 退出while, 这时线程也就退出..
}
}
}
}
/*
(1)
public synchronized void start() {
start0();
}
(2)
//start0() 是本地方法,是JVM调用, 底层是c/c++实现
//真正实现多线程(创建线程)的效果, 是start0(), 而不是 run
//run实现传入对象(线程)的功能
private native void start0();
*/
Runnable中只有一个方法run()
Thread需要重写,继承了Thread的子类也需要重写(实现具体方法)
当使用实现接口Runnable的对象来创建线程时
启动线程会导致在单个线程中**调用传入对象的run方法**。
执行start0()的时候,会调用run()
**真正实现多线程的效果, 是start0(), 而不是 run**
c)JConsole监控线程⭐⭐⭐
2)实现Runnable接口,重写run方法
a)采用该实现机制的原因
b)核心代码
public class Test {
main(){
...
T1 t1 = new T1();
T2 t2 = new T2();
Thread thread1 = new Thread(t1);
Thread thread2 = new Thread(t2);
thread1.start();//启动第1个线程
thread2.start();//启动第2个线程
...
}
}
class T1 implements Runnable{...}
class T2 implements Runnable{...}
c)代码模拟Runnable接口
【设计模式:代理模式】
//代码模拟代理模式,实现Runnable接口 开发线程机制
public class Test {
public static void main(String[] args) {
Tiger tiger = new Tiger();
ThreadProxy thread = new ThreadProxy(tiger);
thread.start();
}
}
class Animal{}
class Tiger extends Animal implements Runnable {
@Override
public void run() {
System.out.println("老虎嗷嗷叫");
}
}
class ThreadProxy implements Runnable { //可以把Proxy类当作ThreadProxy
private Runnable target = null;
@Override
public void run() {
if(target != null) {
target.run();
}
}
public ThreadProxy(Runnable target) {
this.target = target;
}
public void start() {
start0();
}
public void start0() {
run();
}
}
d)应用案例
public class Thread02 {
public static void main(String[] args) {
Dog dog = new Dog();
//dog.start(); 这里不能调用start ⭐⭐⭐⭐⭐
//创建了Thread对象,把 dog对象(实现Runnable),放入Thread
Thread thread = new Thread(dog);
thread.start();
}
}
class Dog implements Runnable { //通过实现Runnable接口,开发线程
int count = 0;
@Override
public void run() { //普通方法
while (true) {
System.out.println("小狗汪汪叫..hi" + (++count) + Thread.currentThread().getName());
//休眠1秒
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (count == 10) {
break;
}
}
}
}
2、多线程执行
package thread_;
public class Thread03 {
public static void main(String[] args) {
T1 t1 = new T1();
T2 t2 = new T2();
Thread thread1 = new Thread(t1);
Thread thread2 = new Thread(t2);
thread1.start();//启动第1个线程
thread2.start();//启动第2个线程
//...
}
}
class T1 implements Runnable {
int count = 0;
@Override
public void run() {
while (true) {
//每隔1秒输出 “hello,world”,输出60次
System.out.println("hello,world " + (++count));
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(count == 60) {
break;
}
}
}
}
class T2 implements Runnable {
int count = 0;
@Override
public void run() {
//每隔1秒输出 “hi”,输出50次
while (true) {
System.out.println("hi " + (++count));
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(count == 50) {
break;
}
}
}
}
3、继承Thread vs 实现Runnable 的区别
两种方式最终都需要调用Thread对象的start方法->调用其中的start0()方法->实现Runnable接口中的run方法
4、多线程售票问题
public class Ticket {
public static void main(String[] args) {
//SellTicket
//会出现超卖和重卖的现象
//原因--同一时刻多个线程同时访问资源且修改资源,上一个线程修改了的数据还没保存,就被第二个线程取走了改之前的数据
SellTicket sellTicket = new SellTicket();
SellTicket sellTicket1 = new SellTicket();
SellTicket sellTicket2 = new SellTicket();
sellTicket.start();
sellTicket1.start();
sellTicket2.start();
//SellTicket2
//也会出现超卖重卖现象
SellTicket2 sellTicket4 = new SellTicket2();
Thread thread1 = new Thread(sellTicket4);
Thread thread2 = new Thread(sellTicket4);
Thread thread3 = new Thread(sellTicket4);
thread1.start();
thread2.start();
thread3.start();
}
}
class SellTicket extends Thread {
private static int ticketNum = 100; //static:让多个线程共享ticketNum
@Override
public void run() {
while(true) {
if(ticketNum <= 0) break;
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "窗口卖了一张票,剩余票数:" + (--ticketNum));
}
}
}
class SellTicket2 implements Runnable {
private int ticketNum = 100; //这里不用static,因为是把同一个对象放入不同的线程,Runnable本身就有可以共享资源的机制
@Override
public void run() {
while(true) {
if(ticketNum <= 0) break;
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "窗口卖了一张票,剩余票数:" + (--ticketNum));
}
}
}
class SellTicket3 implements Runnable {
int ticketNum = 100;
@Override
public void run() {
while(true) {
if(ticketNum <= 0) break;
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "窗口2卖了一张票,剩余票数:" + (--ticketNum));
}
}
}
比如下面,第35张票被卖了3次
5、线程终止
1)基本说明
① 当线程完成任务后,会自动退出;
② 还可以通过使用变量来控制run方法退出的方式停止线程,即通知方式。
2)应用实例
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) { //检测到loop被更改为false后会终止执行
try {
Thread.sleep(50);// 让当前线程休眠50ms
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("T 运行中...." + (++count));
}
}
public void setLoop(boolean loop) {
this.loop = loop;
}
}
通知方式:主线程执行过程中,可以调用线程对象,传入一个变量,让它停止运行
三、线程方法
1、setName() / start() / run() / sleep() / interrupt()…
(1)setName
//设置线程名称,使其与参数name相同;
(2)getName
//返回该线程的名称;
(3)start
//使该线程开始执行;Java虚拟机底层调用该线程的start0方法;
(4)run
//调用线程对象run方法;
(5)setPriority
//更改线程的优先级;
(6)getPriority
//获取线程的优先级;
(7)sleep
//在指定的毫秒数内让当前正在执行的线程休眠(暂停执行);
(8)interrupt
//中断线程,注意不是终止,如果线程正在休眠,则停止休眠,继续执行。
public class ThreadMethod01 {
public static void main(String[] args) throws InterruptedException {
//测试相关的方法
T t = new T();
t.setName("老韩"); //设置线程名字
t.setPriority(Thread.MIN_PRIORITY);//线程优先级:最低
t.start();//启动子线程
//主线程打印 5个 hi ,然后就中断 子线程的休眠
for(int i = 0; i < 5; i++) {
Thread.sleep(1000);
System.out.println("hi " + i);
}
System.out.println(t.getName() + " 线程的优先级 =" + t.getPriority());//1
t.interrupt();//当执行到这里,就会中断 t线程的休眠.
}
}
class T extends Thread { //自定义的线程类
@Override
public void run() {
while (true) {
for (int i = 0; i < 50; i++) {
//Thread.currentThread().getName() 获取当前线程的名称
System.out.println(Thread.currentThread().getName() + " 吃包子~~~~" + i);
}
try {
System.out.println(Thread.currentThread().getName() + " 休眠中~~~");
Thread.sleep(20000);//20秒
} catch (InterruptedException e) {
//当该线程执行到一个interrupt 方法时,就会catch 一个 异常, 可以加入自己的业务代码
//InterruptedException 是捕获到一个中断异常.
System.out.println(Thread.currentThread().getName() + "被 interrupt了");
}
}
}
}
2、注意事项和细节
(1)start底层会创建新的线程,调用run,run就是一个简单的方法调用,不会启动新线程;
(2)线程优先级的范围;
(3)interrupt,中断休眠线程,但没有真正的结束线程,所以一般用于中断正在休眠的线程;(中断休眠,相当于将线程从休眠状态唤醒)
(4)sleep:线程的静态方法,使当前线程休眠。
3、yield() / join()
(1)yield
:线程的礼让
让出CPU,让其他线程执行,但是礼让的时间不确定,所以不一定礼让成功。
(2)join
:线程的插队
插队的线程一旦插队成功,则肯定先执行完插入的线程所有的任务。
(3)案例
main线程创建了一个子线程,每隔1s输出hello,输出20次,主线程每隔1s,输出hi,输出20次。要求两个线程同时执行,当主线程输出5次后,就让子线程运行完毕,主线程再继续执行。
public class ThreadMethod02 {
public static void main(String[] args) throws InterruptedException {
T3 t3 = new T3();
t3.start();
for(int i = 1; i <= 20; i++) {
Thread.sleep(1000);
System.out.println("主线程(小弟) 吃了 " + i + " 包子");
if(i == 5) {
System.out.println("主线程(小弟) 让 子线程(老大) 先吃");
//join, 线程插队,一定会成功
//t3.join(); // 这里相当于让t3 线程先执行完毕
Thread.yield(); //礼让,不一定成功..
System.out.println("线程(老大) 吃完了 主线程(小弟) 接着吃..");
}
}
}
}
class T3 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 + " 包子");
}
}
}
4、课堂练习题
package thread_;
public class ThreadMethodExcercise {
public static void main(String[] args) throws InterruptedException {
T4 t4 = new T4();
Thread t = new Thread(t4);
for (int i = 0; i < 10; i++) {
Thread.sleep(1000);
if(i == 4){
t.start();
t.join();
}
System.out.println("hi " + (i+1));
}
System.out.println("主线程结束..");
}
}
class T4 implements Runnable {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("hello " + (i+1));
}
System.out.println("子程序结束...");
}
}
5、用户线程和守护线程
1)用户线程
也叫工作线程,当线程的任务执行完或通知方式通知它结束。
2)守护线程
一般是为了工作线程服务的,当所有的用户线程结束,守护线程自动结束。
常见的守护线程就是垃圾回收机制。
将子线程设为守护线程=>线程类继承Thread,在主线程中对象调用setDaemon(true)
注意 守护线程的设置 要放在 创建线程 的前面,如下。
myDaemonThread.setDaemon(true);
myDaemonThread.start();
public class ThreadMethod03 {
public static void main(String[] args) throws InterruptedException {
MyDaemonThread myDaemonThread = new MyDaemonThread();
//如果我们希望当main线程结束后,子线程自动结束
//,只需将子线程设为守护线程即可
myDaemonThread.setDaemon(true);
myDaemonThread.start();
for( int i = 1; i <= 10; i++) {//main线程
System.out.println("宝强在辛苦的工作...");
Thread.sleep(1000);
}
}
}
class MyDaemonThread extends Thread {
public void run() {
for (; ; ) {//无限循环
try {
Thread.sleep(1000);//休眠1000毫秒
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("马蓉和宋喆快乐聊天,哈哈哈~~~");
}
}
}
四、线程的生命周期
1、线程状态分类
线程有七种状态:NEW、Runable(Ready、Running)、TimeWaiting、Waiting、Blocked、Teminated
【PS:如果把Runable细化成Ready和Running,那就有七个状态:NEW、Ready、Running、TimeWaiting、Waiting、Blocked、Teminated】
【如果不细化,那就只有六个状态:NEW、Runable、TimeWaiting、Waiting、Blocked、Teminated】
官方文档:
//说明:
RUNNABLE状态表示可以运行,但不代表马上运行。什么时候运行由操作系统内核决定
2、线程状态转换图⭐⭐⭐⭐⭐
演示线程状态
查看状态:getState();
状态名:Thread.State.TERMINATED / NEW / RUNNABLE / TIMED_WATITING / WAITING / BLOCKED
public class ThreadState_ {
public static void main(String[] args) throws InterruptedException {
T t = new T();
System.out.println(t.getName() + " 状态 " + t.getState());
t.start();
while (Thread.State.TERMINATED != t.getState()) { //线程还没终止
System.out.println(t.getName() + " 状态 " + t.getState());
Thread.sleep(500);
}
System.out.println(t.getName() + " 状态 " + t.getState());
}
}
class T extends Thread {
@Override
public void run() {
while (true) {
for (int i = 0; i < 10; i++) {
System.out.println("hi " + i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
break;
}
}
}
五、Synchronized⭐⭐⭐⭐⭐
1、线程同步机制
(1)在多线程编程,一些敏感数据不允许被多个线程同时访问,此时就使用同步访问技术,保证数据在任何同一时刻,最多有一个线程访问,以保证数据的完整性;
(2)线程同步,即当有一个线程在对内存进行操作时,其他线程都不可以对这个内存地址进行操作,直至该线程完成操作,其他线程才能对该内存地址进行操作。
2、同步具体方法 - Synchronized
(1)同步代码块
synchronized(对象){ //得到对象的锁,才能操作同步代码
//需要被同步代码;
}
(2)同步方法
synchronized还可以放在方法声明中,表示整个方法。
public synchronized void m(String name){
//需要被同步的代码
}
3、使用同步解决售票问题
只把访问共享资源的代码放入synchronized方法中 =》 休眠代码和循环逻辑放在外面
package syschronized_;
public class Ticket {
public static void main(String[] args) {
System.out.println("===使用继承类方式来售票=====");
SellTicket03 sellTicket1 = new SellTicket03();
SellTicket03 sellTicket2 = new SellTicket03();
SellTicket03 sellTicket3 = new SellTicket03();
sellTicket1.start();//启动售票线程
sellTicket2.start();//启动售票线程
sellTicket3.start();//启动售票线程
// System.out.println("===使用实现接口方式来售票=====");
// SellTicket04 sellTicket02 = new SellTicket04();
//
// new Thread(sellTicket02).start();//第1个线程-窗口
// new Thread(sellTicket02).start();//第2个线程-窗口
// new Thread(sellTicket02).start();//第3个线程-窗口
}
}
//使用Thread方式
class SellTicket03 extends Thread {
private static int ticketNum = 100;//让多个线程共享 ticketNum
private static boolean bool = true;//控制run方法的变量
public static synchronized void sell() {//同步方法,同一时刻,只能有一个线程执行该方法
if (ticketNum <= 0) {
System.out.println("售票结束...");
bool = false;
return;
}
System.out.println("窗口 " + Thread.currentThread().getName() + " 售出一张票"
+ " 剩余票数=" + (--ticketNum));
}
@Override
public void run() {
while (bool) {
sell(); //只把访问共享资源的代码放入synchronized方法中
//休眠50毫秒,
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
//实现接口方式
class SellTicket04 implements Runnable {
private int ticketNum = 100;//让多个线程共享 ticketNum
private boolean loop = true;
public synchronized void sell() {
if (ticketNum <= 0) {
System.out.println("售票结束...");
loop = false;
return;
}
System.out.println("窗口 " + Thread.currentThread().getName() + " 售出一张票"
+ " 剩余票数=" + (--ticketNum));
}
@Override
public void run() {
while (loop) {
sell();
//休眠50毫秒, 模拟
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
六、互斥锁⭐⭐⭐⭐⭐
1、基本介绍
(1)Java语言中,引入了对象互斥锁(锁在对象上,由线程去抢夺锁)的概念,来保证共享数据操作的完整性;
(2)每个对象都对应于一个可称为“互斥锁”的标记,这个标记用来保证在任意时刻,只能有一个线程访问该对象;
(3)关键字synchronized来于对象的互斥锁练习,当某个对象用synchronized修饰时,表明该对象在任一时刻只能由一个线程访问;
(4)同步的局限性:导致程序的执行效率要降低;
(5)同步方法(非静态的)的锁可以是this,也可以是其他对象(要求是同一个对象,即多个线程访问的是同一个对象);【该方法适用于实现接口的同步,因为继承类的化不同线程操作的是不同的对象】
class SellTicket04 implements Runnable {
private int ticketNum = 100;//让多个线程共享 ticketNum
private boolean loop = true;
Object obj = new Object();
public /*synchronized*/ void sell() {
//也可以写成synchronized(this)
synchronized (obj) { //synchronized写在代码块上,表示该对象obj只能由得到锁的线程访问
if (ticketNum <= 0) {
System.out.println("售票结束...");
loop = false;
return;
}
System.out.println("窗口 " + Thread.currentThread().getName() + " 售出一张票"
+ " 剩余票数=" + (--ticketNum));
}
}
@Override
public void run() {
while (loop) {
sell();
//休眠50毫秒, 模拟
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
(6)同步方法(静态的)的锁为当前类本身。【该方法适用于继承类的同步,也可以用于实现接口的同步】
//继承类的同步
//第二种,静态方法内部写同步代码块,默认的锁在该类
class SellTicket03 extends Thread {
private static int ticketNum = 100;//让多个线程共享 ticketNum
private static boolean bool = true;
public static void sell(){
synchronized(SellTicket03.class){
if (ticketNum <= 0) {
System.out.println("售票结束...");
bool = false;
return;
}
//休眠50毫秒, 模拟
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("窗口 " + Thread.currentThread().getName() + " 售出一张票"
+ " 剩余票数=" + (--ticketNum));
}
}
@Override
public void run() {
while (bool) {
sell();
}
}
}
2、注意事项和细节
(1)同步方法如果没有使用static修饰:默认锁对象为this;
(2)如果方法使用static修饰,默认锁对象:当前类.class
(3)实现的落地步骤:
① 需要先分析上锁的代码;
② 选择同步代码块【范围小,效率高,优选】或者同步方法;
③ 要求多个线程的锁对象为同一个即可。
七、死锁
1、基本介绍
多个线程都占用了对方的资源,都不肯相让,导致了死锁,在编程中一定要避免死锁的发生。
说人话:得不到自己要的资源,又不给对方它想要的资源。
2、应用实例
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");
//A线程卡在这↓ ---> A处于BLOCK状态 ---> 不能接着向下走,就无法释放o1对象锁->B得不到o1对象锁->B卡死
synchronized (o2) { // 这里获得O2对象的监视权
System.out.println(Thread.currentThread().getName() + " 进入2");
}
}
} else {
synchronized (o2) {
System.out.println(Thread.currentThread().getName() + " 进入3");
//B线程卡在这↓ ---> B处于BLOCK状态---> 不能接着向下走,就无法释放o2对象锁->A得不到o2对象锁->A卡死
synchronized (o1) { // 这里获得O1对象的监视权
System.out.println(Thread.currentThread().getName() + " 进入4");
}
}
}
}
}
八、释放锁
1、下面操作会释放锁
(1)当前线程的同步方法、同步代码块执行结束;
(2)当前线程在同步代码块、同步方法中遇到break、return
;
(3)当前线程在同步代码块、同步方法中出现了未处理的Error或Exception,导致异常结束;
(4)当前线程在同步代码块、同步方法中执行了线程对象的wait()
方法,当前线程停止,并释放锁。
2、下面操作不会释放锁
(1)线程执行同步代码块或同步方法时,程序调用Thread.sleep(),Thread.yield()方法暂停当前线程的执行,不会释放锁;
(2)线程执行同步代码块时,其他线程调用了该线程的suspend()方法将该线程挂起,该线程不会释放锁。
提示:不推荐使用suspend()和resume()来控制线程。
九、本章作业
package syschronized_;
import java.util.Scanner;
public class Test {
public static void main(String[] args) {
T1 t1 = new T1();
T2 t2 = new T2(t1);
new Thread(t1).start();
new Thread(t2).start();
//继承Thread方法
// t1.start();
// t2.start();
}
}
class T1 implements Runnable {
private boolean loop = true;
public void setLoop(boolean loop){
this.loop = loop;
}
@Override
public void run() {
while(loop){
System.out.println((int)(Math.random()*101));
try {
Thread.sleep(1000); //需要在下一次激活(休眠结束)前设置loop = false,不然无法退出,可以把时间设置长一点
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("T1线程退出");
}
}
class T2 implements Runnable {
Scanner scanner = new Scanner(System.in);
private T1 t1;
public T2(T1 t1){ //将main创建的t1对象通过构造器传入T2,实现T2和main共享对象
this.t1 = t1;
}
@Override
public void run() {
while(true){
System.out.println("请输入Q退出程序:");
if(scanner.next().toUpperCase().charAt(0) == 'Q'){
t1.setLoop(false);//以通知的方式结束a线程
System.out.println("T2线程退出");
break;
}
}
}
}
使用守护线程:
public class Test {
public static void main(String[] args) {
//将t1设置为守护线程,当t2线程结束时,t1守护线程自动结束
T1 t1 = new T1();
T2 t2 = new T2();
t1.setDaemon(true); //守护线程
t1.start();
t2.start();
}
}
class T1 extends Thread { //将t1设置为守护线程,需要继承Thread
@Override
public void run() {
while(true){
System.out.println((int)(Math.random()*101));
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class T2 extends Thread {
Scanner scanner = new Scanner(System.in);
@Override
public void run() {
while(true){
System.out.println("请输入Q退出程序:");
if(scanner.next().toUpperCase().charAt(0) == 'Q'){
System.out.println("t2线程退出");
break;
}
}
}
}
package syschronized_;
public class Homework02 {
public static void main(String[] args) {
Customer customer = new Customer();
Thread customer1 = new Thread(customer);
Thread customer2 = new Thread(customer);
customer1.setName("用户1");
customer2.setName("用户2");
customer1.start();
customer2.start();
}
}
class Customer implements Runnable {
private static int money = 10000;
@Override
public void run() {
while(true){
if(money <= 0){
System.out.println("余额不足");
break;
}
synchronized (this) {
money-=1000;
System.out.println(Thread.currentThread().getName() + "取出1000元,余额:" + money);
}
//休眠
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}