目录

线程基本概念

1、java实现线程

2、线程的生命周期

3、线程常用的方法

3.1、sleep()

3.2、interrupt方法

3.3、stop方法

4、线程调度

4.1、常见的线程调度模型

4.2、java中提供的线程调度方法

4.3、线程让步

4.4、线程合并

4.5、线程安全

4.5.1、线程同步的实现

4.5.2、java中的线程安全性

4.5.3、synchronized总结

4.5.4、死锁

4.6、守护线程

4.6.1、守护线程的特点

4.6.2、一个简单的守护线程的例子

5、定时任务

5.1、实现一个定时器

6、通过Callable接口实现一个线程

7、Object类中的wait和notify方法

7.1、wait和notify方法介绍

7.2、生产者和消费者模式

7.3、实现奇偶数的交替输出


线程基本概念

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 {
@Override
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 {
@Override
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{
@Override
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{
@Override
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{
@Override
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; //通过引入一个布尔类型的变量,来标记该线程的状态(运行/停止)
@Override
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{
@Override
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{
@Override
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{
@Override
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{ @Override
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{ @Override
public void run() {
for(int i=0;i<3;i++){
System.out.println(Thread.currentThread().getName()+"-->"+i);
}
}
}
class Thread_08_2 implements Runnable{ @Override
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;
}
@Override
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;
} @Override
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;
} @Override
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;
} @Override
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 {
@Override
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 { @Override
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 { @Override
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;
} @Override
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;
} @Override
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;
} @Override
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;
} @Override
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++;
}
}

最新文章

  1. python file operations
  2. redsocks 将socks代理转换成全局代理
  3. PHP中文函数顺序排列一数组且其序数不变
  4. 高频交易[z]
  5. spark之combineByKey
  6. Android Studio, gradle plugin is too old or set ANDROID_DAILY_OVERRIDE
  7. 《锋利的jQuery》心得笔记--Two Sections
  8. Swift静态属性
  9. [置顶] gridview中嵌套gridview(并实现子gridview的数据绑定),页面传值,加密,数据绑定
  10. ViewPager的用法和实现过程
  11. w10 系统升级
  12. 获取Delphi所有类的类信息
  13. 深入浅出-iOS函数式编程的实现 &amp;&amp; 响应式编程概念
  14. ios layer 动画
  15. Django学习报错记录
  16. 洛谷 2634&amp;&amp;BZOJ 2152: 聪聪可可【点分治学习+超详细注释】
  17. Android常见漏洞
  18. iOS 屏幕方向
  19. 【easy】784. Letter Case Permutation
  20. javascript 之 第七章第三节(this关键字)

热门文章

  1. python流程控制下-for、while循环补充
  2. BZOJ2654 tree (wqs二分)
  3. 20220729 - DP训练 #2
  4. 常用RE对照表——敬请期待!!!
  5. <jsp:useBean>动作的使用
  6. 成功解决:snippet设置的开机自启没有效果
  7. 成功解决:Can‘t find Python executable “python“, you can set the PYTHON env variable.
  8. 0025:2011年NOIp普及组真题——瑞士轮题解
  9. 1.pygame快速入门-创建游戏窗口
  10. 2.httprunner-yaml用例结构