Java多线程并发笔记(1.3)多线程遇到的问题

前言

记录学习过程
我们可以通过Runnable接口创建线程,也可以实现多线程,接下来就需要实现线程安全
线程安全:当一个类,不断被多个线程调用,仍能表现出正确的行为时,那它就是线程安全的
而以前很多安全的代码到了多线程就会出问题,先要明白为什么会线程不安全
多线程基础知识

线程不安全

多线程是为了提高程序的使用率,然而会出现很多线程安全问题

因为在多线程的环境下,线程是交替执行的,一般他们会使用多个线程执行相同的代码。如果在此相同的代码里边有着共享的变量,或者一些组合操作,我们想要的正确结果就很容易出现了问题

共享变量问题

package com.company.Thread;

public class ThreadOfPool {
    private static int count=1;
     static class CreatThread1 extends Thread {
        public void run(){
            int counts =count ;
            try {
                sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            counts=counts+1;
            System.out.println("线程1:"+counts);
        }
    }
    static class CreatThread2 extends Thread {
        public void run() {
            count++;
            System.out.println("线程2:"+count);
        }
    }
    public int getCount(){
         return count;
    }
    public static void main(String[] args) throws InterruptedException {
         CreatThread1 creatThread1=new CreatThread1();
         CreatThread2 creatThread2=new CreatThread2();
         creatThread1.start();
         creatThread2.start();
    }
}

就是一个 读取-修改-写入 存在的问题
这里在读取后sleep了一秒,模拟count++的操作
在这里插入图片描述

按理两次相加应该为3,而这两个线程都显示2,就是线程1读取了共享变量,还没修改线程2就读取了共享变量,导致结果的不正常

对象的发布与逸出

发布(publish) 使对象能够在当前作用域之外的代码中使用

逸出(escape) 当某个不应该发布的对象被发布了

常见逸出的有下面几种方式:

  • 静态域逸出
  • public修饰的get方法
  • 方法参数传递
  • 隐式的this

静态域逸出

  public static Set<Father> fatherset;

静态变量随着类加载而加载,存储在方法区,public修饰的静态域相对于发布了Father对象,没有初始化就可以使用Father对象了

public修饰get方法

    public int getCount(){
         return count;
    }

其他类也可以通过getCount方法获得count变量
count变量超出了作用域

方法参数传递
作为一个方法的参数传递出去,逸出

    public void setCount(int count){
         this.count=count;
    }

count作为参数逸出

隐式的this

this引用逸出是指,在类的构造方法中发布该类的对象,导致尚未构造完全的对象被其他线程访问。这样一来,不同的线程得到的可能是不同的结果,有些线程访问的是构造完全的对象,而另一些线程则会访问到尚未构造完全的对象,最终导致不可预测的错误

效率问题

使用多线程是为了提高程序运行效率
没有设计好反倒会降低效率甚至死锁

例如 web中无限制创建线程
线程竞争开销,上下文切换开销
还需要深入学习
Java多线程引发的性能问题以及调优策略

线程安全

线程的不安全大致可以看做在对象创建时对象创建后。不仅仅是在对象创建后的业务逻辑中要考虑线程的安全性,在对象创建的过程中,也要考虑线程安全。

多线程带来的线程安全问题在Java中,我们一般会有下面这么几种办法来实现线程安全问题:

  • 无状态(没有共享变量)

  • 使用final使该引用变量不可变(如果该对象引用也引用了其他的对象,那么无论是发布或者使用时都需要加锁)

  • 加锁(内置锁,显示Lock锁)

  • 使用JDK为我们提供的类来实现线程安全(此部分的类就很多了)
    原子性(就比如上面的count++操作,可以使用AtomicLong来实现原子性,那么在增加的时候就不会出差错了!)
    容器(ConcurrentHashMap等等…)

发布了49 篇原创文章 · 获赞 0 · 访问量 1225

猜你喜欢

转载自blog.csdn.net/key_768/article/details/104279516