线程通信的例子:使用两个线程打印1-100 线程1,线程2 交替打印 * * 涉及到的三个方法: * wait():一旦执行此方法,当前线程就进入阻塞状态,并释放同步监视器 * notify():一旦执行此方法,就会唤醒被wait的线程。如果有多个线程被wait,就唤醒优先级高的那个 *notifyAll():一旦执行此方法,就会唤醒所有被wait的线程 * *说明: * 1.wait()、notify()、notifyAll()三个方法必须使用在同步代码块或同步方法中 * 2.wait()、notify()、notifyAll()三个方法的调用必须是同步代码块或同步方法中的同步监视器, * 否则会出现IllegalMonitorStateException异常 *3.wait()、notify()、notifyAll()三个方法是定义在java.lang.Object类中(不是Thread中,很容易错认为) * * * 面试题:sleep()和wait()的异同? * 1.相同点:一旦执行方法,都可以使得当前的线程进入阻塞状态 * 2.不同点:1)两个方法声明的位置不同:Thread类中可以声明sleep(),Object类中声明wait() * 2)调用的要求不同:sleep()可以在任何需要的场景下调用,wait()必须在同步代码块或同步方法下调用 * 3)关于是否释放同步监视器:如果两个方法都使用在同步代码块或同步方法中,sleep()不会释放锁,wait()会释放锁
class Number implements Runnable{
private int number = 1;
private Object obj = new Object();
@Override
public void run() {
while (true){
// synchronized (this) {
synchronized (obj){
obj.notify();//this可以省略,但如果监视器是obj,前面则不能是this,详看说明2
// notify();//this可以省略,但如果监视器是obj,前面则不能是this,详看说明2
if (number <= 100) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":" + number);
number++;
try {
//使得调用wait()方法的线程进入阻塞状态
obj.wait();
// wait();当this充当监视器时才能是this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
break;
}
}
}
}
}
public class CommunicationTest {
public static void main(String[] args) {
Number number =new Number();
Thread t1 = new Thread(number);
Thread t2 = new Thread(number);
t1.setName("线程1");
t2.setName("线程2");
t1.start();
t2.start();
}
}
练习: 创建两个分线程,其中一个线程遍历100以内的偶数,另一个线程遍历100以内的奇数
public class ThreadExer {
public static void main(String[] args) {
// MyThread1 m1 = new MyThread1();
// MyThread2 m2 = new MyThread2();
//
// m1.start();
// m2.start();
//或者
//创建Thread类的匿名子类的方式
new Thread(){
public void run(){
for(int i = 0 ; i<100; i++){
if(i % 2 == 0){
System.out.println(Thread.currentThread().getName() + ":"+i);
}
}
}
}.start();
new Thread(){
public void run(){
for(int i = 0 ; i<100; i++){
if(i % 2 != 0){
System.out.println(Thread.currentThread().getName() + ":"+i);
}
}
}
}.start();
}
}
class MyThread1 extends Thread{
public void run(){
for(int i = 0 ; i<100; i++){
if(i % 2 == 0){
System.out.println(Thread.currentThread().getName() + ":"+i);
}
}
}
}
class MyThread2 extends Thread{
public void run(){
for(int i = 0 ; i<100; i++){
if(i % 2 != 0){
System.out.println(Thread.currentThread().getName() + ":"+i);
}
}
}
}
设立三个窗口卖100张票 (存在线程安全问题) *用同步代码块解决继承Thread类出现的线程安全问题 *说明:在继承Thread类创建多线程的方式中,慎用this充当同步监视器,考虑使用当前类充当同步监视器
class Window1 extends Thread {
private static int ticket = 100;
private static Object obj = new Object();
@Override
public void run() {
// synchronized (obj) { //正确的
//错误的 synchronized (this){ //this:唯一的Window1的对象,这里new了三次,这里的this代表w1,w2,w3
synchronized (Window1.class){
while (true) {
if (ticket > 0) {
System.out.println(getName() + ":卖票,票号为:" + ticket);
ticket--;
} else {
break;
}
}
}
}
}
public class WindowTest3 {
public static void main(String[] args) {
Window1 w1 = new Window1();
Window1 w2 = new Window1();
Window1 w3 = new Window1();
w1.start();
w2.start();
w3.start();
}
}