线程同步:线程同步是为了解决线程安全问题。
有三种方式完成同步操作:
- 同步代码块。
- 同步方法。
- 锁机制(Lock锁)。
问题引入:
窗口1线程进入操作的时候,窗口2和窗口3线程只能在外等着,窗口1操作结束,窗口1和窗口2和窗口3才有机会进入代码去执行。也就是说在某个线程修改共享资源的时候,其他线程不能修改该资源,等待修改完毕同步之后,才能去抢夺CPU资源,完成对应的操作,保证了数据的同步性,解决了线程不安全的现象。 |
方式一:同步代码块
synchronized关键字可以用于方法中的某个区块中,表示只对这个区块的资源实行互斥访问。
格式:
synchronized(对象锁){ 需要同步操作的代码 } 语法:synchronized(对象锁){ 代码块} 对象锁: 任何一个对象都是可以作为对象锁,包括类 * 1.this who调用当前方法,who就是对象锁 搭配成员方法进行使用 * 通过同一个s1来调用的(同一把锁),出现 有序等待 现象 * 通过不同的s1 s2来调用的(不同的锁),出现 争抢 现象 * * 2.类.class 类锁 ,无论创建多少个对象,类锁都是同一把 搭配静态方法进行使用 * 通过同一个s1来调用的(同一把锁),出现 有序等待 现象 * 通过不同的s1 s2来调用的(不同的锁),出现 有序等待 现象 * * 3.字符串常量 例如“A” "B" 在字符串常量池中分配内存 String a = "A" ; String b = "A" a == b 结果是true * 通过同一个s1来调用的(同一把锁),出现 有序等待 现象 * 通过不同的s1 s2来调用的(不同的锁),出现 有序等待 现象 * * 4.byte[] lock = new byte[0] 开销最小的锁 一般当作属性使用 作用和this是一致的 * 通过同一个s1来调用的(同一把锁),出现 有序等待 现象 * 通过不同的s1 s2来调用的(不同的锁),出现 争抢 现象 |
public class SyncDemo1 {
private byte[] lock = new byte[0];//开销最小的锁
//成员方法
public void test1(){
//同步代码块 ==> 给一段代码加锁
// synchronized (this) { //this代表的是 s1
//synchronized (SyncDemo1.class){ //类锁 无论创建多少个SyncDemo1的对象 类锁都是同一把
//synchronized ("A"){ //字符串变量
synchronized (lock){ //开销最小的锁
for (int i = 0; i < 10; i++) {
try {
TimeUnit.SECONDS.sleep(1);//线程沉睡不会释放对象锁
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ",i=" + i);
}
}
}
public static void main(String[] args) {
//同一个锁
SyncDemo1 s1 = new SyncDemo1();
SyncDemo1 s2 = new SyncDemo1();
//开启多线程
new Thread(()->s1.test1(),"线程1").start();
new Thread(()->s2.test1(),"线程2").start();
}
}
方式二:同步方法
使用synchronized修饰的方法,就叫做同步方法,保证A线程执行该方法的时候,其他线程只能在方法外等着。
格式:
public synchronized void method(){ 可能会产生线程安全问题的代码 } 语法:public synchronized 返回值类型 方法名(){} * 1.成员方法 * 类似于this this作为对象锁 who在调用方法,who就是对象锁 * 通过同一个s1来调用的(同一把锁),出现 有序等待 现象 * 通过不同的s1 s2来调用的(不同的锁),出现 争抢 现象 * * 2.静态方法 * 类似于类锁 类作为对象锁 无论创建多少个对象,类锁都是同一把 * 通过同一个s1来调用的(同一把锁),出现 有序等待 现象 * 通过不同的s1 s2来调用的(不同的锁),出现 有序等待 现象 |
public class SyncDemo2 {
//成员方法
public synchronized void test1(){
for (int i = 0; i <10 ; i++) {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+",i="+i);
}
}
//静态方法
public static synchronized void test2(){
for (int i = 0; i <10 ; i++) {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+",i="+i);
}
}
public static void main(String[] args) {
SyncDemo2 s1 = new SyncDemo2();
SyncDemo2 s2 = new SyncDemo2();
// new Thread(()->s1.test1(),"线程1").start();
// new Thread(()->s2.test1(),"线程2").start();
new Thread(()->s1.test2(),"线程1").start();
new Thread(()->s2.test2(),"线程2").start();
}
}
方式三:Lock锁
java.util.concurrent.locks.Lock机制提供了比synchronized代码块和synchronized方法更广泛的锁定操作,同步代码块/同步方法具有的功能Lock都有,除此之外更强大
Lock锁也称同步锁,加锁与释放锁方法化了,如下:
- public void lock():加同步锁。
- public void unlock():释放同步锁。
* 方法:lock()获取锁 unlock()释放锁 * * 1.作为成员属性 和对象有关 * private final Lock lock = new ReentrantLock(); * 通过同一个s1来调用的(同一把锁),出现 有序等待 现象 * * 通过不同的s1 s2来调用的(不同的锁),出现 争抢 现象 * * 2.作为静态属性 和类有关 * private final static Lock lock = new ReentrantLock(); * 通过同一个s1来调用的(同一把锁),出现 有序等待 现象 * * 通过不同的s1 s2来调用的(不同的锁),出现 有序等待 现象 * * 注意:必须在finally中释放锁,确保无论是否有异常产生都可以释放锁, * 从而避免出现异常没有释放锁导致其他线程始终获取不到锁 --死锁现象 |
例:
public class LockDemo {
//成员属性 和对象有关
//private final Lock lock = new ReentrantLock();
//静态属性 和类有关
private final static Lock lock = new ReentrantLock();
public void test1(){
try{
//获取锁
lock.lock();
System.out.println(Thread.currentThread().getName()+"获取到锁");
for (int i = 0; i < 10 ; i++) {
System.out.println(Thread.currentThread().getName()+",i="+i);
Thread.sleep(1000);
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
//无论是否有异常产生 都要释放锁
lock.unlock();
System.out.println(Thread.currentThread().getName()+"释放了锁");
}
}
public void test2(){
try{
//获取锁
lock.lock();
System.out.println(Thread.currentThread().getName()+"获取到锁");
for (int i = 0; i < 10 ; i++) {
System.out.println(Thread.currentThread().getName()+",i="+i);
Thread.sleep(1000);
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
//无论是否有异常产生 都要释放锁
lock.unlock();
System.out.println(Thread.currentThread().getName()+"释放了锁");
}
}
public static void main(String[] args) {
LockDemo lock1 = new LockDemo();
LockDemo lock2 = new LockDemo();
new Thread(()->lock1.test1(),"线程1").start();
new Thread(()->lock2.test2(),"线程2").start();
}
}