java基础-如何实现线程安全?synchronized的使用

进程与线程的概念:

进程:操作系统运行的一个程序,CPU资源分配的最小单位
线程:进程的一个执行单元,CPU调度的最小单位
一个进程至少有一个线程,如果进程有多个线程,则它是多线程应用程序

java线程栈是相互独立的,每个线程都有自己的栈空间,共享堆区和方法区

主线程:JVM启动主线程,主线程运行main方法
用户线程:开启新的线程,也称子线程
守护线程:守护线程是为其他线程提供服务 的线程,不能独立运行,当JVM中只有守护线程时,JVM就会退出
垃圾回收器就是一个守护线程

Java线程调度方式

线程睡眠:Thread.sleep(long millis),使线程转换到阻塞状态,当睡眠结束后,就转为就绪状态

线程等待:Object.wait(),导致当前的线程等待,直到其他线程调用此对象的notify()或notifyAll()

线程唤醒:Object.notify()方法,唤醒在此对象上等待的单个线程。

线程让步:Thread.yield(),暂停当前正在执行的线程对象,把执行机会让给相同或更高优先级的线程

线程加入:join(),等待其他线程终止

线程同步

线程安全问题:当多个线程同时操作堆区或者方法区的某个数据时,可能会出现数据不一致的现象

java线程是相互独立的,每个线程都有自己的栈空间,共享堆区和方法区

创建线程代码例子:

public class ThreadDemo extends Thread {
    public void run(){
        for(int i=0;i<100;i++){
            System.out.println("ThreadDemo"+i);
        }
    }
}
class Test04{
    public static void main(String[] args) {
        ThreadDemo t1 = new ThreadDemo();
        t1.start();//启动新的线程 自动执行run()方法
        for(int i=1;i<10;i++){
            System.out.println("main"+i);
        }
    }
    /*
    *在当前程序main 和 t1线程在同时执行,每次运行的结果可能不一样
    * 多线程程序中的多个线程 谁抢到cpu谁执行
    * */

}

如何解决线程安全问题:

1)每个线程都访问自己的局部遍历

2)如果多个线程必须操作 实例变量或者静态变量,可以采用线程同步技术。(当一个线程在操作期间,它不允许其他线程加入)

线程不安全例子:

public class BankAccount {
    int balance = 10000;//账户余额:万元
    public void withdraw(){
        System.out.println(Thread.currentThread().getName()+"取钱前查询余额"+balance+"万元");
        balance-=1000;//每次取1000万元
        System.out.println(Thread.currentThread().getName()+"取了1000万后查询余额"+balance+"万元");
    }
}
//用户操作
public class PersonThread extends Thread{
    private BankAccount account;
    public PersonThread(BankAccount account) {
        super();
        this.account = account;
    }
    @Override
    public void run(){
        //从账户中取钱
        account.withdraw();

    }
}
class Test{
    public static void main(String[] args) {
        //先开户
        BankAccount account = new BankAccount();
        //创建3个线程 模拟用户取钱
        PersonThread bingbing = new PersonThread(account);
        PersonThread xiaoming = new PersonThread(account);
        PersonThread xiaogang = new PersonThread(account);
        bingbing.setName("bingbing");
        xiaoming.setName("xiaoming");
        xiaogang.setName("xiaogang");
        bingbing.start();
        xiaoming.start();
        xiaogang.start();
    }
}

运行结果:

可以看出xiaoming取钱前查询余额10000万元 xiaoming取了1000万后查询余额8000万元
期间有其他线程操作了取的钱导致数据不一致

线程同步技术,解决线程不安全问题

工作原理:

1)任意对象都可作为锁对象,每个对象只有一个内置锁

2)某一时刻,1个锁对象最多只能被一个线程持有

3)如果线程获得了锁对象后会一直持有,直到执行完同步代码块后才释放

4)线程要执行同步代码块,必须持有锁对象

代码例子(多个线程必须操作 实例变量或者静态变量)

//线程安全例子
//1)bingbing先获得CPU执行权,执行withdraw方法,获得OBJ这个锁对象,执行取钱操作
//2)xiaoming获得CPU执行权,执行withdraw方法,在执行取钱操作需要获得锁对象,现在锁对象被bingbing持有,xiaoming转到等待锁对象池中进行阻塞
//3)xiaogang获得CPU执行权,执行withdraw方法,在执行取钱操作需要获得锁对象,现在锁对象被bingbing持有,xiaogang转到等待锁对象池中进行阻塞
//4)xiaogang重新获得CPU执行权,执行完取钱操作释放锁对象
//5)等待锁对象池中的 xiaoming或xiaogang中的1个获得锁对象,获得锁对象的 进入就绪状态,未获得锁对象的还是阻塞状态
public class BankAccount {
    int balance = 10000;//账户余额:万元
    private static final Object OBJ =   new Object(); //定义了一个常量
    //取钱操作约定每次取1000万元
    public void withdraw(){
        //经常把一个常量作为一个锁对象
        synchronized (OBJ){
            System.out.println(Thread.currentThread().getName()+"取钱前查询余额"+balance+"万元");
            balance-=1000;//每次取1000万元
            System.out.println(Thread.currentThread().getName()+"取了1000万后查询余额"+balance+"万元");

        }
    }
}
public class PersonThread extends Thread{
    private BankAccount account;
    public PersonThread(BankAccount account) {
        super();
        this.account = account;
    }
    @Override
    public void run(){
        //从账户中取钱
        account.withdraw();

    }
}

class Test{
    public static void main(String[] args) {
        //先开户
        BankAccount account = new BankAccount();
        //创建3个线程 模拟用户取钱
        PersonThread bingbing = new PersonThread(account);
        PersonThread xiaoming = new PersonThread(account);
        PersonThread xiaogang = new PersonThread(account);
        bingbing.setName("bingbing");
        xiaoming.setName("xiaoming");
        xiaogang.setName("xiaogang");

        bingbing.start();
        xiaoming.start();
        xiaogang.start();
    }
}

运行结果:

如何同步方法?保证方法顺序执行

public class MainSynchronized {
    public static void main(String[] args) {
        MainSynchronized mainSynchronized = new MainSynchronized();
        new Thread(new Runnable() {
            @Override
            public void run() {
                mainSynchronized.m1();
            }
        }).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                mainSynchronized.m2();
            }
        }).start();
    }


    public synchronized void m1(){
        System.out.println("m1方法开始执行");
        for(int i=0;i<50;i++){
            System.out.println("m1---"+i);
        }
    }
    public synchronized void m2(){
        System.out.println("m2方法开始执行");
        for(int i=0;i<20;i++){
            System.out.println("m2---"+i);
        }

    }
}

posted @ 2021-08-08 03:31  只要不兔的学习笔记  阅读(185)  评论(0)    收藏  举报