1.synchronized关键字只是起到了多个线程“串行”执行临界区中代码的作用,但是哪个线程先执行,哪个线程后执行依然无法确定。
2.如果将一个时间线程先获取时间,然后有另外一个显示线程输出时间,代码如下:
package keeper2;
import java.util.Date;
public class Watch {
private static String time;
static class Display extends Thread{ //显示线程
@Override
public void run() {
System.out.println(time);
}
}
static class Time extends Thread{ //时间线程
@Override
public void run() {
time = new Date().toString();
}
}
public static void main(String[] args) {
Time time = new Time();
time.start();
new Display().start();
}
}
如果同时开启时间线程和显示线程,那么是竞争的关系,时间线程可能抢不过显示线程,所以没有给time赋值,就会输出null。
怎么解决线程之间执行的先后顺序呢?
①在显示时间线程输出语句之前添加sleep(100);代码如下:
package keeper2;
import java.util.Date;
public class Watch {
private static String time;
static class Display extends Thread{
@Override
public void run() {
if(time==null){
try {
sleep(100); //处于阻塞状态,等待显示线程执行
} catch (InterruptedException e1) {
e1.printStackTrace();
}
}
System.out.println(time);
}
}
static class Time extends Thread{
@Override
public void run() {
time = new Date().toString();
}
}
public static void main(String[] args) {
Time time = new Time();
time.start();
new Display().start();
}
}
②用timeThread.join();此时显示线程阻塞,等待时间线线程运行,时间线程运行完了再继续运行显示线程
package keeper2;
import java.util.Date;
public class Watch {
private static String time;
static class Display extends Thread{
Time timeThread;
Display(Time time){
this.timeThread = time;
}
@Override
public void run() {
if(time == null) {
try {
timeThread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(time);
}
}
static class Time extends Thread{
@Override
public void run() {
time = new Date().toString();
}
}
public static void main(String[] args) {
Time timeThread = new Time();
timeThread.start();
new Display(timeThread).start();
}
}
③Object类中的wait()、notify()和notifyAll()三个方法解决了线程间的协作问题,通过这三个方法的“合理”使用可以确定多线程中线程的先后执行顺序:
(1)wait():对象锁调用了wait()方法会使当前持有该对象锁的线程处于线程等待状态同时该线程释放对对象锁的控制权,让对象锁回到原来的状态。直到在其他线程中该对象锁调用notify()方法或notifyAll()方法时等待此对象锁的线程才会被唤醒。
(2)notify():对象锁调用notify()方法就会唤醒在此对象锁上等待的单个线程。
(3)notifyAll():对象锁调用notifyAll()方法就会唤醒在此对象锁上等待的所有线程;调用notifyAll()方法并不会立即激活某个等待线程,它只能撤销等待线程的中断状态,这样它们就能够在当前线程退出同步方法或同步代码块法后与其它线程展开竞争,以争取获得资源对象来执行,唤醒的所有对象锁最终都会执行,时间先后而已。
总结:谁调用了wait方法,谁就必须调用notify或notifyAll方法,并且“谁”是对象锁。
package keeper2;
import java.util.Date;
public class Watch {
private static String time;
static class Display extends Thread{
Time timeThread;
Object object;
Display(Time time,Object object){
this.timeThread = time;
this.object = object;
}
@Override
public void run() {
if(time == null) {
synchronized (object) {
try {
object.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
System.out.println(time);
}
}
static class Time extends Thread{
Object object;
Time(Object object) {
this.object = object;
}
@Override
public void run() {
time = new Date().toString();
synchronized (object) {
object.notify();
}
}
}
public static void main(String[] args) {
Object object = new Object();
Time time = new Time(object);
time.start();
new Display(time, object).start();
}
}
3.使用Object类中的wait()、notify()和notifyAll()三个方法需要注意以下几点:
①wait()方法需要和notify()或notifyAll()方法中的一个配对使用,且wait方法与notify()或notifyAll()方法配对使用时不能在同一个线程中。如过在同一个线程中,run方法一样,欸此执行的内容一样,不会有notify()和notifyAll(),所以不能在同一个线程中
②wait()方法、notify()方法和notifyAll()方法必须在同步方法或者同步代码块中使用,否则出现IllegalMonitorStateException 异常。就是说必须用:包裹
synchronized (object) {
}
4.sleep()与wait()方法区别
①sleep()方法被调用后当前线程进入阻塞状态,但是当前线程仍然持有对象锁,在当前线程sleep期间,其它线程无法执行sleep方法所在临界区中的代码。
②对象锁调用了wait()方法会使当前持有该对象锁的线程处于线程等待状态同时该线程释放对对象锁的控制权,在当前线程处于线程等待期间,其它线程可以执行wait方法所在临界区中的代码。临界区就是被synchronized 包裹的大括号里的内容
5.时间线程也可以充当对象锁
import java.text.*;
import java.util.Date;
public class Test {
public static void main(String[] args) {
TimeThread timeThread = new TimeThread ();
timeThread.start();
synchronized (timeThread) {
try {
timeThread.wait();//为什么时间线程没有进入阻塞状态呢?——timeThread变量所代表的对象充当对象锁,由于该代码在main方法中,也就是说将来由主线程执行临界区中的代码,也就是说主线程是持有对象锁的线程,主线程执行完“timeThread.wait()”代码后进入阻塞状态,是主线程进入阻塞状态而非时间线程,这也是main方法中“System.out.println("main方法");”代码不执行的原因。
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("main方法");//为什么这行代码不执行?——由于主线程进入了阻塞状态,所以该行代码不执行
}
}
class TimeThread extends Thread{
@Override
public void run() {
DateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");
while (true) {
String currentTime = dateFormat.format(new Date());
System.out.println(currentTime);
try {
sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}