javaSE——多线程基础

目录

  1. 进程和线程的定义、区别、组成
  2. 线程的使用
    2.1. 线程的创建:继承Thread类、实现Runnable接口(匿名内部类)
    2.2. 线程的使用
  3. 线程的基本状态和方法
    3.1 基本状态
    3.2 方法
  4. 多线程安全问题
    4.1 同步锁和同步代码块
    4.2 同步方法
    4.3 死锁
    4.4 线程通信

本文章的所有代码
零碎知识



一、进程和线程的定义、区别

1. 定义:

  • 进程:进程是正在运行的程序的实例
  • 线程:进程中的一个执行任务(控制单元),负责当前进程中程序某部分代码的执行。
    javaSE——多线程基础_第1张图片
    javaSE——多线程基础_第2张图片

2. 区别:

  • 进程是操作系统资源分配的基本单位,线程是CPU的基本调度单位
  • 一个程序运行后至少有一个进程
  • 一个进程可以包含多个线程,但至少要有一个线程,不然该进程没有意义
  • 进程间不能共享数据地址,但同个进程下的多个线程间可以共享。

3. 线程的组成:

任何一个线程都具有基本的组成部分:

CPU时间片:操作系统 (OS)为每个线程分配的执行时间

运行数据
堆空间:存储线程需要使用的对象,多个线程可以共享堆中的对象
栈空间:存储线程需要使用的局部变量,每个线程都拥有独立的栈,不共享

③ 线程的具体代码

4.线程的特点:

①. 线程抢占式执行
效率高
可防止单一线程长时间独占CPU
②. 在 单核 CPU中:宏观并行,微观串行


二、线程的使用


Thread类的中文文档
Runnable中文文档


2.1. 线程的创建:继承Thread类、实现Runnable接口(匿名内部类)

继承Thread类的创建方式:

换句话说就是继承Thread,重写run() 方法,就可以使用了
javaSE——多线程基础_第3张图片
实现Runnable接口的创建方式:

  1. 实现Runnable接口, 一样重写run() 方法
  2. 用Runnable对象包装自定义类
  3. 再使用Thread的构造方法创建Thread对象,该Thread对象就是新线程了

javaSE——多线程基础_第4张图片
可以简写成:(没必要包装成Runnable的)javaSE——多线程基础_第5张图片
实现Runnable接口的还有一种写法——匿名内部类
javaSE——多线程基础_第6张图片
简写:javaSE——多线程基础_第7张图片


2.2. 线程的使用

抢占式执行演示:javaSE——多线程基础_第8张图片
javaSE——多线程基础_第9张图片
获取线程的ID和Name:(推荐Thread.currentThread() 方式)
javaSE——多线程基础_第10张图片

设置线程Name:(ID无法修改)

  1. setName()
  2. 创建时初始化javaSE——多线程基础_第11张图片

★ 用run()启动和用start()启动的区别: start()并行,run()串行
javaSE——多线程基础_第12张图片
javaSE——多线程基础_第13张图片
用run() 启动的线程类失去了多线程的意义,跟普通类没区别
(有种说法:run() 是在调用线程对象的方法,就跟调用普通对象的方法一样,但run() 确实也启动了线程类,可以看下下面两张图的区别)

javaSE——多线程基础_第14张图片
javaSE——多线程基础_第15张图片
用this.get直接获取的就不能发现
javaSE——多线程基础_第16张图片
javaSE——多线程基础_第17张图片

三、线程的基本状态 和 方法

3.1 基本状态:

  1. NEW 初始态:new
  2. RUNNABLE 就绪态:start
  3. RUNNING 运行态:run
  4. WAITING 等待态:sleep,join,wait
  5. BLOCK 阻塞:synchronized
  6. TERMINATED 终止态stop

初始态:只在堆中开辟内存与普通对象无异
就绪态:执行了start()方法,等待操作系统分配CPU时间片给它
运行态:操作系统给予CPU时间片,正在执行
等待态:线程进入等待或休眠状态,放弃CPU片,但不一定会释放锁和其他资源
阻塞:阻塞状态是指线程因为某种原因失去了cpu使用权,暂时停止运行,直到线程重新进入运行态。
终止态:线程结束生命周期

jdk1.5后 就绪态和运行态合并为 运行态Runnable
javaSE——多线程基础_第18张图片
在这里插入图片描述
状态图:

javaSE——多线程基础_第19张图片
Thread源码中的所有状态值:
javaSE——多线程基础_第20张图片
TIMED_WAITING即sleep,指定时间长度的等待

3.2 方法:

⑴sleep()休眠 :休眠指定毫秒数,自动苏醒javaSE——多线程基础_第21张图片
javaSE——多线程基础_第22张图片

⑵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)+"次运行,休息一秒");
		} 
	}
}

javaSE——多线程基础_第23张图片
⑶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。");
	}
}

javaSE——多线程基础_第24张图片
只会阻塞当前线程(在哪个线程里写的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: 我打印完了---------------------------");
	}
}

javaSE——多线程基础_第25张图片
⑸守护线程(后台线程):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));
	}
}

加同步锁就不会出现(一般情况没有,实际上还是有问题的)
javaSE——多线程基础_第26张图片
卖票例子:

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--;//修改共享资源
		}
	}
}

javaSE——多线程基础_第27张图片
加了同步锁后:

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--;//修改共享资源
			} } }
}

javaSE——多线程基础_第28张图片
javaSE——多线程基础_第29张图片
错误加锁的例子之一:位置不对
javaSE——多线程基础_第30张图片
在这里插入图片描述
当然,不是说加同步锁的范围越大越好,要考虑运行效率和其他的问题

★ 能不加锁尽量不加锁

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("女孩可以吃饭了");
			}
		}
	}
}

互不想让,形成死锁
javaSE——多线程基础_第31张图片
形成死锁的线程不会释放已经拥有的锁,这样会一直堵塞,例如:
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锁了。。。。。");
		}
	}
}

javaSE——多线程基础_第32张图片
死锁的解决方法暂时不说,避免出现死锁就行

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() 方法,加多两个人,还是只有一张银行卡
javaSE——多线程基础_第33张图片

好家伙,钱越来越多( 要是真能如此,就不用打工了)
javaSE——多线程基础_第34张图片
还有一种越来越少的:
javaSE——多线程基础_第35张图片

这就是多存多取的问题,问题的根源在这里:在这里插入图片描述
在这里插入图片描述
★ 通过wait() 进入等待的线程 在被重新唤醒后 会从wait语句后接着执行

★ 上面的线程被唤醒后不会重新判断flag,没更新flag状态
解决方法: 让被唤醒的线程重新判断flag就行,将两个if改成while就行在这里插入图片描述
造成上面越存越多的问题的过程:

  1. 男 1 号抢到CPU ,存钱成功,余额:1000,等待队列没人
  2. 男 1 号立马再次抢到CPU,存钱失败,余额:1000,进入等待队列,等待队列:男 1 号
  3. 男 2 号抢到CPU ,存钱失败,余额:1000,随机唤醒男 1 号,等待队列没人
  4. 男 2 号立马再次抢到CPU,存钱失败,余额:1000,进入等待队列,等待队列:男 2 号
  5. 男 1 号抢到CPU,继续存钱,余额:2000,随机唤醒男 2号,等待队列没人
  6. 男 1 号立马再次抢到CPU,存钱失败,余额:2000,进入等待队列,等待队列:男 1 号
  7. 男 2 号抢到CPU,继续存钱,余额:3000,随机唤醒男 1 号,等待队列没人
  8. 不断循环男一男二的互相唤醒,钱就越来越多了(真爽)
  9. 你说女一女二?腿短跑得慢没抢到过CPU, 或者男一男二不喜欢她们两个,每次唤醒都不叫她们

总结:多存多取,突破上下限的原因——使用if判断的线程在被唤醒后,不会重新判断条件是否符合


★ 问题2:唤醒后重复判断——>死锁?
改完while,问题又来了,不打印了,卡住了javaSE——多线程基础_第36张图片

javaSE——多线程基础_第37张图片

问题根源:一些线程腿太长,连续抢到CPU,自投罗网,加上随机唤醒一个的时候没唤醒对的人
javaSE——多线程基础_第38张图片
★ 不是 死锁,是所有线程都在wait,都进入等待队列了(两个取钱的速度太快了,像极了我)

造成全员等待的过程:(可能性之一,这个符合上面的结果而已)

  1. 女 1 号抢到CPU,取钱失败,余额:0,释放锁,进入等待队列,队列里:女 1 号
  2. 女 2 号抢到CPU,取钱失败,余额:0,释放锁,进入等待队列,队列里:女 1 号、女 2 号
  3. 男 1 号抢到CPU,存钱成功,将flag——>true,余额:1000,随机唤醒女 1 号,队列里:女 2 号 (打印第一句)
  4. 男 2 号抢到CPU,存钱失败 ,余额:1000,释放锁,进入等待队列,队列里:女 2 号、男 2 号
  5. 男 1 号抢到CPU,存钱失败 ,余额:1000,释放锁,进入等待队列,队列里:女 2 号、男 2 号、男 1 号
  6. 女 1 号抢到CPU,取钱成功,余额:0,将flag——>false,随机唤醒男 2 号 ,队列里:女 2 号、男 1 号 (打印第二句)
  7. 女 1 号厉害的再次抢到CPU,取钱失败,余额:0,释放锁,进入等待队列,随机唤醒男 2 号 ,队列里:女 2 号、男 1 号、女 1 号 (女 1 号的自投罗网)
  8. 男 2 号抢到CPU,存钱成功,将flag——>true,余额:1000,随机唤醒男 1 号,队列里:女 2 号、女 1 号 (此时错误的随机唤醒男 1 号,就GG了) (打印第三句)
  9. 男 1 号抢到CPU,存钱失败 ,余额:1000,释放锁,进入等待队列,队列里:女 2 号、男 2 号、男 1 号
  10. 男 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();
		}
	}
}



回到目录

本文章的所有代码:密码1234

链接: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锁了。。。。。");
		}
	}
}

你可能感兴趣的:(javaSE,并发编程,java,多线程)