锁的功能
在java并发中,加锁可以保证线程的安全性,解决竞态条件问题。
竞态条件
某个计算的正确性取决于多个线程的交替执行时序。
常见类型:1. 先检查后执行(包括延时初始化)
2. 读取 – 修改 – 写入 操作
几种synchronized关键字的使用方式
1. 加在函数上
public class Test {
private int count;
public static void main(String[] args) {
new Test().run();
}
private void run() {
for (int i = 0; i < 1000; i++)
new Thread() {
@Override
public void run() {
addCount();
}
}.start();
System.out.println("count = " + count);
}
private synchronized void addCount() {
count++;
}
}
这个锁加在了addCount函数前面,锁定的是Test实例出的对象
如果使用AtomicInteger,就不需要加锁操作
2. synchronized 代码块
1. synchronized(this){} 锁定对象
2. synchronized(A.class){} 锁定Test.class实例出的所有对象
public class Test2 {
private int count;
public static void main(String[] args) {
new Test2().run();
}
private void run() {
for (int i = 0; i < 3; i++)
new Thread() {
@Override
public void run() {
new A().print(getName());
}
}.start();
}
}
class A {
public void print(String threadName) {
synchronized (A.class) {
for (int i = 0; i < 5; i++)
System.out.println(threadName + " " + i);
}
}
}
虽然在Test2.run中,每个线程持有的是不同的对象,但是由于锁定的是class,输出是
Thread-0 0 Thread-0 1 Thread-0 2 Thread-0 3 Thread-0 4 Thread-2 0 Thread-2 1 Thread-2 2 Thread-2 3 Thread-2 4 Thread-1 0 Thread-1 1 Thread-1 2 Thread-1 3 Thread-1 4
3. synchronized(某个变量){}
在这种情况下,代码块会调用变量内部的锁,因此这个变量不能是原始类型(int,double等)
public class Test2 {
private A a;
public static void main(String[] args) {
new Test2().run();
}
private void run() {
a = new A();
for (int i = 0; i < 1000; i++)
new Thread() {
@Override
public void run() {
addCount();
System.out.println(a.get());
}
}.start();
}
private void addCount() {
synchronized(a) { // 使用的是a的锁
a.addCount();
}
}
}
class A {
private int count;
public void addCount() {
count++;
}
public int get() {
return count;
}
}
死锁
当一个线程获取到锁后始终不退出时,其他的线程就只能一直等待
public class Test {
private A a = new A();
public static void main(String[] args) {
new Test().run();
}
private void run() {
new Thread() {
@Override
public void run() {
System.out.println("Thread A run");
a.printA();
}
}.start();
new Thread() {
@Override
public void run() {
System.out.println("Thread B sleep");
try {
sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Thread B try printB");
a.printB();
}
}.start();
}
}
class A {
public synchronized void printA() {
while(true) {
// 这里死循环,Thread A 获取锁后不退出
}
}
public synchronized void printB() {
System.out.println("A, printB");
}
}
输出为:
Thread A run
Thread B sleep
Thread B try printB
这里给一个对比
public class Test {
private A a = new A();
public static void main(String[] args) {
new Test().run();
}
private void run() {
new Thread() {
@Override
public void run() {
System.out.println("Thread A run");
while(true) {
a.printA();
}
}
}.start();
new Thread() {
@Override
public void run() {
System.out.println("Thread B sleep");
try {
sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Thread B try printB");
a.printB();
}
}.start();
}
}
class A {
public synchronized void printA() {
}
public synchronized void printB() {
System.out.println("A, printB");
}
}
修改的部分为Thread A run函数的实现和A class中的printA函数,即将printA函数中的死循环移到了Thread A run函数中
输出为:
Thread A run
Thread B sleep
Thread B try printB
A, printB