背景
地点:长沙飞思
时间:2023/10/23~30
内容:synchronized关键字与wait()
前言
在并发编程中,在面对数以万计的请求时,如何控制并发,保证线程的安全显得尤为重要,于是锁理所当然的出现在这里。synchronized是Java中的关键字,synchronized可以保证方法或者代码块在运行时,同一时刻只有一个方法可以进入到临界区,同时它还可以保证共享变量的内存可见性,Java中每一个对象都可以作为锁,这是synchronized实现同步的基础。
synchronized
既然要聊synchronized关键字,就不得不说一下线程的生命周期
线程创建调用start()后,进入就绪状态(cpu可调度但不一定调度),cpu调度后进入运行状态;当调用synchronized关键字拿不到锁,就会进入阻塞状态,又使用了wait()且未满足条件就会进入等待状态。
修饰对象
1. 修饰一个代码块,被修饰的代码块称为同步语句块,其作用的范围是大括号{}括起来的代码,作用的对象是调用这个代码块的对象;
package com.wbg;
import java.util.ArrayList;
import java.util.List;
public class MySynchronized {
public static void main(String[] args) {
System.out.println("使用关键字synchronized");
SyncThread syncThread = new SyncThread();
Thread thread1 = new Thread(syncThread, "SyncThread1");
Thread thread2 = new Thread(syncThread, "SyncThread2");
thread1.start();
thread2.start();
}
}
package com.zs;
/**
* 修饰一个代码块
*/
public class SyncThread implements Runnable{
private static int count;
public SyncThread() {
count = 0;
}
@Override
public void run() {
synchronized (this){
for (int i = 0; i < 5; i++) {
try {
System.out.println("线程名:"+Thread.currentThread().getName() + ":" + (count++));
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public int getCount() {
return count;
}
}
2. 修饰一个方法,被修饰的方法称为同步方法,其作用的范围是整个方法,作用的对象是调用这个方法的对象;
/**
* 修饰一个方法
*/
@Override
public synchronized void run() {
{
for (int i = 0; i < 5; i++) {
try {
System.out.println("线程名:"+Thread.currentThread().getName() + ":" + (count++));
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
3. 修改一个静态的方法,其作用的范围是整个静态方法,作用的对象是这个类的所有对象;
/**
* 修饰一个静态方法
*/
public synchronized static void method() {
for (int i = 0; i < 5; i ++) {
try {
System.out.println(Thread.currentThread().getName() + ":" + (count++));
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public synchronized void run() {
method();
}
4. 修改一个类,其作用的范围是synchronized后面括号括起来的部分,作用主的对象是这个类的所有对象。
/**
* 修饰一个类
*/
class ClassName {
public void method() {
synchronized(ClassName.class) {
}
}
}
public static void method() {
synchronized(SyncThread.class) {
for (int i = 0; i < 5; i ++) {
try {
System.out.println(Thread.currentThread().getName() + ":" + (count++));
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public synchronized void run() {
method();
}
wait
是Object类中的方法,一定要配合synchronized关键字,也要与while()一起使用,一般出现于需要条件线程才执行的场合。而唤醒wait()则需要用到notify(),同时notify()也需要配合synchronized关键字。
package com.zs;
import java.util.concurrent.TimeUnit;
public class App {
boolean isGf = false;
Thread zs;
Object lock = new Object();
public static void main(String[] args) throws InterruptedException {
new App().m();
}
public void m() throws InterruptedException {
/**
* 条件线程
*/
zs = new Thread(() -> {
try {
zswork();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}, "zs");
zs.start();
TimeUnit.MILLISECONDS.sleep(10);
/**
* 一般无条件线程
*/
for (int i = 1; i < 11; i++) {
Thread thread = new Thread(() -> {
work();
}, "t" + i);
thread.start();
}
TimeUnit.SECONDS.sleep(2);
/**
* 条件给予线程
*/
Thread boss = new Thread(() -> {
setCondition();
}, "boss");
boss.start();
}
public void work() {
synchronized (lock) {
System.out.println("working:" + Thread.currentThread().getName());
}
}
public void zswork() throws InterruptedException {
synchronized (lock) {
while (!isGf) {
System.out.println("条件不满足:" + isGf);
lock.wait();
}
System.out.println("zs is working:" + Thread.currentThread().getName());
}
}
//满足条件
public void setCondition() {
synchronized (lock) {
isGf = true;
System.out.println("给予条件:" + Thread.currentThread().getName());
/**
* 满足条件时
*/
// lock.notify();//随机唤醒一个线程
lock.notifyAll();//唤醒全部线程
}
}
}
线程的等待与阻塞
线程大体上存在两种阻塞方式,第一种方式就是:调用synchronized关键字拿不到锁而阻塞的线程,一般这种线程就会放到等待区中的entrySet集合当中,在这个集合中的线程状态被称为blocked状态,也就是生命周期中的阻塞状态;另一种方式就是:调用了wait()方法而阻塞的线程,此时这种线程会被放到waitSet集合中,也就是等待状态(waiting)。
当条件满足时,就会出现“线程转移”,将处于waiting状态的线程,转移到blocked状态,然后再一次去竞争锁。
ReentrantLock
在多线程高并发的情况下(或者公平锁),存在一些条件时,synchronized它的wait方法只有一个条件队列,所有不管因为条件而wait的线程都会把它们放到一起,那么在将来叫醒的时候,可能会存在虚假唤醒,无法满足精确叫醒满足条件的某一类线程。
而reentrantlock可以存在多个wait,得显示的创建出来
//创建一个wait队列
Condition mCondition = lock.newCondition();
//调用它的wait方法
mCondition.await();
//唤醒当前队列里面所有线程
mdition.signalAll;
总结
温故而知新