多线程同步并发问题的解决方法和相关案例

多线程同步问题:synchronized与案例

多线程任务经常会出现数据不一致的情况,要解决这种问题,就需要使用synchronized同步锁。

目录

一、synchronized的基础知识

1.概念

2. synchronized的使用方法 

2.1 synchronized修饰实例方法

二、相关案例

1.多线程售票

2.多线程打印数字和字母

3.利用AtomicInteger的原子性,简化代码,不用加锁


一、synchronized的基础知识

1.概念

synchronized关键字,也可以理解为同步锁。synchronized就像是一把锁一样,把需要互斥的代码锁住,只有获得了这把锁的线程才可以访问这段代码。同时,只有一个线程可以获得这把锁,这样就实现了原子性,这段代码也称做为临界区。临界区就不会被随意访问了。

2. synchronized的使用方法 

2.1 synchronized修饰实例方法

public synchronized void doSth1(){
        System.out.println("实例方法加锁");
    }

 2.2 synchronized修饰静态方法

public synchronized static void doSth3(){
        System.out.println("静态方法加锁");
    }

2.3 synchronized修饰代码块

public void doSth2(){
        synchronized (this){
            System.out.println("在实例方法中的代码块加锁");
        }
    }
public static void doSth4(){
        synchronized (Class.class){
            System.out.println("在静态方法中的代码块加锁");
        }
    }
//直接使用synchronized关键字
        synchronized (Counter.lock){
            for (int i = 0; i <10000 ; i++) {
                //临界区
                Counter.count-=1;
            }
        }//释放锁

二、相关案例

1.多线程售票

代码如下:

package org.example;

public class ProjectExample {
    public static void main(String[] args) {
        TicketPool publicTicketPool=new TicketPool(20);
         new Thread(publicTicketPool,"窗口1").start();
        new Thread(publicTicketPool,"窗口2").start();
        new Thread(publicTicketPool,"窗口3").start();
    }
}
class TicketPool implements Runnable{
    private int count;
    public TicketPool(int count){
        this.count=count;
    }
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"开始卖票");
        synchronized (this){
            while(true){
                if(count<=0){
                    System.out.println("票售罄了,"+Thread.currentThread().getName()+"关闭");
                    break;
                }else{
                    String name=Thread.currentThread().getName();
                    System.out.println(name+"售出票"+"还剩"+(--count)+"张票");
                    try {
                        //sleep()休眠期间不释放锁
                        //Thread.sleep(1000);
                        //wait()等待期间释放锁,必须要由充当锁的对象调用(必须在syncjronized代码块里面)
                        this.wait(1000);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        }
    }
}

2.多线程打印数字和字母

代码如下:

package org.example;

//输出12A 34B 56C 78D 910E
// 1112F 1314G 1516H 1718I 1920J 2122K 2324L
// 2526M 2728N 2930O 3132P 3334Q 3536R 3738S 3940T
// 4142U 4344V 4546W 4748X 4950Y 5152Z 格式内容

public class ProjectExample2 {
    public static void main(String[] args) {
        Object lock=new Object();
        //分别创建两个线程,执行打印数字和字母的线程任务
        Thread t1=new Thread(new Number(lock));
        t1.start();

        Thread t2=new Thread(new Character(lock));
        t2.start();
    }
}
//数字线程任务
class Number implements Runnable{
    public  Object lock;

    public Number(Object lock) {
        this.lock=lock;
    }

    @Override
    public void run() {
        //加同步锁,使得输出的时候不会被打断
        synchronized (lock){
            for (int i = 1; i <=52 ; i++) {

                //奇数前面加入空格
                if ((i&1)==1){
                    //第一个数字前没有空格
                    if (i!=1){
                        System.out.print(" ");
                    }
                }
                //打印数字
                System.out.print(i);

                //偶数进入等待,等待字母进程,在等待前唤醒字母进程
                if ((i&1)==0){
                    //【数字进程】唤醒【字母进程】
                    lock.notifyAll();
                    try {
                        //【数字进程】等待,并释放锁
                        lock.wait();
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }

            }
        }
    }
}
//字母线程任务
class Character implements Runnable{
    public  Object lock;

    public Character(Object lock) {
        this.lock=lock;
    }

    @Override
    public void run() {
       synchronized (lock){
           for (char c ='A' ; c <='Z' ; c++) {
               System.out.print(c);

               //字母进程唤醒数字进程
               lock.notifyAll();
               if(c<'Z'){

                   try {
                       //小于Z,字母线程等待,并释放锁
                       lock.wait();
                   } catch (InterruptedException e) {
                       throw new RuntimeException(e);
                   }
               }
           }
       }
    }
}

3.利用AtomicInteger的原子性,简化代码,不用加锁

代码如下:

package org.example;

import java.util.concurrent.atomic.AtomicInteger;

public class ProjectExample3 {
    //利用AtomicInteger的原子性,简化代码,不用加锁
    private static AtomicInteger counter=new AtomicInteger();
    public static void main(String[] args) {
        Thread t1=new Thread(()->{
            for (int i = 0; i <=10000 ; i++) {
                int value=counter.incrementAndGet();
                System.out.println("本次递增的结果是:"+value);
            }
        });
        Thread t2=new Thread(()->{
            for (int i = 0; i <=10000 ; i++) {
                int value=counter.decrementAndGet();
                System.out.println("本次递减的结果是:"+value);
            }
        });
        t1.start();
        t2.start();
        try {
            t1.join();
            t2.join();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("最后的结果为:"+counter);
    }
}

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值