目录
- 进程和线程的定义、区别、组成
- 线程的使用
2.1. 线程的创建:继承Thread类、实现Runnable接口(匿名内部类)
2.2. 线程的使用 - 线程的基本状态和方法
3.1 基本状态
3.2 方法 - 多线程安全问题
4.1 同步锁和同步代码块
4.2 同步方法
4.3 死锁
4.4 线程通信
一、进程和线程的定义、区别
1. 定义:
- 进程:进程是正在运行的程序的实例
- 线程:进程中的一个执行任务(控制单元),负责当前进程中程序某部分代码的执行。
2. 区别:
- 进程是操作系统资源分配的基本单位,线程是CPU的基本调度单位
- 一个程序运行后至少有一个进程
- 一个进程可以包含多个线程,但至少要有一个线程,不然该进程没有意义
- 进程间不能共享数据地址,但同个进程下的多个线程间可以共享。
3. 线程的组成:
任何一个线程都具有基本的组成部分:
① CPU时间片:操作系统 (OS)为每个线程分配的执行时间
② 运行数据:
–堆空间:存储线程需要使用的对象,多个线程可以共享堆中的对象
–栈空间:存储线程需要使用的局部变量,每个线程都拥有独立的栈,不共享
③ 线程的具体代码
4.线程的特点:
①. 线程抢占式执行:
–效率高
–可防止单一线程长时间独占CPU
②. 在 单核 CPU中:宏观并行,微观串行
二、线程的使用
2.1. 线程的创建:继承Thread类、实现Runnable接口(匿名内部类)
继承Thread类的创建方式:
换句话说就是继承Thread,重写run() 方法,就可以使用了
实现Runnable接口的创建方式:
- 实现Runnable接口, 一样重写run() 方法
- 用Runnable对象包装自定义类
- 再使用Thread的构造方法创建Thread对象,该Thread对象就是新线程了
可以简写成:(没必要包装成Runnable的)
实现Runnable接口的还有一种写法——匿名内部类
简写:
2.2. 线程的使用
抢占式执行演示:
获取线程的ID和Name:(推荐Thread.currentThread() 方式)
设置线程Name:(ID无法修改)
- setName()
- 创建时初始化
★ 用run()启动和用start()启动的区别: start()并行,run()串行
用run() 启动的线程类失去了多线程的意义,跟普通类没区别
(有种说法:run() 是在调用线程对象的方法,就跟调用普通对象的方法一样,但run() 确实也启动了线程类,可以看下下面两张图的区别)
用this.get直接获取的就不能发现
三、线程的基本状态 和 方法
3.1 基本状态:
- NEW 初始态:new
- RUNNABLE 就绪态:start
- RUNNING 运行态:run
- WAITING 等待态:sleep,join,wait
- BLOCK 阻塞:synchronized
- TERMINATED 终止态:
stop
初始态:只在堆中开辟内存与普通对象无异
就绪态:执行了start()方法,等待操作系统分配CPU时间片给它
运行态:操作系统给予CPU时间片,正在执行
等待态:线程进入等待或休眠状态,放弃CPU片,但不一定会释放锁和其他资源
阻塞:阻塞状态是指线程因为某种原因失去了cpu使用权,暂时停止运行,直到线程重新进入运行态。
终止态:线程结束生命周期
jdk1.5后 就绪态和运行态合并为 运行态Runnable
状态图:
Thread源码中的所有状态值:
TIMED_WAITING即sleep,指定时间长度的等待
3.2 方法:
⑴sleep()休眠 :休眠指定毫秒数,自动苏醒
⑵yield() 放弃:主动放弃CPU一次,重新等待操作系统分配CPU时间片。
public class ThreadMethods {
public static void main(String[] args) {
new Thread(new YieldThread()).start();
new Thread(new PojoThread()).start();
}
}
class YieldThread implements Runnable{
public void run() {
int i =0 ;
while(i<20) {
++i;
yy();
System.out.println("Yield:我这是第"+(++i)+"次运行,休息一秒");
}
}
void yy() {
System.out.println("Yield:我放弃了,这次不打印,没心情");
Thread.yield();
}
}
class PojoThread implements Runnable{
public void run() {
int i =0 ;
while(i<20) {
System.out.println("Pojo:我这是第"+(++i)+"次运行,休息一秒");
}
}
}
⑶join() 线程加入:新线程(join)加入当前线程,阻塞当前线程,等执行完新线程(join)后再释放执行当前线程。
换句话就是插队,办完事再还当前线程的CPU
public class ThreadMethods {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(new JoinThread());
t1.start();
t1.join();//插队,抢当前线程CPU(main线程)
for (int j = 1; j < 10; j++) {
System.out.println("main:我这是第"+(j)+"次运行");
}
}
}
class JoinThread implements Runnable{
@Override
public void run() {
int i =0 ;
System.out.println("Join:插个队,等我全部打印完你再执行。");
while(i<5) {
System.out.println("Join:我这是第"+(++i)+"次运行-----------");
}
System.out.println("Join:我全部打印完了,还你CPU。");
}
}
只会阻塞当前线程(在哪个线程里写的join,就是哪个),不会阻塞同时运行的其他线程
public class ThreadMethods {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(new JoinThread());
t1.start();
new Thread(new PojoThread()).start();
//只会阻塞当前线程(main线程),不会阻塞同时运行的其他线程
t1.join();//插队,抢当前线程CPU(main线程)
for (int j = 1; j < 10; j++) {
System.out.println("main:我这是第"+(j)+"次运行");
}
}
}
class JoinThread implements Runnable{
@Override
public void run() {
int i =0 ;
System.out.println("Join:插个队,等我全部打印完你再执行。");
while(i<10) {
System.out.println("Join:我这是第"+(++i)+"次运行-----------");
}
System.out.println("Join:我全部打印完了,还你CPU。");
}
}
class PojoThread implements Runnable{
public void run() {
int i =0 ;
while(i<10) {
System.out.println("Pojo:我就是不让join,不能惯他,这是第"+(++i)+"次运行");
}
}
}
Join:插个队,等我全部打印完你再执行。
Pojo:我就是不让join,不能惯他,这是第1次运行
Join:我这是第1次运行-----------
Pojo:我就是不让join,不能惯他,这是第2次运行
Join:我这是第2次运行-----------
Pojo:我就是不让join,不能惯他,这是第3次运行
Join:我这是第3次运行-----------
Pojo:我就是不让join,不能惯他,这是第4次运行
Join:我这是第4次运行-----------
Pojo:我就是不让join,不能惯他,这是第5次运行
Join:我这是第5次运行-----------
Pojo:我就是不让join,不能惯他,这是第6次运行
Join:我这是第6次运行-----------
Pojo:我就是不让join,不能惯他,这是第7次运行
Join:我这是第7次运行-----------
Pojo:我就是不让join,不能惯他,这是第8次运行
Join:我这是第8次运行-----------
Pojo:我就是不让join,不能惯他,这是第9次运行
Join:我这是第9次运行-----------
Pojo:我就是不让join,不能惯他,这是第10次运行
Join:我这是第10次运行-----------
Join:我全部打印完了,还你CPU。
main:我这是第1次运行
main:我这是第2次运行
main:我这是第3次运行
main:我这是第4次运行
main:我这是第5次运行
main:我这是第6次运行
main:我这是第7次运行
main:我这是第8次运行
main:我这是第9次运行
⑷设置线程优先级:1~10,1最低,10最高,默认5,越高越容易抢到CPU时间片
public class ThreadMethods {
public static void main(String[] args) throws InterruptedException {
new Thread(new PriorityThread(1),"p1").start() ;
new Thread(new PriorityThread(5),"p5").start() ;
new Thread(new PriorityThread(10),"p10").start();
}
}
class PriorityThread extends Thread{
public PriorityThread() {
super();
}
public PriorityThread(int p) {
super();
//这里不能this.setPriority(p),这样不会设置成功的
Thread.currentThread().setPriority(p);
}
@Override
public void run() {
for (int i = 0; i < 50; i++) {
System.out.print (Thread.currentThread().getName()+" Priority: " + i);
}
System.out.println("\r\n"+Thread.currentThread().getPriority()+ " " +Thread.currentThread().getName()+" Priority: 我打印完了---------------------------");
}
}
⑸守护线程(后台线程):setDaemon(true),默认false(前台)
- 线程有两类:一类是用户线程(前台线程),一类是守护线程(后台线程)
- 当程序的所有前台线程都结束了,后台线程会自动结束
- 垃圾回收器属于守护线程
public class ThreadMethods {
public static void main(String[] args) throws InterruptedException {
new Thread(new PojoThread("普通线程")).start() ;
Thread t2 = new DaemonThread();
t2.setDaemon(true);//注销则是前台线程,不会自动停止
t2.start();
}
}
class DaemonThread extends Thread{
public DaemonThread() {
super();
}
public void run() {
while(true)
System.out.println("Daemon:我是守护线程,即后台线程 ,等所有前台线程结束后自动结束 "
+Thread.currentThread().isDaemon());
}
}
四、多线程安全问题:
- 多线程在访问临界资源时,如果被破坏了原子操作,就可能出现数据不一致的安全问题
- 临界资源:多个线程共享的资源,单次原子操作时,仅允许一个线程操作使用,才能保证数据的正确性。
- 原子操作:不可分割的单个或多个操作,被视为一个整体,其顺序和步骤不可被打乱或缺省
4.1 同步锁和同步代码块
用同步代码块解决(这里先简单的讲一下,实际应用不这样)
import java.util.Arrays;
public class ThreadMethods {
static int index = 0;
public static void main(String[] args) throws InterruptedException {
Object socket = new Object();
String[] str = new String[10];
Runnable r1 = new Runnable() {
public void run() {
// synchronized (socket) {
str[index++] = "hello";
str[index++] = "hello";
// }
}; };
Runnable r2 = new Runnable() {
public void run() {
// synchronized (socket) {
str[index++] = "world";//访问并修改共享资源
str[index++] = "world";//访问并修改共享资源
// }
}; };
Thread t1 = new Thread(r1,"r1");
Thread t2 = new Thread(r2,"r2");
t1.start(); t2.start();
t1.join(); t2.join();
System.out.println(Arrays.toString(str));
}
}
加同步锁就不会出现(一般情况没有,实际上还是有问题的)
卖票例子:
import java.util.Arrays;
public class ThreadMethods {
static int index = 0;
public static void main(String[] args) throws InterruptedException {
TicketRunnable tickets = new TicketRunnable();
Thread t1 = new Thread(tickets,"t1");
Thread t2 = new Thread(tickets,"t2");
Thread t3 = new Thread(tickets,"t3");
Thread t4 = new Thread(tickets,"t4");
t1.start(); t2.start();t3.start(); t4.start();
}
}
class TicketRunnable implements Runnable{
private int ticket = 200;
@Override
public void run() {
while(true) {
if(ticket <= 0)//访问共享资源
break;
System.out.println(Thread.currentThread().getName()
+ "卖了第"+ticket +"张票");//访问共享资源
ticket--;//修改共享资源
}
}
}
加了同步锁后:
public class ThreadMethods {
static int index = 0;
public static void main(String[] args) throws InterruptedException {
TicketRunnable tickets = new TicketRunnable();
Thread t1 = new Thread(tickets,"t1");
Thread t2 = new Thread(tickets,"t2");
Thread t3 = new Thread(tickets,"t3");
Thread t4 = new Thread(tickets,"t4");
t1.start(); t2.start();t3.start(); t4.start();
}
}
class TicketRunnable implements Runnable{
private int ticket = 200;
private Object ticketSocket= new Object();
@Override
public void run() {
while(true) {
synchronized (ticketSocket) {
//ticketSocket是该同步代码块的锁,保证锁唯一就行,Object对象
if(ticket <= 0)//访问共享资源
break;
System.out.println(Thread.currentThread().getName()
+ "卖了第"+ticket +"张票");//访问共享资源
ticket--;//修改共享资源
} } }
}
错误加锁的例子之一:位置不对
当然,不是说加同步锁的范围越大越好,要考虑运行效率和其他的问题
★ 能不加锁尽量不加锁
4.2 同步方法:
★ 同步方法的锁只有两种:
- this :使用该方法的当前对象
- 当前类.class(包含该同步方法的类)
public class ThreadMethods {
static int index = 0;
public static void main(String[] args) throws InterruptedException {
SynchronizedMethod synMethod = new SynchronizedMethod();
Thread t1 = new Thread(synMethod,"t1");
Thread t2 = new Thread(synMethod,"t2");
Thread t3 = new Thread(synMethod,"t3");
Thread t4 = new Thread(synMethod,"t4");
t1.start(); t2.start();t3.start(); t4.start();
}
}
class SynchronizedMethod implements Runnable{
private int ticket = 200;
private Object ticketSocket= new Object();
@Override
public void run() { synMethod(); }
//同步方法
public synchronized void synMethod() {
//锁是this,this是执行同步方法的对象——>main()里的TicketRunnable tickets
while(true) {
if(ticket <= 0)//访问共享资源
break;
System.out.println(Thread.currentThread().getName()
+ "卖了第"+ticket +"张票");//访问共享资源
ticket--;//修改共享资源
}
}
//如果写成静态同步方法,如
//public static synchronized void synMethod(){}
//那么锁是类本身,————>SynchronizedMethod.class
}
JDK中线程安全的一些常见类:
- StringBuffer
- Vector
- HashTable
4.3 死锁
死锁:A、B两线程互相持有双方所需要的锁,却互不相让,就形成死锁
男孩和女孩一起吃饭的例子:(只有一双筷子)
public class ThreadMethods {
static int index = 0;
public static void main(String[] args) throws InterruptedException {
new Thread(new BoyRunnable()).start();
new Thread(new GirlRunnable()).start();
}
}
class MyLock {
static Object a = "a";
static Object b = "b";
}
class BoyRunnable implements Runnable{
public void run() {
synchronized (MyLock.a) {
System.out.println("男孩拿到a筷子");
synchronized (MyLock.b) {
System.out.println("男孩拿到了b筷子");
System.out.println("男孩可以吃饭了");
}
}
}
}
class GirlRunnable implements Runnable{
public void run() {
synchronized (MyLock.b) {
System.out.println("女孩拿到b筷子");
synchronized (MyLock.a) {
System.out.println("女孩拿到了a筷子");
System.out.println("女孩可以吃饭了");
}
}
}
}
互不想让,形成死锁
形成死锁的线程不会释放已经拥有的锁,这样会一直堵塞,例如:
OtherBoyRunnable 永远也拿不到 BoysRunnable 手中的a锁
public class ThreadMethods {
static int index = 0;
public static void main(String[] args) throws InterruptedException {
new Thread(new BoysRunnable()).start();
new Thread(new GirlRunnable()).start();
Thread.sleep(100);
new Thread(new OtherBoyRunnable()).start();
}
}
class MyLock {
static Object a = "a"; static Object b = "b";
static Object c = "c"; static Object d = "d";
}
class GirlRunnable implements Runnable{
public void run() {
synchronized (MyLock.b) {
System.out.println("女孩拿到b锁,想继续拿a锁");
synchronized (MyLock.a) {
System.out.println("女孩拿到了a锁");
}
}
}
}
class BoysRunnable implements Runnable{//多情boys
public void run() {
synchronized (MyLock.a) {
System.out.println("boys:拿到a锁,想继续拿b锁");
synchronized(MyLock.b) {
System.out.println("boys:拿到a锁和b锁");
synchronized(MyLock.c) {
System.out.println("boys:拿到a锁、b锁和c锁");
}
}
}
}
}
class OtherBoyRunnable implements Runnable{
public void run() {
System.out.println("OtherBoy:我什么锁都没拿到,我想要拿到a锁");
synchronized (MyLock.a) {
System.out.println("OtherBoy:我拿到a锁了。。。。。");
}
}
}
死锁的解决方法暂时不说,避免出现死锁就行
4.4 线程通信:
- 锁.wait() :释放锁和资源,并进入等待队列
- 锁.notify() : 从等待队列中随机唤醒一个线程
- 锁.notifyAll() :将等待队列中的所有线程都唤醒
存取钱的例子:
public class ThreadMethods {
static int index = 0;
public static void main(String[] args) throws InterruptedException {
BankCard card = new BankCard();
Thread t1 = new Thread(new AddMoneyRunnable(card),"男一号");
Thread t3 = new Thread(new SubMoneyRunnable(card),"女一号");
t1.start();
t3.start();
}
}
class BankCard {//银行卡类
private double money = 0;
private boolean flag = false;
//true:有钱,能取,不能存
//false:没钱,不能取,能存
public synchronized void addMoney() throws InterruptedException {
if(flag) // this——>main()中的 BankCard card 对象,保证了锁的唯一型
this.wait();//有钱,不能再存了
money += 1000; // 存钱
System.out.println(Thread.currentThread().getName()+"存了1000, 余额:"+money);
flag = true; // 修改标记,可以取钱了,不能再存钱
this.notify(); //随机唤醒等待队列里的 一个 线程
}
public synchronized void subMoney() throws InterruptedException {
if(!flag) // flag = false 时进入等待队列
this.wait();//没钱,不能再取了
money -= 1000; // 取钱
System.out.println(Thread.currentThread().getName()+"取了1000, 余额:"+money);
flag = false; // 修改标记,可以存钱了,不能再取钱
this.notify(); //随机唤醒等待队列里的 一个 线程
}
}
class SubMoneyRunnable implements Runnable{
private BankCard card;
public SubMoneyRunnable(BankCard card) {
this.card = card;
}
public void run() {
while(true)
try {
card.subMoney();
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
class AddMoneyRunnable implements Runnable{
private BankCard card;
public AddMoneyRunnable(BankCard card) {
this.card = card;
}
public void run() {
while(true) {
try {
card.addMoney();
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
上面只有一存一取,两个人时,就没问题,当多人存多人取,就会出问题:
★ 问题1:多存多取——>突破上下限
只修改main() 方法,加多两个人,还是只有一张银行卡
好家伙,钱越来越多( 要是真能如此,就不用打工了)
还有一种越来越少的:
这就是多存多取的问题,问题的根源在这里:
★ 通过wait() 进入等待的线程 在被重新唤醒后 会从wait语句后接着执行
★ 上面的线程被唤醒后不会重新判断flag,没更新flag状态
解决方法: 让被唤醒的线程重新判断flag就行,将两个if改成while就行
造成上面越存越多的问题的过程:
- 男 1 号抢到CPU ,存钱成功,余额:1000,等待队列没人
- 男 1 号立马再次抢到CPU,存钱失败,余额:1000,进入等待队列,等待队列:男 1 号
- 男 2 号抢到CPU ,存钱失败,余额:1000,随机唤醒男 1 号,等待队列没人
- 男 2 号立马再次抢到CPU,存钱失败,余额:1000,进入等待队列,等待队列:男 2 号
- 男 1 号抢到CPU,继续存钱,余额:2000,随机唤醒男 2号,等待队列没人
- 男 1 号立马再次抢到CPU,存钱失败,余额:2000,进入等待队列,等待队列:男 1 号
- 男 2 号抢到CPU,继续存钱,余额:3000,随机唤醒男 1 号,等待队列没人
- 不断循环男一男二的互相唤醒,钱就越来越多了(真爽)
- 你说女一女二?腿短跑得慢没抢到过CPU, 或者男一男二不喜欢她们两个,每次唤醒都不叫她们
总结:多存多取,突破上下限的原因——使用if判断的线程在被唤醒后,不会重新判断条件是否符合
★ 问题2:唤醒后重复判断——>死锁?
改完while,问题又来了,不打印了,卡住了
问题根源:一些线程腿太长,连续抢到CPU,自投罗网,加上随机唤醒一个的时候没唤醒对的人
★ 不是 死锁,是所有线程都在wait,都进入等待队列了(两个取钱的速度太快了,像极了我)
造成全员等待的过程:(可能性之一,这个符合上面的结果而已)
- 女 1 号抢到CPU,取钱失败,余额:0,释放锁,进入等待队列,队列里:女 1 号
- 女 2 号抢到CPU,取钱失败,余额:0,释放锁,进入等待队列,队列里:女 1 号、女 2 号
- 男 1 号抢到CPU,存钱成功,将flag——>true,余额:1000,随机唤醒女 1 号,队列里:女 2 号 (打印第一句)
- 男 2 号抢到CPU,存钱失败 ,余额:1000,释放锁,进入等待队列,队列里:女 2 号、男 2 号
- 男 1 号抢到CPU,存钱失败 ,余额:1000,释放锁,进入等待队列,队列里:女 2 号、男 2 号、男 1 号
- 女 1 号抢到CPU,取钱成功,余额:0,将flag——>false,随机唤醒男 2 号 ,队列里:女 2 号、男 1 号 (打印第二句)
- 女 1 号厉害的再次抢到CPU,取钱失败,余额:0,释放锁,进入等待队列,随机唤醒男 2 号 ,队列里:女 2 号、男 1 号、女 1 号 (女 1 号的自投罗网)
- 男 2 号抢到CPU,存钱成功,将flag——>true,余额:1000,随机唤醒男 1 号,队列里:女 2 号、女 1 号 (此时错误的随机唤醒男 1 号,就GG了) (打印第三句)
- 男 1 号抢到CPU,存钱失败 ,余额:1000,释放锁,进入等待队列,队列里:女 2 号、男 2 号、男 1 号
- 男 2 号抢到CPU,存钱失败 ,余额:1000,释放锁,进入等待队列,队列里:女 2 号、男 2 号、男 1 号 、男 2 号 (全员GG,都在等待队列里了)
总结:造成全员等待的原因是——某个线程 连续 抢到线程后,随机唤醒时又错误唤醒了其他线程
⑽生产者与消费者例子:
public class ThreadMethods {
static int index = 0;
public static void main(String[] args) throws InterruptedException {
BreadCon breadCon = new BreadCon();
Thread p1 = new Thread(new ProducerRunnable(breadCon), "生产者1号");
Thread c1 = new Thread(new ConsumerRunnable(breadCon), "消费者1号");
Thread p2 = new Thread(new ProducerRunnable(breadCon), "生产者2号");
Thread c2 = new Thread(new ConsumerRunnable(breadCon), "消费者2号");
p1.start(); c1.start();
p2.start(); c2.start();
}
}
class BreadProduct {
private int id ;
private String ProductName;
public BreadProduct() {
super();
}
public BreadProduct(String productName) {
super();
this.ProductName = productName;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getProductName() {
return ProductName;
}
public void setProductName(String productName) {
ProductName = productName;
}
@Override
public String toString() {
return "BreadProduct [id=" + id + ", ProductName=" + ProductName + "]";
}
}
class BreadCon { //仓库
private BreadProduct[] breads = new BreadProduct[20];
private int index = 0;
private int breadId = 0;
public synchronized void input(BreadProduct bread) throws InterruptedException {
while(index > (breads.length-1))
this.wait();
bread.setId(++breadId);
breads[index++] = bread;
System.out.println(Thread.currentThread().getName() + "生产了"+bread.getId()+"号面包");
this.notifyAll();
}
public synchronized void output() throws InterruptedException {
while(index < 1)
this.wait();
--index;
System.out.println(Thread.currentThread().getName() + "消费了"
+ breads[index].getId() + "号面包,该面包生产者:"
+ breads[index].getProductName());
breads[index] = null;
this.notifyAll();
}
}
class ProducerRunnable implements Runnable{
private BreadCon breadCon;
public ProducerRunnable(BreadCon breadCon) {
super();
this.breadCon = breadCon;
}
public void run() {
try {
for (int i = 0; i < 30; i++)
breadCon.input(new BreadProduct(Thread.currentThread().getName()));
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
class ConsumerRunnable implements Runnable{
private BreadCon breadCon;
public ConsumerRunnable(BreadCon breadCon) {
super();
this.breadCon = breadCon;
}
public void run() {
try {
for (int i = 0; i < 30; i++)
breadCon.output();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
链接:https://pan.baidu.com/s/1sV7KN-ZoCYeN_FT2VZS6NA
提取码:1234
- 加锁:只有当前线程取到了某个锁,才可以调用该锁的方法【锁.wait()等】。
例如:A线程拿到了a锁,能使用a.wait()、a.notify(),却不能执行锁b.wait(),因为A线程没有取得b锁 - 当前线程.yield():放弃当前线程已获取的CPU时间片,进入等待队列。仅仅放弃了CPU,不会释放已经持有的锁和其他资源。
- java多线程可以抢占式运行,但JDK提倡协作式运行,这就是为什么JDK将stop()、suspend()、resume()设为不推荐方法的原因,这里不详细说其他文章再写
线程执行了yield()后,并不会释放锁的例子
public class ThreadMethods {
static int index = 0;
public static void main(String[] args) throws InterruptedException {
new Thread(new YieldRunnable2(), "yield").start();
Thread.sleep(1000);//确保a锁先被YieldRunnable2拿到
new Thread(new OtherBoyRunnable(), "other").start();
}
}
class YieldRunnable2 implements Runnable{
public void run() {
int number = 2;
synchronized (MyLock.a) {
System.out.println("我拿到了a锁");
for (int i = 0; i < 10; i++) {
System.out.println("yield:我让步了,放弃了CPU,但是还拿着a锁");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Thread.yield();
}
System.out.println("yield:我运行完了,释放a锁");
}
}
}
class OtherBoyRunnable implements Runnable{
public void run() {
System.out.println("OtherBoy:我什么锁都没拿到,我想要拿到a锁");
synchronized (MyLock.a) {
System.out.println("OtherBoy:我拿到a锁了。。。。。");
}
}
}