synchronized 和 Lock 的区别:
① synchronized是内置的Java关键字,Lock是一个接口
② synchronized无法判断是否获取到锁,Lock可以判断是否获取到锁
③ synchronized会自动释放锁,Lock必须要手动释放锁,否则可能会死锁!
④ synchronized当有两个线程,其中一个得到锁,另一个线程则死等,Lock不一定会死等
⑤ synchronized是可重入锁 不可中断的 非公平锁,Lock是可重入锁 可以判断锁 可设为公平锁
⑥ synchronized适合锁少量的代码同步问题,Lock适合锁大量的同步代码
① 使用synchronized进行线程同步:
public class Demo01 {
public static void main(String[] args) {
// 并发,多线程操作统一资源
Ticket ticket = new Ticket();
new Thread(()->{
// Lambda表达式
for (int i = 0; i < 60; i++) {
ticket.saleTicket();
}
},"张三").start();
new Thread(()->{
for (int i = 0; i < 60; i++) {
ticket.saleTicket();
}
},"李四").start();
new Thread(()->{
for (int i = 0; i < 60; i++) {
ticket.saleTicket();
}
},"王五").start();
}
}
class Ticket{
private int number = 50;
public synchronized void saleTicket(){
// 同步方法
if (number>0){
System.out.println(Thread.currentThread().getName()+"卖出了第"+(number--)+"张票");
}
}
}
结果:
张三卖出了第50张票
张三卖出了第49张票
张三卖出了第48张票
张三卖出了第47张票
......(此处省略)
张三卖出了第5张票
张三卖出了第4张票
张三卖出了第3张票
张三卖出了第2张票
张三卖出了第1张票
② 使用Lock进行线程同步:
public class Demo02 {
public static void main(String[] args) {
// 并发,多线程操作统一资源
Ticket2 ticket = new Ticket2();
// Lambda表达式
new Thread(()->{
for (int i = 0; i < 60; i++) ticket.saleTicket(); },"张三").start();
new Thread(()->{
for (int i = 0; i < 60; i++) ticket.saleTicket(); },"李四").start();
new Thread(()->{
for (int i = 0; i < 60; i++) ticket.saleTicket(); },"王五").start();
}
}
class Ticket2{
private int number = 50;
Lock lock = new ReentrantLock();
public void saleTicket(){
lock.lock(); // 加锁
try{
if (number>0){
System.out.println(Thread.currentThread().getName()+"卖出了第"+(number--)+"张票");
}
}finally {
lock.unlock(); // 解锁
}
}
}
结果:
张三卖出了第50张票
张三卖出了第49张票
张三卖出了第48张票
张三卖出了第47张票
张三卖出了第46张票
......(此处省略)
张三卖出了第5张票
张三卖出了第4张票
张三卖出了第3张票
张三卖出了第2张票
张三卖出了第1张票
线程之间交替操作num,当num==0时,num加一,当num>0时,num减一
① 使用synchronized进行线程同步:
public class Demo03 {
public static void main(String[] args) {
Num num = new Num();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
num.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"A").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
num.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"B").start();
}
}
class Num{
private int num = 0;
// +1:当num==0时,加一
public synchronized void increment() throws InterruptedException {
if (num!=0){
this.wait(); // 等待
}
num++;
System.out.println(Thread.currentThread().getName()+"-->"+num);
this.notifyAll(); // 唤醒,通知其他线程+1完毕
}
// -1:当num>0时,减一
public synchronized void decrement() throws InterruptedException {
if (num==0){
this.wait(); // 等待
}
num--;
System.out.println(Thread.currentThread().getName()+"-->"+num);
this.notifyAll(); // 唤醒,通知其他线程-1完毕
}
}
结果:
A-->1
B-->0
A-->1
B-->0
A-->1
B-->0
A-->1
B-->0
A-->1
B-->0
A-->1
B-->0
A-->1
B-->0
A-->1
B-->0
A-->1
B-->0
A-->1
B-->0
当有线程超过两个时,可能会出现虚假唤醒的情况,所以判断语句用while循环,避免虚假唤醒
public class Demo04 {
public static void main(String[] args) {
Num2 num = new Num2();
new Thread(()->{
for (int i = 0; i < 5; i++) {
try {
num.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"A").start();
new Thread(()->{
for (int i = 0; i < 5; i++) {
try {
num.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"B").start();
new Thread(()->{
for (int i = 0; i < 5; i++) {
try {
num.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"C").start();
new Thread(()->{
for (int i = 0; i < 5; i++) {
try {
num.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"D").start();
}
}
class Num2{
private int num = 0;
// +1:当num==0时,加一
public synchronized void increment() throws InterruptedException {
while (num!=0){
// 使用while循环,避免虚假唤醒
this.wait(); // 等待
}
num++;
System.out.println(Thread.currentThread().getName()+"-->"+num);
this.notifyAll(); // 唤醒,通知其他线程+1完毕
}
// -1:当num>0时,减一
public synchronized void decrement() throws InterruptedException {
while (num==0){
// 使用while循环,避免虚假唤醒
this.wait(); // 等待
}
num--;
System.out.println(Thread.currentThread().getName()+"-->"+num);
this.notifyAll(); // 唤醒,通知其他线程-1完毕
}
}
结果:
A-->1
B-->0
A-->1
B-->0
A-->1
B-->0
A-->1
B-->0
A-->1
B-->0
C-->1
D-->0
C-->1
D-->0
C-->1
D-->0
C-->1
D-->0
C-->1
D-->0
② 使用Lock进行线程同步:
Condition:同步监视器
public class Demo05 {
public static void main(String[] args) {
Num3 num = new Num3();
new Thread(()->{
for (int i = 0; i < 5; i++) {
try {
num.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"A").start();
new Thread(()->{
for (int i = 0; i < 5; i++) {
try {
num.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"B").start();
new Thread(()->{
for (int i = 0; i < 5; i++) {
try {
num.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"C").start();
new Thread(()->{
for (int i = 0; i < 5; i++) {
try {
num.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"D").start();
}
}
class Num3{
private int num = 0;
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
// +1:当num==0时,加一
public void increment() throws InterruptedException {
lock.lock();
try {
while (num!=0){
condition.await(); // 等待 相当于synchronized中的wait()
}
num++;
System.out.println(Thread.currentThread().getName()+"-->"+num);
condition.signalAll(); // 唤醒,通知其他线程+1完毕 相当于synchronized中的notifyAll()
}finally {
lock.unlock();
}
}
// -1:当num>0时,减一
public void decrement() throws InterruptedException {
lock.lock();
try {
while (num==0){
condition.await(); // 等待 相当于synchronized中的wait()
}
num--;
System.out.println(Thread.currentThread().getName()+"-->"+num);
condition.signalAll(); // 唤醒,通知其他线程-1完毕 相当于synchronized中的notifyAll()
}finally {
lock.unlock();
}
}
}
结果:
A-->1
B-->0
A-->1
B-->0
A-->1
B-->0
A-->1
B-->0
C-->1
B-->0
C-->1
D-->0
C-->1
D-->0
C-->1
D-->0
C-->1
D-->0
A-->1
D-->0
Condition 实现精准通知唤醒:
// A执行完执行B,B执行完执行C,C执行完执行A
public class Demo06 {
public static void main(String[] args) {
Num4 num = new Num4();
new Thread(()->{
for (int i = 0; i < 5; i++) {
num.printA();
}
},"A").start();
new Thread(()->{
for (int i = 0; i < 5; i++) {
num.printB();
}
},"B").start();
new Thread(()->{
for (int i = 0; i < 5; i++) {
num.printC();
}
},"C").start();
}
}
class Num4{
private int num = 1;
private Lock lock = new ReentrantLock();
private Condition condition1 = lock.newCondition();
private Condition condition2 = lock.newCondition();
private Condition condition3 = lock.newCondition();
public void printA(){
lock.lock();
try {
while (num!=1){
condition1.await(); //等待
}
num = 2;
System.out.println(Thread.currentThread().getName()+"-->"+"AAA");
condition2.signal(); // 唤醒B
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
public void printB(){
lock.lock();
try {
while (num!=2){
condition2.await(); //等待
}
num = 3;
System.out.println(Thread.currentThread().getName()+"-->"+"BBB");
condition3.signal(); // 唤醒C
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
public void printC(){
lock.lock();
try {
while (num!=3){
condition3.await(); //等待
}
num = 1;
System.out.println(Thread.currentThread().getName()+"-->"+"CCC");
condition1.signal(); // 唤醒A
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
}
结果:
A-->AAA
B-->BBB
C-->CCC
A-->AAA
B-->BBB
C-->CCC
A-->AAA
B-->BBB
C-->CCC
A-->AAA
B-->BBB
C-->CCC
A-->AAA
B-->BBB
C-->CCC
Lock用法跟synchronized差不多,但能够显现精准唤醒