目录
线程基本概念
1、什么是进程?什么是线程?
进程是一个应用程序,线程是一个进程中的执行场景/执行单元。一个进程可以启动多个线程。在java语言中对于两个线程A和B,堆内存和方法区内存共享。但是栈内存独立,一个线程一个栈。 在使用了多线程机制之后,main()方法结束了,只是主线程结束了,主栈空了,但其他线程不一定结束,其他栈(线程)可能还在压栈弹栈。
1、java实现线程
java语言支持多线程机制。并且java已经实现了多线程(java.lang.Thread类和java.lang.Runnable接口)
第一种实现方式(继承java.lang.Thread类并重写run方法)
-
public class Thread_01 extends Thread {
-
-
public void run() {
-
super.run();
-
System.out.println("第一个线程");
-
}
-
-
public static void main(String[] args) {
-
Thread thread=new Thread(new Thread_01(),"first_thread"); //创建线程对象
-
thread.start(); //启动线程 start方法使线程处于就绪队列,等待CPU调用
-
}
-
}
第二种实现方式(实现java.lang.Runnable接口并实现run方法)
-
public class Thread_02 implements Runnable {
-
-
public void run() {
-
System.out.println("第一个线程");
-
}
-
public static void main(String[] args) {
-
Thread thread=new Thread(new Thread_01(),"first_thread"); //创建线程对象
-
thread.start(); //启动线程 start方法使线程处于就绪队列,等待CPU调用
-
}
-
}
通常使用第二种方法,因为一个类实现了接口还可以继承其他类
注意:start方法和run方法的区别!!!
run方法不会启动线程。
start方法的作用是:启动一个线程,在JVM中为线程开辟一个新的栈空间。之后start方法就结束了。线程启动成功并进入排队等待序列。等到被CPU调用到,就会自动调用run方法。
2、线程的生命周期
3、线程常用的方法
3.1、sleep()
public static native void sleep(long millis) throws InterruptedException;
作用:让线程进入休眠,进入“阻塞状态”。放弃占有CPU时间片,让其他线程使用。
-
public class Thread_03 {
-
public static void main(String[] args) throws InterruptedException {
-
Thread thread=new Thread(new Thread_03_1(),"Thread_03_1");
-
thread.start();
-
int count=1; //计数器
-
while(true){
-
System.out.println("Thread_03_1线程沉睡了"+count+++"秒");
-
Thread.sleep(1000);
-
if(count>5){
-
break;
-
}
-
}
-
}
-
-
}
-
-
class Thread_03_1 implements Runnable{
-
-
public void run() {
-
try {
-
Thread.sleep(1000*5); //让线程沉睡(等待) 5秒
-
} catch (InterruptedException e) {
-
e.printStackTrace();
-
}
-
System.out.println(Thread.currentThread().getName());
-
}
-
}
3.2、interrupt方法
interrupt方法可以中断线程的睡眠,依靠了java异常处理机制
-
public class Thread_03 {
-
public static void main(String[] args) throws InterruptedException {
-
Thread thread=new Thread(new Thread_03_1(),"Thread_03_1");
-
thread.start();
-
int count=1; //计数器
-
while(true){
-
System.out.println("Thread_03_1线程沉睡了"+count+++"秒");
-
Thread.sleep(1000);
-
if(count==3){
-
System.out.println("打断Thread_03_1睡眠");
-
thread.interrupt();
-
break;
-
}
-
}
-
}
-
-
}
-
-
class Thread_03_1 implements Runnable{
-
-
public void run() {
-
try {
-
Thread.sleep(1000*5); //让线程沉睡(等待) 5秒
-
} catch (InterruptedException e) {
-
e.printStackTrace();
-
}
-
System.out.println(Thread.currentThread().getName()+"醒了");
-
}
-
}
线程Thread_03_1原计划沉睡5秒,在它睡到3秒时,使用interrupt方法打断其睡眠。
3.3、stop方法
stop方法可以强制终止一个线程的执行。不过这种方式容易丢失数据。因为这种方式会直接杀死线程,线程没有保存的数据会丢失。所以不建议使用
-
public class Thread_04 {
-
public static void main(String[] args) throws InterruptedException {
-
Thread thread = new Thread(new Thread_04_1(), "Thread_03_1");
-
thread.start();
-
//线程Thread_03_1执行5秒后,强制结束线程Thread_03_1
-
Thread.sleep(1000*6);
-
System.out.println("强制终止线程Thread_03_1");
-
thread.stop(); //强制终止线程
-
}
-
}
-
class Thread_04_1 implements Runnable{
-
-
public void run() {
-
for(int i=1;i<1000;i++) {
-
try {
-
Thread.sleep(1000);
-
} catch (InterruptedException e) {
-
e.printStackTrace();
-
}
-
System.out.println(Thread.currentThread()+"-->"+i+"秒");
-
}
-
}
-
}
建议使用如下方法结束一个线程:在线程类中增加一个布尔类型的变量run,通过改变run的值,来控制线程运行/停止状态。
-
public class Thread_05 {
-
public static void main(String[] args) throws InterruptedException {
-
Thread_05_1 t=new Thread_05_1();
-
Thread thread = new Thread(t, "Thread_05_1");
-
thread.start();
-
//等候5秒之后,终止该线程
-
Thread.sleep(1000*5);
-
t.run=false;
-
System.out.println(thread.getName()+"线程已暂停");
-
}
-
}
-
-
class Thread_05_1 implements Runnable{
-
boolean run=true; //通过引入一个布尔类型的变量,来标记该线程的状态(运行/停止)
-
-
public void run() {
-
for (int i = 1; i < 1000; i++) {
-
if (run) {
-
System.out.println(Thread.currentThread().getName() + "-->" + i + "秒");
-
try {
-
Thread.sleep(1000);
-
} catch (InterruptedException e) {
-
e.printStackTrace();
-
}
-
}else{
-
/**
-
* return就表示该线程结束了
-
* 如果有什么需要保存的,可以写在return之前
-
*/
-
return;
-
}
-
}
-
}
-
}
4、线程调度
4.1、常见的线程调度模型
抢占式调度模型、均分式调度模型
4.2、java中提供的线程调度方法
-
void setPriority(int newPriority) //设置线程优先级
-
-
int getPriority() //获取线程优先级
最低优先级:1 默认优先级:5 最高优先级:10
优先级高的线程抢占的CPU时间片就多一些,处于运行状态的时间片就多一些
-
public class Thread_06 {
-
public static void main(String[] args) {
-
Thread_06_1 t61=new Thread_06_1();
-
Thread_06_2 t62=new Thread_06_2();
-
Thread_06_3 t63=new Thread_06_3();
-
Thread thread1=new Thread(t61,"Thread_06_1");
-
Thread thread2=new Thread(t62,"Thread_06_2");
-
Thread thread3=new Thread(t63,"Thread_06_3");
-
//设置线程优先级
-
thread1.setPriority(1); thread2.setPriority(2); thread3.setPriority(10);
-
thread1.start(); thread2.start(); thread3.start();
-
System.out.println("主线程优先级为:"+Thread.currentThread().getPriority());
-
for(int i=0;i<1000;i++){
-
System.out.println(Thread.currentThread().getName()+"-->"+i);
-
}
-
}
-
}
-
class Thread_06_1 implements Runnable{
-
-
public void run() {
-
System.out.println(Thread.currentThread().getName()+"优先级为:"+Thread.currentThread().getPriority());
-
for(int i=0;i<1000;i++){
-
System.out.println(Thread.currentThread().getName()+"-->"+i);
-
}
-
}
-
}
-
-
class Thread_06_2 implements Runnable{
-
-
public void run() {
-
System.out.println(Thread.currentThread().getName()+"优先级为:"+Thread.currentThread().getPriority());
-
for(int i=0;i<1000;i++){
-
System.out.println(Thread.currentThread().getName()+"-->"+i);
-
}
-
}
-
}
-
-
class Thread_06_3 implements Runnable{
-
-
public void run() {
-
System.out.println(Thread.currentThread().getName()+"优先级为:"+Thread.currentThread().getPriority());
-
for(int i=0;i<1000;i++){
-
System.out.println(Thread.currentThread().getName()+"-->"+i);
-
}
-
}
-
}
4.3、线程让步
yield方法:使当前线程暂停,回到就绪状态,让给其他线程
public static native void yield();
-
public class Thread_07 {
-
public static void main(String[] args) {
-
Thread_07_1 tt=new Thread_07_1();
-
Thread thread=new Thread(tt,"Thread_07_1");
-
thread.start();
-
for(int i=0;i<100;i++){
-
System.out.println(Thread.currentThread().getName()+"-->"+i);
-
}
-
}
-
}
-
class Thread_07_1 implements Runnable{
-
-
-
public void run() {
-
for(int i=0;i<100;i++){
-
if(i%10==0){
-
System.out.println(Thread.currentThread().getName()+"暂停了一下");
-
Thread.yield(); //让当前线程暂停一下
-
}
-
System.out.println(Thread.currentThread().getName()+"-->"+i);
-
}
-
}
-
}
4.4、线程合并
join方法可以使得在t.join()中让CPU优先执行完t。将t合并到当前线程中,使当前线程受阻,t线程执行直到结束。
-
public class Thread_08 {
-
public static void main(String[] args) throws InterruptedException {
-
Thread_08_1 thread_08_1=new Thread_08_1();
-
Thread_08_2 thread_08_2=new Thread_08_2();
-
Thread thread=new Thread(thread_08_1,"Thread_08_1");
-
Thread thread1=new Thread(thread_08_2,"Thread_08_2");
-
thread.start();
-
//合并线程
-
thread.join(); //t合并到当前线程中,当前线程受阻塞,t线程执行直到结束
-
thread1.start();
-
thread1.join();
-
//Thread.currentThread().join();
-
System.out.println("main over");
-
}
-
}
-
class Thread_08_1 implements Runnable{
-
-
-
public void run() {
-
for(int i=0;i<3;i++){
-
System.out.println(Thread.currentThread().getName()+"-->"+i);
-
}
-
}
-
}
-
class Thread_08_2 implements Runnable{
-
-
-
public void run() {
-
for(int i=0;i<3;i++){
-
System.out.println(Thread.currentThread().getName()+"-->"+i);
-
}
-
}
-
}
join()的底层实现代码。
-
public final void join() throws InterruptedException {
-
join(0);
-
}
-
-
-
public final synchronized void join(long millis)
-
throws InterruptedException {
-
long base = System.currentTimeMillis();
-
long now = 0;
-
-
if (millis < 0) {
-
throw new IllegalArgumentException("timeout value is negative");
-
}
-
-
if (millis == 0) {
-
while (isAlive()) {
-
wait(0);
-
}
-
} else {
-
while (isAlive()) {
-
long delay = millis - now;
-
if (delay <= 0) {
-
break;
-
}
-
wait(delay);
-
now = System.currentTimeMillis() - base;
-
}
-
}
-
}
join()是在底层调用了wait方法,当主线程调用了thread.join()之后,主线程进入此方法,调用join()方法中的wait(0)方法,wait(0)表示无限等待直到被notify。即主线程会无限等待thread线程执行完成。
-
public final void wait(long timeout, int nanos) throws InterruptedException {
-
if (timeout < 0) {
-
throw new IllegalArgumentException("timeout value is negative");
-
}
-
-
if (nanos < 0 || nanos > 999999) {
-
throw new IllegalArgumentException(
-
"nanosecond timeout value out of range");
-
}
-
-
if (nanos > 0) {
-
timeout++;
-
}
-
-
wait(timeout);
-
}
4.5、线程安全
数据在多线程并发的环境下会存在安全问题。例如如果多个用户想要修改某个共享的数据,就会引发线程安全问题。因此需要引入线程同步机制(即线程排队执行,不能并发)
4.5.1、线程同步的实现
java里面通过关键字synchronized给线程加锁。线程会获取锁,并独占cpu,只有当线程释放了锁之后,其余线程拿到锁之后才能运行。
当一个线程在运行状态时遇到synchronized关键字,该线程就会放弃占有的cpu时间片,在锁池里面找共享对象的对象锁。
一个线程同步synchronized的例子——模拟ATM机取款
Account类
-
package ATM;
-
/*
-
银行账户,使用线程同步机制,解决线程安全问题
-
*/
-
public class Account {
-
private String accountName; //账户名
-
private double remain; //余额
-
Object obj=new Object();
-
-
public Account(String accountName, int remain) {
-
this.accountName = accountName;
-
this.remain = remain;
-
}
-
-
public String getAccountName() {
-
return accountName;
-
}
-
-
public void setAccountName(String accountName) {
-
this.accountName = accountName;
-
}
-
-
public double getRemain() {
-
return remain;
-
}
-
-
public void setRemain(double remain) {
-
this.remain = remain;
-
}
-
//取款方法
-
public void withdraw(double money) {
-
//synchronized (obj){ //obj是一个全局变量,实例一次Account创建一个obj对象,是可以被共享的
-
-
/*Object obj1=new Object();
-
synchronized (obj1){*/ //obj1是一个局部变量,每调用一次run方法则会创建一个obj1对象,所以obj1不是共享对象
-
-
//synchronized (null){ //报错:空指针
-
//synchronized ("abc"){ //"abc"在字符串常量池当中,会让所有的线程都同步
-
synchronized (this){
-
double before = this.getRemain(); //取款之前的余额
-
double after = before - money; //取款之后的余额
-
try{
-
Thread.sleep(1000);
-
} catch (InterruptedException e) {
-
e.printStackTrace();
-
}
-
this.setRemain(after); //更新余额
-
}
-
}
-
}
AccountThread类
-
package ATM;
-
-
public class AccountThread extends Thread {
-
//两个线程必须共享同一个账户对象
-
private Account act;
-
private double money; //取款金额
-
-
//通过构造方法传递过来构造对象
-
public AccountThread(Account act,double money) {
-
this.act = act;
-
this.money=money;
-
}
-
-
public void run() { //run方法执行表示取款操作
-
act.withdraw(money);
-
System.out.println(Thread.currentThread().getName()+"在账户"+
-
act.getAccountName()+"取款"+money+"之后余额为:"+act.getRemain());
-
}
-
}
启动类:Test类
-
package ATM;
-
-
public class Test {
-
public static void main(String[] args) {
-
Account act=new Account("act001",10000); //创建账户对象
-
-
//创建两个线程,对同一账户取款
-
Thread threadA=new AccountThread(act,5000);
-
Thread threadB=new AccountThread(act,3000);
-
-
threadA.setName("小明");
-
threadB.setName("小华");
-
-
threadA.start();
-
threadB.start();
-
}
-
}
结果:
-
//synchronized可以用在实例方法上,此时锁是this
-
public synchronized void withdraw(double money) {
-
double before = this.getRemain(); //取款之前的余额
-
double after = before - money; //取款之后的余额
-
try {
-
Thread.sleep(1000);
-
} catch (InterruptedException e) {
-
e.printStackTrace();
-
}
-
this.setRemain(after); //更新余额
-
}
-
}
可以在实例方法上使用synchronized,synchronized出现在实例方法上,锁一定是this,不能是其他的对象了。所以这种方式不灵活。
另外还有一个缺点:synchronized出现在实例方法上,表示整个方法体都需要同步,可能会无故扩大同步的范围,导致程序的执行效率降低。所以这种方式不常用。
4.5.2、java中的线程安全性
java中有三大变量 :1、实例变量(在堆中) 2、静态变量(在方法中) 3、局部变量(在栈中)
局部变量不会有线程安全问题,因为局部变量不共享,局部变量在栈中,一个线程一个栈。
常量不会有线程安全问题,因为常量不会被改变。
实例变量在堆中,堆只有1个。静态变量在方法区中,方法区只有1个。由于堆和方法区都是多线程可共享的,所以实例变量和静态变量可能存在线程安全问题。
ArrayList、HashMap 、HashSet 是非线程安全的
Vector、Hashtable 是线程安全的
4.5.3、synchronized总结
synchronized有三种写法:
第一种:同步代码块(灵活)
synchronized(线程共享对象){
同步代码块;
}
第二种:在实例方法上使用 synchronized
表示共享对象一定是this,并且同步代码块是整个方法体。
第三种:在静态方法上使用 synchronized
表示找类锁。
类锁永远只有1把(就算创建了100个对象,那类锁也只有1把)
对象锁:1个对象1把锁,100个对象100把锁。 类锁:100个对象,也可能只是1把类锁。
-
package Synchronized;
-
-
//Q:doOther方法执行的时候需要等待doSome方法的结束吗?
-
//A:需要,因为静态方法是类锁,不管创建了几个对象,类锁只有1把
-
public class Exam01 {
-
public static void main(String[] args) throws InterruptedException {
-
MyClass mc1 = new MyClass();
-
MyClass mc2 = new MyClass();
-
Thread t1 = new Mythread(mc1);
-
Thread t2 = new Mythread(mc2);
-
t1.setName("t1");
-
t2.setName("t2");
-
t1.start();
-
Thread.sleep(1000);
-
t2.start();
-
}
-
}
-
-
class Mythread extends Thread {
-
private MyClass mc;
-
-
public Mythread(MyClass mc) {
-
this.mc = mc;
-
}
-
-
-
public void run() {
-
super.run();
-
if (Thread.currentThread().getName().equals("t1")) {
-
try {
-
mc.doSome();
-
} catch (InterruptedException e) {
-
e.printStackTrace();
-
}
-
}
-
if (Thread.currentThread().getName().equals("t2")) {
-
mc.doOther();
-
}
-
}
-
}
-
-
class MyClass {
-
//synchronized出现在静态方法上是走 类锁
-
public synchronized static void doSome() throws InterruptedException {
-
System.out.println("doSome begin");
-
Thread.sleep(1000 * 5);
-
System.out.println("doSome over");
-
}
-
-
public synchronized static void doOther() {
-
System.out.println("doOther begin");
-
System.out.println("doOther over");
-
}
-
}
4.5.4、死锁
一个简单的死锁实现的例子
-
package Thread;
-
-
/*
-
实现一个死锁,
-
t1中obj1锁上后就睡了,t2中obj2锁上后就睡了。
-
t1想要释放obj1锁,就必须请求到obj2锁
-
t2想要释放obj2锁,就必须请求到obj1锁
-
t1与t2互相请求锁,但彼此都无法释放锁,所以形成了死锁
-
-
*/
-
public class Dead_lock {
-
public static void main(String[] args) {
-
Object obj1 = new Object();
-
Object obj2 = new Object();
-
-
//t1,t2两个线程共享o1,o2
-
Thread t1 = new MyThread_1(obj1, obj2);
-
Thread t2 = new MyThread_2(obj1, obj2);
-
t1.start();
-
t2.start();
-
}
-
}
-
-
class MyThread_1 extends Thread {
-
Object obj1 = new Object();
-
Object obj2 = new Object();
-
-
public MyThread_1(Object obj1, Object obj2) {
-
this.obj1 = obj1;
-
this.obj2 = obj2;
-
}
-
-
-
public void run() {
-
synchronized (obj1) {
-
try {
-
Thread.sleep(1000 * 3);
-
} catch (InterruptedException e) {
-
e.printStackTrace();
-
}
-
synchronized (obj2) {
-
-
}
-
}
-
}
-
}
-
-
class MyThread_2 extends Thread {
-
Object obj1 = new Object();
-
Object obj2 = new Object();
-
-
public MyThread_2(Object obj1, Object obj2) {
-
this.obj1 = obj1;
-
this.obj2 = obj2;
-
}
-
-
-
public void run() {
-
synchronized (obj2) {
-
try {
-
Thread.sleep(1000 * 3);
-
} catch (InterruptedException e) {
-
e.printStackTrace();
-
}
-
synchronized (obj1) {
-
-
}
-
}
-
}
-
}
-
注:synchronized在开发中最好不要嵌套使用,一不小心就可能导致死锁现象发生
4.6、守护线程
java语言中线程可分为两大类:
一类是:用户线程 例如:main方法主线程
一类是:守护线程(后台线程) 例如java垃圾回收线程
4.6.1、守护线程的特点
一般守护线程是一个死循环,所有用户线程只要结束,守护线程自动结束。
守护线程一般会用在一些定时任务,例如每天0点系统自动备份需要用到定时器,我们可以将定时器设置为守护线程一直在那里看着。每到0点就备份一次。所有的用户线程如果结束了,守护线程就自动退出。
4.6.2、一个简单的守护线程的例子
-
package Thread;
-
-
/**
-
* 用户线程备份数据,守护线程守护。
-
*/
-
public class GuardThread {
-
public static void main(String[] args) {
-
Thread thread = new BakDataThread();
-
thread.setName("备份数据的线程");
-
-
//启动线程之前,将线程设置为守护线程
-
thread.setDaemon(true);
-
thread.start();
-
for (int i = 0; i < 5; i++) {
-
System.out.println(Thread.currentThread().getName() + "--->" + i);
-
try {
-
Thread.sleep(1000);
-
} catch (InterruptedException e) {
-
e.printStackTrace();
-
}
-
}
-
}
-
}
-
-
class BakDataThread extends Thread {
-
-
public void run() {
-
int i = 0;
-
//即使是死循环,但由于该线程是守护者。当用户线程结束,守护线程自动终止
-
while (true) {
-
System.out.println(Thread.currentThread().getName() + "--->" + i++);
-
try {
-
Thread.sleep(1000);
-
} catch (InterruptedException e) {
-
e.printStackTrace();
-
}
-
}
-
}
-
}
-
5、定时任务
5.1、实现一个定时器
-
package Thread;
-
-
import java.text.ParseException;
-
import java.text.SimpleDateFormat;
-
import java.util.Date;
-
import java.util.Timer;
-
import java.util.TimerTask;
-
-
public class TimerTest {
-
public static void main(String[] args) throws ParseException {
-
//创建定时器对象
-
Timer timer = new Timer();
-
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
-
Date firstTime = sdf.parse("2020-07-25 19:10:30");
-
//指定定时任务
-
//timer.schedule(定时任务,第一次执行时间时间,间隔多久执行一次)
-
timer.schedule(new LogTimerTask(), firstTime, 1000 * 5);
-
}
-
}
-
-
//编写一个定时任务
-
class LogTimerTask extends TimerTask {
-
-
-
public void run() {
-
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
-
String strTime = sdf.format(new Date());
-
System.out.println(strTime + ":成功完成了一次数据备份!");
-
}
-
}
6、通过Callable接口实现一个线程
使用Callable接口实现线程,可以获得该线程的返回值(JDK8新特性)
一个简单的实例:
-
package Thread;
-
-
import java.util.concurrent.Callable;
-
import java.util.concurrent.ExecutionException;
-
import java.util.concurrent.FutureTask;
-
-
public class ThirdWay {
-
public static void main(String[] args) throws ExecutionException, InterruptedException {
-
FutureTask futureTask = new FutureTask(new Task());
-
Thread thread = new Thread(futureTask);
-
thread.start();
-
Object object = futureTask.get(); //通过get方法可以获取当前线程的返回值
-
////主线程中这里的程序必须等待get()方法结束才执行
-
// get()方法为了拿另一个线程的执行结果需要等待其执行完成,因此要等待较长时间
-
System.out.print("线程执行结果:");
-
System.out.println(object);
-
}
-
}
-
-
class Task implements Callable {
-
-
-
public Object call() throws Exception {
-
System.out.println("call method begin");
-
Thread.sleep(1000 * 10);
-
System.out.println("call method end");
-
int a = 100;
-
int b = 200;
-
return a + b;
-
}
-
}
FutureTask类相关源码
-
public FutureTask(Callable<V> callable) {
-
if (callable == null)
-
throw new NullPointerException();
-
this.callable = callable;
-
this.state = NEW; // ensure visibility of callable
-
}
-
-
public V get() throws InterruptedException, ExecutionException {
-
int s = state;
-
if (s <= COMPLETING)
-
s = awaitDone(false, 0L);
-
return report(s);
-
}
Future类中实现了get方法获取传入线程的返回结果
7、Object类中的wait和notify方法
7.1、wait和notify方法介绍
wait和notify方法不是线程对象的方法,不能通过线程对象调用。
-
Object object=new Object();
-
object.wait();//object.wait()让正在object对象上活动的线程进入等待状态,无限等待,直到被唤醒为止
-
object.notify();//object.notify()唤醒正在object对象上等待的线程
-
object.notifyAll();//object.notifyAll唤醒正在object对象上等待的所有线程
7.2、生产者和消费者模式
一个简单的生产者和消费者实例
模拟生产者生产一个,消费者就消费一个。让仓库始终零库存。
-
package Thread;
-
-
import java.awt.*;
-
import java.util.*;
-
import java.util.List;
-
-
public class Producer_Consumer {
-
public static void main(String[] args) {
-
List<String> list = new ArrayList<String>();
-
Thread thread1 = new Thread(new Consumer(list), "消费者线程");
-
Thread thread2 = new Thread(new Producer(list), "生产者线程");
-
thread1.start();
-
thread2.start();
-
}
-
}
-
-
//消费者线程
-
class Consumer implements Runnable {
-
//仓库
-
private List<String> list;
-
-
public Consumer(List<String> list) {
-
this.list = list;
-
}
-
-
-
public void run() {
-
//消费
-
while (true) {
-
synchronized (list) {
-
//如果仓库已经空了,消费者线程等待并释放list集合的锁
-
if (list.size() == 0) {
-
try {
-
list.wait();
-
} catch (InterruptedException e) {
-
e.printStackTrace();
-
}
-
}
-
//仓库中有商品,消费者进行消费
-
String str = list.remove(0);
-
System.out.println(Thread.currentThread().getName() + " 消费 " + str);
-
list.notify(); //唤醒生产者
-
}
-
-
}
-
}
-
}
-
-
//生产者线程
-
class Producer implements Runnable {
-
//仓库
-
private List<String> list;
-
-
public Producer(List<String> list) {
-
this.list = list;
-
}
-
-
-
public void run() {
-
//生产
-
while (true) {
-
synchronized (list) {
-
//如果仓库里有东西,则停止生产。生产者线程等待并释放list集合的锁
-
if (list.size() > 0) {
-
try {
-
list.wait();
-
} catch (InterruptedException e) {
-
e.printStackTrace();
-
}
-
}
-
list.add("商品");
-
System.out.println(Thread.currentThread().getName() + " 生产 " + list.get(0));
-
list.notify(); //唤醒消费者
-
}
-
}
-
}
-
}
-
:
7.3、实现奇偶数的交替输出
-
package Thread;
-
-
/**
-
* 使用生产者和消费者模式实现两个线程交替输出:一个线程负责输出奇数,另一个线程负责输出偶数
-
*/
-
public class Number {
-
public static void main(String[] args) throws InterruptedException {
-
Num num = new Num(0);
-
Thread thread1 = new Thread(new Odd(num), "Odd");
-
Thread thread2 = new Thread(new Event(num), "Event");
-
thread1.start();
-
thread2.start();
-
}
-
}
-
-
class Odd implements Runnable {
-
Num num;
-
-
public Odd(Num num) {
-
this.num = num;
-
}
-
-
-
public void run() {
-
while (true) {
-
synchronized (num) {
-
try {
-
Thread.sleep(1000);
-
} catch (InterruptedException e) {
-
e.printStackTrace();
-
}
-
if (num.getI() % 2 == 0) {
-
try {
-
num.wait();
-
} catch (InterruptedException e) {
-
e.printStackTrace();
-
}
-
}
-
System.out.println(Thread.currentThread().getName() + "--->" + num.printNum());
-
num.notifyAll();
-
}
-
}
-
}
-
}
-
-
class Event implements Runnable {
-
Num num;
-
-
public Event(Num num) {
-
this.num = num;
-
}
-
-
-
public void run() {
-
while (true) {
-
synchronized (num) {
-
try {
-
Thread.sleep(1000);
-
} catch (InterruptedException e) {
-
e.printStackTrace();
-
}
-
if (num.getI() % 2 != 0) {
-
try {
-
num.wait();
-
} catch (InterruptedException e) {
-
e.printStackTrace();
-
}
-
}
-
System.out.println(Thread.currentThread().getName() + "--->" + num.printNum());
-
num.notifyAll();
-
}
-
}
-
}
-
}
-
-
class Num {
-
private int i = 0;
-
-
public Num(int i) {
-
this.i = i;
-
}
-
-
public int getI() {
-
return i;
-
}
-
-
public void setI(int i) {
-
this.i = i;
-
}
-
-
int printNum() {
-
return i++;
-
}
-