Java多线程高并发,代码示例(一)

前言

项目主体源码可以从ConcurrenceBasics获得,喜欢的朋友可以点个star~。
参考文献在参考资料部分有介绍。

对某个对象加锁
/**
 * synchronized关键字
 * 对某个对象加锁
 */
public class T {
    private int count = 10;
    private Object o = new Object();
    public void m(){
        //任何线程要执行下面代码,必须先拿到o锁
        synchronized (o){
            count--;
            System.out.println(Thread.currentThread().getName() + " count = " + count);
        }
    }
}
对当前对象加锁(一)
/**
 * synchronized关键字
 * 对某个对象加锁
 */
public class T {
    private int count = 10;
    public void m(){
        //任何线程要执行下面的代码,必须先拿到this的锁
        synchronized (this){
            count--;
            System.out.println(Thread.currentThread().getName() + " count = " + count);
        }
    }
}
对当前对象加锁(二)
/**
 * synchronized关键字
 * 对某个对象加锁
 */
public class T {
    private int count = 10;
    //同等于在方法的代码执行要synchronized(this)
    public synchronized void m(){
        count--;
        System.out.println(Thread.currentThread().getName() + " count = " + count);
    }
}
对当前对象加锁(三)
/**
 * synchronized关键字
 * 对某个对象加锁
 */
public class T {
    private static int count = 10;
    //这里等同于synchronized(xxx.T.class)
    public synchronized static void m(){
        count--;
        System.out.println(Thread.currentThread().getName() + " count = " + count);
    }
    public static void ms(){
        synchronized (T.class){
            count--;
        }
    }
}
synchronized使用案例
/**
 * 实验结果:在run方法如果不加上synchronized,会产生线程抢占,
 * 如果加上就确保了线程运行的原子性,先执行的执行,后执行的等待。
 */
public class T implements Runnable {

    private int count = 10;

    @Override
    public /*synchronized*/ void run() {
        count--;
        System.out.println(Thread.currentThread().getName() + " count = " + count);
    }

    public static void main(String[] args) {
        T t = new T();
        for (int i = 0; i < 5; i++){
            new Thread(t, "Thread" + i).start();
        }
    }
}
在一个对象一面两个方法加不加synchronized修饰的影响
/**
 * 实验结果:此案例说明加synchronized修饰后的线程对象,
 * 和不加synchronized修饰,两者同时运行并不冲突。
 */
public class T {
    public synchronized void m1(){
        System.out.println(Thread.currentThread().getName() + " m1 start...");
        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + " m1 end...");
    }

    public void m2(){
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + " m2...");
    }

    public static void main(String[] args) {
        T t = new T();
        //语法有点叼
        new Thread(()->t.m1(),"t1").start();
        new Thread(()->t.m2(),"t2").start();

        //和上面结果一样
        //new Thread(t::m1, "t1").start();
        //new Thread(t::m2, "t2").start();
    }
}
脏读以及解决方法
/**
 * 脏读
 * 实验结果:实验结果为0.0。
 * 解决方法:确保读写都加上synchronized,保证原子性。
 */
public class Account {
    String name;
    double balance;

    public synchronized void set(String name, double balance){
        this.name = name;
        //这里加上间隔时间的意义在于模拟线程被其他线程进行调用
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        this.balance = balance;
    }

    public /*synchronized*/ double getBalance(String name){
        return this.balance;
    }

    public static void main(String[] args) {
        Account account = new Account();
        new Thread(()->account.set("xipiker", 21.0)).start();

        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println(account.getBalance("xipiker"));

    }
}
synchronized获得的锁可重入
/**
 * 一个同步方法可以调用另一个同步方法,一个线程已经拥有某个对象的锁,
 * 再次申请的时候仍然会得到该兑现的锁。也就是说synchronized获得的
 * 锁是可重入的。
 */
public class T {
    synchronized void m1(){
        System.out.println("m1 start...");
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        m2();
    }

    synchronized void m2(){
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("m2 start...");
    }

    public static void main(String[] args) {
        T t = new T();
        new Thread(()->t.m1(), "t1").start();
    }
}
子类调用父类的同步方法
/**
 * 子类调用父类的同步方法
 */
public class T {
    synchronized void m(){
        System.out.println("m start");
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("m end");
    }

    public static void main(String[] args) {
        new TT().m();
    }
}

class TT extends T{
    synchronized void m(){
        System.out.println("child m start");
        super.m();
        System.out.println("child m end");
    }
}
程序运行出现异常,默认情况锁会被释放
/**
 * 程序在执行过程中,如果出现异常,默认情况下锁会被释放。
 * 所以,在并发处理过程中,有异常要多加小心,不然可能会发生不一致情况。
 * 比如,在一个web app处理过程中,多个servlet线程共同访问同一个资源,
 * 这时候如果异常处理不合适,在第一个线程中抛出异常,其他线程就会进入同步代码区,
 * 有可能会访问到异常产生时的数据。因此要非常小心的处理同步业务逻辑中的异常。
 * 实验结果:锁被释放了,原子性不存在了,第二个线程接着第一个线程继续执行。
 */
public class T {
    int count = 0;
    synchronized void m(){
        System.out.println(Thread.currentThread().getName() + " start...");
        while (true){
            count++;
            System.out.println(Thread.currentThread().getName() + " count = " + count);
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //这里我们模拟异常
            //此处抛出异常,锁将释放,要想不被释放,可以在这里进行catch,然后让循环继续
            if (count == 5){
                int i = 1/0;
            }
        }
    }

    public static void main(String[] args) {
        T t = new T();
        Runnable r = new Runnable() {
            @Override
            public void run() {
                t.m();
            }
        };

        new Thread(r, "t1").start();

        try {
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(r, "t2").start();
    }
}
volatile使一个变量在多个线程间可见
/**
 * volatile关键字,使一个变量在多个线程间可见。
 * 例子: A B线程都用到一个变量,java默认是A线程中保留一份copy,这样如果B线程修改了该变量,则A线程未必知道
 * 使用volatile关键字,会让所有线程都会读到变量的赋值。
 *
 * 在下面代码中running是存在于堆内存的t对象中,当线程t1开始运行的时候,会把running值从内存中读到t1线程的工作区,
 * 在运行过程中直接使用这个copy,并不会每次都去读取堆内存,这样,当主线程修改running的值之后,t1线程感知不到,所以不会停止运行。
 * 使用volatile,将会强制所有线程都去堆内存中读取running的值。
 * volatile并不能保证多个线程共同修改running变量时所带来的不一致问题,也就是说volatile不能替代synchronized。
 */
public class T {
    volatile boolean running = true;
    void m(){
        System.out.println("m start...");
        while (running){

        }
        System.out.println("m end!");
    }

    public static void main(String[] args) {
        T t = new T();
        new Thread(t::m, "t1").start();

        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        t.running = false;
    }
}
volatile并不能保证原子性
/**
 * volatile并不能保证多个线程共同修改running变量时所带来的不一致问题,也就是说volatile不能代替synchronized。
 * 实验结果:多个线程间比如A线程执行中的count为100,B线程计算的A的count在加一为101,然后轮到A执行时他的count值时应该count结果为102,
 * 但是执行了为101,所以原本count的102值又被清回101。
 */
public class T {
    volatile int count = 5;
    void m(){
        for(int i = 0; i < 1000; i++)
            count++;
    }

    public static void main(String[] args) {
        T t = new T();
        List<Thread> threads = new ArrayList<Thread>();

        for (int i = 0; i < 10; i++){
            threads.add(new Thread(t::m, "Thread-" + i));
        }

        threads.forEach((o)->o.start());

        threads.forEach((o)->{
            try {
                o.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        System.out.println(t.count);
    }
}
使用AtomXXX类,确保变量的原子性
/**
 * 确保变量的原子性,更加高效的方法可以使用AtomXXX类。
 * AtomXXX类本身方法都是原子性,但不能保证多个方法连续调用是原子性。
 */
public class T {
    AtomicInteger count = new AtomicInteger(0);
    void m(){
        for (int i = 0; i < 10000; i++)
            //if count.get() < 1000 ->这里如果执行那么就不在具有原子性了,
            // 因为可能会有线程执行到count.get() < 1000里面去,而又有的线程执行了下面的++操作。
            count.incrementAndGet(); //count++
    }

    public static void main(String[] args) {
        T t = new T();
        List<Thread> threadList = new ArrayList<Thread>();
        for (int i = 0; i < 10; i++){
            threadList.add(new Thread(t::m, "thread-" + i));
        }

        threadList.forEach((o)->o.start());

        threadList.forEach((o)->{
            try {
                o.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        System.out.println(t.count);
    }
}
  • 4
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值