javaSE--多线程2

一、线程的上下文切换

一个CPU内核中同一时间只能运行一个线程或者一行指令,切换到另一个线程的过程称为线程的上下文切换

1.线程切换存在的问题

	1.线程切换的时候如何找到上次执行的地方?
	每个线程中都有各自的程序计数器,用于记录上次执行的行数
	线程在上下切换时会消耗时间,性能会降低

二、线程安全(同步)问题

CPU在多个线程之间切换时,一个线程成员变量做了读的操作,线程切换之后,另一个线程对这个成员变量进行修改,就会出现线程安全问题

案例:对一些账户的余额做存取操作,存取一次后对银行总额进行计算

public class ThreadLock {
    //下标表示账户,对应的value表示余额
    private int[] account = new int[100];

    {
        //初始化数组
        for (int i = 0; i < account.length; i++) {
            account[i] = 10000;
        }
    }

    public  void transfer(int from, int to, int money) {
        if (account[from] < money) {
            throw new RuntimeException("余额不足");
        }
            System.out.println(Thread.currentThread().getName());
            account[from] -= money;
            System.out.println(from + "转账到" + to + "--" + money + "元");
            account[to] += money;
            System.out.println(to + "收到" + from + "--" + money + "元");
            System.out.println("总账为:" + total());

    }
    /**
     * 合计
     *
     * @return
     */
    public int total() {
        int num = 0;
        for (int i = 0; i < account.length; i++) {
            num += account[i];
        }
        return num;
    }

    public static void main(String[] args) {
        ThreadLock threadLock = new ThreadLock();
        Random random = new Random();
        for (int i = 0; i < 50; i++) {
            new Thread(() -> {
                int from = random.nextInt(100);
                int to = random.nextInt(100);
                int money = random.nextInt(2000);
                threadLock.transfer(from, to, money);
            }).start();
        }
    }
}

在这里插入图片描述
会出现余额总计对不上

三、解决线程安全问题–上锁

上锁的方法:

	1.同步方法
	2.同步代码块
	3.同步锁

1.同步方法

package com.tx.thread;

import java.util.Random;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class ThreadLock {
    //下标表示账户,对应的value表示余额
    private int[] account = new int[100];

    {
        //初始化数组
        for (int i = 0; i < account.length; i++) {
            account[i] = 10000;
        }
    }
        //在方法上加上synchronized关键字,给整个方法上锁
    public synchronized void transfer(int from, int to, int money) {
        if (account[from] < money) {
            throw new RuntimeException("余额不足");
        }
            System.out.println(Thread.currentThread().getName());
            account[from] -= money;
            System.out.println(from + "转账到" + to + "--" + money + "元");
            account[to] += money;
            System.out.println(to + "收到" + from + "--" + money + "元");
            System.out.println("总账为:" + total());

    }
    /**
     * 合计
     *
     * @return
     */
    public int total() {
        int num = 0;
        for (int i = 0; i < account.length; i++) {
            num += account[i];
        }
        return num;
    }

    public static void main(String[] args) {
        ThreadLock threadLock = new ThreadLock();
        Random random = new Random();
        for (int i = 0; i < 50; i++) {
            new Thread(() -> {
                int from = random.nextInt(100);
                int to = random.nextInt(100);
                int money = random.nextInt(2000);
                threadLock.transfer(from, to, money);
            }).start();
        }
    }
}

在方法上加上synchronized关键字,给整个方法上锁
当前线程调用方法后,后面的线程会无法执行,当前线程执行完毕后会释放锁,让其他线程进行,也就是会发生阻塞

2.同步代码块

package com.tx.thread;

import java.util.Random;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class ThreadLock {
    //下标表示账户,对应的value表示余额
    private int[] account = new int[100];

    {
        //初始化数组
        for (int i = 0; i < account.length; i++) {
            account[i] = 10000;
        }
    }
        
    public  void transfer(int from, int to, int money) {
        if (account[from] < money) {
            throw new RuntimeException("余额不足");
        }
        //在部分关键代码上 上锁
        synchronized (this){
            System.out.println(Thread.currentThread().getName());
            account[from] -= money;
            System.out.println(from + "转账到" + to + "--" + money + "元");
            account[to] += money;
            System.out.println(to + "收到" + from + "--" + money + "元");
        }

            System.out.println("总账为:" + total());

    }
    /**
     * 合计
     *
     * @return
     */
    public int total() {
        int num = 0;
        for (int i = 0; i < account.length; i++) {
            num += account[i];
        }
        return num;
    }

    public static void main(String[] args) {
        ThreadLock threadLock = new ThreadLock();
        Random random = new Random();
        for (int i = 0; i < 50; i++) {
            new Thread(() -> {
                int from = random.nextInt(100);
                int to = random.nextInt(100);
                int money = random.nextInt(2000);
                threadLock.transfer(from, to, money);
            }).start();
        }
    }
}

在部分代码上加锁,synchronize(this){}这里的this表示当前类,因为只锁住了部分代码,比锁方法的效率高,也就是粒度越小,越灵活,效率越高

3.同步锁

在java.util.concurrent的并发包中有Lock接口
常见实现类:
ReentrantLock(重入锁)
writeLock(写锁)
ReadLock(读锁)
ReadWriteLock(读写锁)
基本方法

方法用法
lock()上锁
unlock()释放锁

1.定义同步锁对象
2.调用lock()方法上锁
3.指令执行完成后调用unlock()释放锁
*使用同步锁时,最后一定要执行unlock方法所以用try{}finally{}来让程序必须执行unlock方法

package com.tx.thread;

import java.util.Random;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class ThreadLock {
    //下标表示账户,对应的value表示余额
    private int[] account = new int[100];

    private Lock lock=new ReentrantLock();

    {
        //初始化数组
        for (int i = 0; i < account.length; i++) {
            account[i] = 10000;
        }
    }

    public  void transfer(int from, int to, int money) {
        if (account[from] < money) {
            throw new RuntimeException("余额不足");
        }

       //在部分关键代码上 上锁
        lock.lock();
        try {
            System.out.println(Thread.currentThread().getName());
            account[from] -= money;
            System.out.println(from + "转账到" + to + "--" + money + "元");
            account[to] += money;
            System.out.println(to + "收到" + from + "--" + money + "元");
        }finally {
            lock.unlock();
        }
            System.out.println("总账为:" + total());

    }
    /**
     * 合计
     *
     * @return
     */
    public int total() {
        int num = 0;
        for (int i = 0; i < account.length; i++) {
            num += account[i];
        }
        return num;
    }

    public static void main(String[] args) {
        ThreadLock threadLock = new ThreadLock();
        Random random = new Random();
        for (int i = 0; i < 50; i++) {
            new Thread(() -> {
                int from = random.nextInt(100);
                int to = random.nextInt(100);
                int money = random.nextInt(2000);
                threadLock.transfer(from, to, money);
            }).start();
        }
    }
}

synchronize的基本原理

一旦代码被synchronize包含,jvm会启动监视器,对这段指令进行监控,当一个线程执行到这段代码时,监视器会判断锁对象是否有其他线程持有,如果有其他线程持有,那这个线程就无法执行,只能等待所得释放, 如果没有其他线程持有,就直接执行

锁对象

可以对当前线程进行管理,如:wait等待,nodify通知
对于非静态方法:this 静态方法:类.class

乐观锁和悲观锁

悲观锁:认为锁的安全问题比较容易出现,对代码进行上锁,前边所说的同步方法,同步代码块,同步锁,都属于悲观锁,只有等到前面的线程释放锁,后面的线程才能继续执行,悲观锁的锁定和释放需要消耗时间,降低了程序的性能
乐观锁:认为锁的安全问题不容易出现,不对代码进行上锁,只会对代码做一下特殊判断

乐观锁

乐观锁的实现方式,也就是乐观锁怎么对代码做判断的

1.采用版本号机制

版本号机制就是给数据加上版本号version,每次更新数据时,版本号Version会+1,当线程做更新是会读版本号version,对比读到的版本号与数据之前的版本号是否一致,是则提交更新,否则提交失败
例如:
银行操作员对 账户(余额:100,version:1)做操作
操作员A:读取账户余额100,version:1,对其进行100-50操作,在操作员A操作的过程中,
操作员B:对其账户进行操作,读到余额:100,version:1,做100-20操作
操作员A:更新数据,余额变为 50,version+1变为2
操作员B做提交更新时,version与自己读到的版本号version不一致,更新会被驳回

2.CAS比较和交换算法

总结

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值