在java中要实现多线程之间的同步操作主要有如下两种方式:synchronized关键字和对象的wait()和notify()/notifyAll();
首先来看一下synchronized关键的字的使用
作用范围:
- 给指定对象加锁,在进入同步代码块前需要先获得指定对象的锁
package com.ziwu.learn.thread;
public class TestLock implements Runnable{
private Object object = new Object();
private static int i=0;
@Override
public void run() {
synchronized (object){
increments();
}
}
private void increments() {
for(int j = 0;j < 1000000;j++){
i++;
}
}
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(new TestLock());
Thread t2 = new Thread(new TestLock());
t1.start();t2.start();
t1.join();
t2.join();
System.out.println(i);
}
}
因为object属于当前实例的,所以每个线程都有各自的锁,所有存在线程安全问题,结果小于2000万。
2.作用于实例方法,相当于对当前实例加锁,在进入同步代码块前需要先获得当前实例对象的锁
package com.ziwu.learn.thread;
public class TestLock implements Runnable{
private static int i=0;
@Override
public void run() {
increments();
}
private synchronized void increments() {
for(int j = 0;j < 1000000;j++){
i++;
}
}
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(new TestLock());
Thread t2 = new Thread(new TestLock());
t1.start();t2.start();
t1.join();
t2.join();
System.out.println(i);
}
}
synchronized用于实例方法上,相当于给当前实例加锁,所以两个线程拥有不同的锁,故而不是线程安全的。
3.作用于静态方法,相当于给当前类加锁,进入同步代码前要获得当前类的锁
package com.ziwu.learn.thread;
public class TestLock implements Runnable{
private static int i=0;
@Override
public void run() {
increments();
}
private static synchronized void increments() {
for(int j = 0;j < 1000000;j++){
i++;
}
}
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(new TestLock());
Thread t2 = new Thread(new TestLock());
t1.start();t2.start();
t1.join();
t2.join();
System.out.println(i);
}
}
这段代码的输出结果是正常的,因为静态方法是属于类的,所以相当于两个线程公用一把锁,所以是线程安全的。
看到这里如何将1.1和1.2的方法变成线程安全的那么就很容易了,核心就是让当前两个线程采用一个锁或者将方法声明为static的使用类锁。
package com.ziwu.learn.thread;
public class TestLock implements Runnable{
private static int i=0;
@Override
public void run() {
increments();
}
private synchronized void increments() {
for(int j = 0;j < 1000000;j++){
i++;
}
}
public static void main(String[] args) throws InterruptedException {
TestLock test = new TestLock();
Thread t1 = new Thread(test);
Thread t2 = new Thread(test);
t1.start();t2.start();
t1.join();
t2.join();
System.out.println(i);
}
}
后面来看一个使用Object.wait(),和Object.notify()让线程协同执行,相比加锁,Object的wait和notify使得个线程可以有序调度。
这两个方法都会抛出 IllegalMonitorStateException异常,需要注意的是wait方法会释放监视器锁,大致语义是是在调用方法的时候没有获取到监视器锁就会异常,简而言之就是该方法得配合synchronized使用,
package com.ziwu.learn.thread;
public class TestWait {
private static final Object object = new Object();
static class T1 implements Runnable{
@Override
public void run() {
synchronized (object){
System.out.println("t1 is start");
try {
System.out.println("t1 is start wait");
object.wait();
System.out.println("t1 is end wait");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
static class T2 implements Runnable{
@Override
public void run() {
synchronized (object){
System.out.println("t2 is start");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("t2 is start notify");
object.notify();
try {
Thread.sleep(5000);
System.out.println("t2 is end notify");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
Thread t1 = new Thread(new T1());
Thread t2 = new Thread(new T2());
t1.start();
t2.start();
}
}
该段代码的输出:
t1 is start
t1 is start wait
t2 is start
t2 is start notify
t2 is end notify
t1 is end wait‘
可以看到t2在唤醒线程t1后,t1并没有立刻执行因为t2还没有执行完成,故而监视器锁还没有释放,t1此时是拿不到锁的。