同步代码块
使用synchronized
关键字给代码块加锁,synchronized
后的括号内可以填入任意对象,被填入的对象将作为同步监视器;线程在执行同步代码块之前,必须先获得对同步监视器的锁定,当同步代码执行完成后,线程会释放对同步监视器的锁定;如果同步监视器已经被其他线程锁定,则当前线程进入阻塞队列,等待同步监视器的锁定被释放,这种方式可以确保同一时刻只有一个线程访问加锁的代码块()。
如下代码,lock
对象充当同步监视器,当一个线程调用test
方法并进入同步代码块之前,必须先获得lock
对象的锁定,如果已经有其他线程持有了该锁定,当前线程将进入阻塞状态。
1 2 3 4 5 6 7 8 9
| public class Main { private final Object lock = new Object(); public void test() { synchronized (lock) { } } }
|
如果多个线程调用了同一个Main
对象的test
方法,它们将竞争同一个lock
对象的锁定,那么只有一个线程能够获得该对象的锁定,并执行同步代码块中的代码,其他线程将被阻塞,直到获得了锁定的线程释放锁。
但如果多个线程调用不同Main
对象的test
方法,那么每个线程都有不同的lock
对象作为同步监视器,获得不同的同步监视器的锁定,所以test
方法中的同步代码是独立的,不会发生阻塞。
若要使多个线程能够同步调用不同Main
对象的test
方法,可以将lock
设为静态,此时多个线程将竞争同一个静态锁,只有一个线程能够获得静态锁并执行同步代码块中的代码,其他线程将被阻塞,直到获得锁的线程释放锁。
1 2 3 4 5 6 7 8 9 10 11
| public class Main {
private static final Object lock = new Object(); public void test() { synchronized (lock) { } } }
|
同步方法
普通同步方法
普通同步方法,以当前实例对象作为同步监视器
1 2 3 4 5 6 7 8 9 10 11
| public synchronized void test() { }
public void test() { synchronized (this) { } }
|
如下代码执行时,多个线程同时执行syncTest
,操作value
值递减,由于运行中的线程时间片用完时会进入阻塞状态,假设当前线程在即将操作value
值时进入阻塞状态,阻塞期间其他得到时间片的线程继续对value
值操作,当阻塞的线程重新获得时间片并执行时,进入阻塞状态之前所操作过的value
可能已经被其他线程改变,所以当输出value
时会出现相同的值,甚至出现负数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| public class SyncTest implements Runnable {
private int value = 100;
public static void main(String[] args) { SyncTest syncTest = new SyncTest();
Thread thread1 = new Thread(syncTest); Thread thread2 = new Thread(syncTest); Thread thread3 = new Thread(syncTest);
thread1.start(); thread2.start(); thread3.start(); }
@Override public void run() { for (int i = 0; i < 100; i++) { try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
if (value > 0) { System.out.println("value=" + (--value)); } } } }
|
对value
值修改部分加锁
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| @Override public void run() { for (int i = 0; i < 100; i++) { try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
synchronized (this) { if (value > 0) { System.out.println("value=" + (--value)); } } } }
|
或者将修改value
值的部分提取到一个方法,并将方法设为同步方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| @Override public void run() { for (int i = 0; i < 100; i++) { try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
sub(); } }
private synchronized void sub() { if (value > 0) { System.out.println("value=" + (--value)); } }
|
静态同步方法
静态同步方法,以当前类的class
对象作为同步监视器
1 2 3 4 5 6 7 8 9 10 11
| public synchronized static void test() { }
public static void test() { synchronized (Main.class) { } }
|
如下synchronized
的用法并不能起到代码同步的作用,因为synchronized
后的括号中填入了this
,代表以当前对象作为代码块的同步监视器,而三个线程使用了不同的对象,所以synchronized
中的代码是独立运行的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| public class TestThread extends Thread{
private static int value = 100;
public static void main(String[] args) { new TestThread().start(); new TestThread().start(); new TestThread().start(); }
@Override public void run() { super.run(); for (int i = 0; i < 100; i++) { try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
synchronized (this) { if (value > 0) { System.out.println("value=" + (--value)); } } } } }
|
要使代码在多个线程中同步,需要确定同步监视器的唯一,可以用当前类的class
对象作为同步监视器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| @Override public void run() { super.run(); for (int i = 0; i < 100; i++) { try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
synchronized (TestThread.class) { if (value > 0) { System.out.println("value=" + (--value)); } } } }
|
或则创建一个静态对象,作为同步监视器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| private final static Object lock = new Object();
@Override public void run() { super.run(); for (int i = 0; i < 100; i++) { try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
synchronized (lock) { if (value > 0) { System.out.println("value=" + (--value)); } } } }
|
又或者使用静态同步方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| @Override public void run() { super.run(); for (int i = 0; i < 100; i++) { try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
sub(); } }
private synchronized static void sub() { if (value > 0) { System.out.println("value=" + (--value)); } }
|