什么是线程锁?
在并发编程中,经常遇到多个线程访问同一个 共享资源 ,这时候作为开发者必须考虑如何维护数据一致性,在java中synchronized关键字被常用于维护数据一致性。synchronized机制是给共享资源上锁,只有拿到锁的线程才可以访问共享资源,这样就可以强制使得对共享资源的访问都是顺序的。
引入多线程后,为解决线程安全问题而引入锁的概念,java中常用的锁有synchronized和lock两种。
java中的锁
一般在java中所说的锁就是指的内置锁,每个java对象都可以作为一个实现同步的锁,虽然说在java中一切皆对象, 但是锁必须是引用类型的,基本数据类型则不可以 。每一个引用类型的对象都可以隐式的扮演一个用于同步的锁的角色,执行线程进入synchronized块之前会自动获得锁,无论是通过正常语句退出还是执行过程中抛出了异常,线程都会在放弃对synchronized块的控制时自动释放锁。 获得锁的唯一途径就是进入这个内部锁保护的同步块或方法 。
通过synchronized的关键字来实现线程同步锁
synchronized 锁住的是对象
Synchronized 三种应用方式:
1.作用于实例方法(普通方法),当前实例加锁,进入同步代码前要获得当前实例的锁;
例如:
public class SyncTest implements Runnable{
//共享资源变量
static int count = 0;
@Override
public synchronized void run() {
increaseCount();
}
private synchronized void increaseCount() {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + ":" + count++);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) throws InterruptedException {
SyncTest syncTest1 = new SyncTest();
// SyncTest syncTest2 = new SyncTest();
Thread thread1 = new Thread(syncTest1, "thread1");
Thread thread2 = new Thread(syncTest1, "thread2");
thread1.start();
thread2.start();
}
/**
* 执行结果
* thread1:0
* thread1:1
* thread1:2
* thread1:3
* thread1:4
* thread2:5
* thread2:6
* thread2:7
* thread2:8
* thread2:9
*/
}
上面是创建1个对象和两个线程,可以看到Thread1和Thread2 按顺序执行
当创建两个不同的对象时,两个线程同步执行,所以说锁住的是对象。如下:
public class SyncTest implements Runnable{
//共享资源变量
static int count = 0;
@Override
public synchronized void run() {
increaseCount();
}
private synchronized void increaseCount() {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + ":" + count++);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) throws InterruptedException {
SyncTest syncTest1 = new SyncTest();
SyncTest syncTest2 = new SyncTest();
Thread thread1 = new Thread(syncTest1, "thread1");
Thread thread2 = new Thread(syncTest2, "thread2");
thread1.start();
thread2.start();
}
/**
* 执行结果
thread2:1
thread1:0
thread1:2
thread2:3
thread2:4
thread1:4
thread1:5
thread2:5
thread2:6
thread1:7
*/
}
2.作用于静态方法,当前类加锁,进去同步代码前要获得当前类对象的锁;
public class SyncTest implements Runnable {
//共享资源变量
static int count = 0;
@Override
public synchronized void run() {
increaseCount();
}
private sys static void increaseCount() {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + ":" + count++);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) throws InterruptedException {
SyncTest syncTest1 = new SyncTest();
SyncTest syncTest2 = new SyncTest();
Thread thread1 = new Thread(syncTest1, "thread1");
Thread thread2 = new Thread(syncTest2, "thread2");
thread1.start();
thread2.start();
}
/**
* 执行结果
thread1:0
thread1:1
thread1:2
thread1:3
thread1:4
thread2:5
thread2:6
thread2:7
thread2:8
thread2:9
*/
}
synchronized 作用于静态方法时,相当于给该类加锁,如上创建两个对象分开两个线程依旧按顺序执行。
3.作用于代码块,这需要指定加锁的对象,对所给的指定对象加锁,进入同步代码前要获得指定对象的锁。
public class SyncTest implements Runnable {
//共享资源变量
static int count = 0;
private byte[] mBytes = new byte[0];
@Override
public synchronized void run() {
increaseCount();
}
private void increaseCount() {
//假设省略了其他操作的代码。
//……………………
synchronized (this) {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + ":" + count++);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) throws InterruptedException {
SyncTest syncTest1 = new SyncTest();
SyncTest syncTest2 = new SyncTest();
Thread thread1 = new Thread(syncTest1, "thread1");
Thread thread2 = new Thread(syncTest1, "thread2");
thread1.start();
thread2.start();
}
/**
* 执行结果
thread1:0
thread1:1
thread1:2
thread1:3
thread1:4
thread2:5
thread2:6
thread2:7
thread2:8
thread2:9
*/
当只需要部分代码同步时,synchronized 作用于代码块,传入参数为对象,效果和作用于普通方法一致,锁住的是对象。
参考:
https://www.cnblogs.com/JackpotHan/category/1305465.html