线程
- java并不能调用硬件开启线程,源码中
private native void start0();
使用native
修饰是一个本地方法 - java有两个线程main和GC
- 线程的6个状态
public static enum State {
// 新生
NEW,
// 运行
RUNNABLE,
// 阻塞
BLOCKED,
// 等待
WAITING,
// 超时等待
TIMED_WAITING,
// 终止
TERMINATED;
private State() {
}
}
wait和sleep区别
wait
来自Object
类,sleep
来自Thread
类wait
会释放锁,sleep
不会释放锁wait
需要在同步代码块中使用
synchronized和Lock区别
synchronized | Lock |
---|---|
是JAVA关键字 | 是一个类 |
可以自动释放锁 | 需要手动解锁 |
不能判断锁的状态 | 可以判断锁的状态 |
如果获取锁的线程阻塞,其它线程会一直等待 | 如果获取锁的线程阻塞,可以尝试获取锁 |
可重入,不可中断,非公平 | 可重入,可判断,(非)公平 |
适合少量代码 | 适合大量代码 |
Synchronized锁:
调用锁的对象一样则调用的是同一把锁,对象不一样则调用的锁也不同
如果静态方法加锁,那么调用锁的就是该方法的类模板,所以使用该类实例化出来的对象使用该静态方法调用的都是同一把锁
- 普通同步方法,锁是当前实例对象
- 静态同步方法,锁是当前类的class对象
- 同步方法块,锁是括号里面的对象
Synchronized:synchronized
可以保证方法或者代码块在运行时,同一时刻只有一个方法可以进入到临界区,同时它还可以保证共享变量的内存可见性
生产者消费者问题
使用Synchronized
三步骤:判断是否等待,执行业务操作,通知。如:
// 生产者消费者问题
public class PCTest {
public static void main(String[] args) {
Data data = new Data();
// 创建两个线程一个执行加一个执行减
new Thread(() -> {
try {
for (int i = 0; i < 10; i++) {
data.increase();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "A").start();
new Thread(() -> {
try {
for (int i = 0; i < 10; i++) {
data.decrease();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "B").start();
}
}
class Data {
private int number = 0;
// number +1
public synchronized void increase() throws InterruptedException {
// 如果不number不等0就等待其它线程完成减1操作
if (number != 0) {
this.wait();
}
// number等0就加1
number++;
System.out.println(Thread.currentThread().getName() + "->" + number);
// 通知其它等待线程已完成操作
this.notifyAll();
}
// number -1
public synchronized void decrease() throws InterruptedException {
if (number == 0) {
this.wait();
}
number--;
System.out.println(Thread.currentThread().getName() + "->" + number);
this.notifyAll();
}
}
输出的结果就是1,0交替出现
如果在该例子的基础上再添加一个加一和减一两个线程则会出现,虚假唤醒的问题,结果如下:
造成这种问题是因为我们使用了if
只进行了一次判断,如果两个线程执行的都是加操作并且都进入了等待的状态,等到被唤醒时并不会再次判断而会直接执行下面的number++
操作。所以解决的办法是将等待放进while
循环之中
class Data {
private int number = 0;
public synchronized void increase() throws InterruptedException {
// 使用while循环进行判断
while (number != 0) {
this.wait();
}
number++;
System.out.println(Thread.currentThread().getName() + "->" + number);
this.notifyAll();
}
// number -1
public synchronized void decrease() throws InterruptedException {
while (number == 0) {
this.wait();
}
number--;
System.out.println(Thread.currentThread().getName() + "->" + number);
this.notifyAll();
}
}
使用Lock
// 生产者消费者问题 Lock
public class PCTest1 {
public static void main(String[] args) {
Data1 data = new Data1();
// 创建两个线程一个执行加一个执行减
new Thread(() -> {
for (int i = 0; i < 10; i++) data.increase();
}, "A").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) data.decrease();
}, "B").start();
}
}
class Data1 {
private int number = 0;
// 获取可重入锁
private Lock lock = new ReentrantLock();
// 获取condition
private Condition condition = lock.newCondition();
public void increase() {
// 加锁
lock.lock();
try {
// 使用while循环进行判断
while (number != 0) {
//等待
condition.await();
}
number++;
System.out.println(Thread.currentThread().getName() + "->" + number);
// 唤醒
condition.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
// 解锁
lock.unlock();
}
}
// number -1
public synchronized void decrease() {
lock.lock();
try {
while (number == 0) {
condition.await();
}
number--;
System.out.println(Thread.currentThread().getName() + "->" + number);
condition.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
另外使用Lock
中的Condition
可以指定唤醒哪一个线程,如:
public class SpecifyWakeUp {
public static void main(String[] args) {
Say say = new Say();
new Thread(()->{ for (int i = 0; i < 10; i++) say.printA(); }, "A").start();
new Thread(()->{ for (int i = 0; i < 10; i++) say.printB(); }, "B").start();
new Thread(()->{ for (int i = 0; i < 10; i++) say.printC(); }, "C").start();
}
}
class Say{
private int flag = 1;
private Lock lock = new ReentrantLock();
private Condition conditionA = lock.newCondition();
private Condition conditionB = lock.newCondition();
private Condition conditionC = lock.newCondition();
// 这是A方法
public void printA(){
// 加锁
lock.lock();
try {
while (flag != 1){
// A线程等待
conditionA.await();
}
flag = 2;
System.out.println(Thread.currentThread().getName() + ":AAAAAA");
// 唤醒B线程
conditionB.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
// 解锁
lock.unlock();
}
}
// 这是B方法
public void printB(){
lock.lock();
try {
while (flag != 2){
conditionB.await();
}
flag = 3;
System.out.println(Thread.currentThread().getName() + ":BBBBBB");
conditionC.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
// 这是C方法
public void printC(){
lock.lock();
try {
while (flag != 3){
conditionC.await();
}
flag = 1;
System.out.println(Thread.currentThread().getName() + ":CCCCCC");
conditionA.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
读写锁
ReadWriteLock,用法与Lock相似
- 读锁:允许多个线程进入
- 写锁:只允许一个线程进入