Durante la entrevista hablé sobre "qué es la seguridad de los hilos" y el entrevistador me dio el visto bueno

No es seguro para subprocesos

El entrevistador preguntó: "¿Qué es la seguridad de subprocesos?" Si no puede responder bien, mire hacia abajo.

Hay un dicho en las Analectas que dice "El estudio y la excelencia llevarán a la oficialidad". Creo que mucha gente piensa que "puedes convertirte en un funcionario después de aprender bien". Sin embargo, esta comprensión es incorrecta. Recuerda lucir literario.

De la misma manera, "seguridad de subprocesos" no significa seguridad de subprocesos, sino seguridad de memoria. ¿Por qué lo dices? Esto está relacionado con el sistema operativo.

Los sistemas operativos convencionales actuales son multitarea, es decir, varios procesos se ejecutan simultáneamente. Para garantizar la seguridad, cada proceso solo puede acceder al espacio de memoria asignado a sí mismo, pero no a otros procesos, esto está garantizado por el sistema operativo.

Hay un área pública especial en el espacio de memoria de cada proceso, generalmente llamada heap (memoria). Todos los subprocesos del proceso pueden acceder a esta área, que es la posible causa del problema.

Supongamos que un hilo procesa los datos a la mitad y se siente cansado, por lo que se toma un descanso y regresa para continuar procesando, pero descubre que los datos se han modificado, no lo que eran cuando se fueron. Puede ser modificado por otros hilos.

Por ejemplo, considere la comunidad en la que vive como un proceso, y los caminos / ecologización de la comunidad pertenecen al área pública. Tiras 10.000 yuanes al suelo y te vas a casa a dormir. Después de despertarse, planea recogerlo y descubrir que el dinero se ha ido. Puede que otros se lo lleven.

Debido a que hay gente entrando y saliendo en áreas públicas, las cosas que guarde deben ser inseguras sin medidas de protección. Lo mismo ocurre con la memoria.

Por lo tanto, la seguridad de subprocesos se refiere al riesgo de modificación accidental de los datos en la memoria del montón porque cualquier subproceso puede acceder a ella sin restricciones.

Es decir, el espacio de la memoria del montón es un lugar inseguro para el subproceso múltiple sin un mecanismo de protección, porque los datos que ingresa pueden ser "destruidos" por otros subprocesos.

¿Qué debemos hacer? El proceso de resolución de problemas es en realidad un proceso de compensaciones y las diferentes soluciones tienen diferentes enfoques.

Las cosas privadas no deben ser conocidas por los demás.

En realidad, mucha gente escondería los 10.000 yuanes y se los guardaría a personas no relacionadas, por lo que es imposible tirarlos a la carretera principal. Porque este dinero es de tu propiedad privada.

Lo mismo ocurre con el programa, por lo que el sistema operativo asigna su propio espacio de memoria para cada subproceso, generalmente llamado memoria de pila, y otros subprocesos no tienen acceso a él. Esto también está garantizado por el sistema operativo.

Si algunos datos solo son utilizados por un determinado subproceso y otros subprocesos no pueden funcionar o no necesitan hacerlo, estos datos se pueden poner en la memoria de pila del subproceso. Las más comunes son las variables locales.

double avgScore(double[] scores) {
    
    
    double sum = 0;
    for (double score : scores) {
    
    
        sum += score;
    }
    int count = scores.length;
    double avg = sum / count;
    return avg;
}

Las variables sum, count y avg son todas variables locales, y todas se asignarán en la memoria de la pila de subprocesos.

Si el hilo A ahora ejecuta este método, estas variables se asignarán en la memoria de pila de A. Al mismo tiempo, el subproceso B también ejecuta este método y estas variables también se asignarán en la memoria de pila de B.

En otras palabras, estas variables locales se asignarán en la memoria de pila de cada hilo. Dado que solo se puede acceder a la memoria de pila de un subproceso por sí mismo, las variables en la memoria de pila pertenecen solo a él mismo y otros subprocesos no lo conocen.

Al igual que la casa de todos pertenece solo a uno mismo, otras personas no pueden entrar. Entonces, si pones 10,000 yuanes en casa, nadie más lo sabrá. Y por lo general se coloca en una habitación en lugar de permanecer sobre la mesa de la sala de estar.

Por lo tanto, es seguro poner sus propias cosas en su propio territorio privado, porque otras personas no pueden saberlo. Y cuanto más privado sea el lugar, mejor.

No agarres a todos, todos tienen una parte

[Beneficios del artículo] El editor recomienda mi propio grupo de intercambio de idiomas linuxC / C ++: 832218493. He compilado algunos libros de aprendizaje y materiales de video que creo que son mejores para compartir. ¡Puede agregarlos si los necesita! ~!
Inserte la descripción de la imagen aquí

Cree que eres inteligente, has descubierto que las soluciones anteriores se basan en la "ubicación". Debido a que la "ubicación" donde coloca las cosas solo la conoce (o puede llegar a ella) usted mismo, las cosas son seguras, por lo que esta seguridad está garantizada por la "ubicación".

En el programa, corresponde a la variable local del método. La razón por la que una variable local es segura es porque la "ubicación" donde se define está en el método. De esta forma se consigue la seguridad, pero su ámbito de uso se limita a este método, y ya no se necesitan otros métodos.

En realidad, a menudo hay situaciones en las que una variable debe ser utilizada por varios métodos. En este momento, la "ubicación" de la variable no se puede definir en el método, pero debe estar fuera del método. Es decir, cambiar de una variable local (del método) a una variable miembro (de la clase) es en realidad la "posición" que ha cambiado.

Luego, de acuerdo con las regulaciones de los lenguajes de programación convencionales, las variables miembro de la clase ya no se pueden asignar en la memoria de pila del hilo, sino que se deben asignar en la memoria de pila pública. De hecho, la "ubicación" de la variable en la memoria ha cambiado, de un área privada a un área pública. Por lo tanto, también se presentan posibles riesgos de seguridad.

¿Cómo garantizar la seguridad de las cosas en las áreas públicas? La respuesta es, no lo agarre, todos tienen una participación. Supongamos que distribuyes agua mineral en la calle gratis y vienen 10,000 personas, pero solo tienes 1,000 botellas de agua, como puedes imaginar, te apresuraste y perdiste la escena. Pero si tienes 100.000 botellas de agua, todo el mundo ve que hay mucha agua, no te preocupes, harás fila una a una, porque la recibirás.

Si hay más cosas, naturalmente es inútil. Desde otra perspectiva, es seguro. Las bicicletas compartidas en la calle son muy seguras ahora, porque hay demasiadas, por todas partes, y todas tienen el mismo aspecto, por lo que hasta los saboteadores se han rendido. Entonces, para hacer algo seguro, cópielo como loco.

De regreso al programa, para hacer que los datos en el área pública apilen la memoria de forma segura para cada hilo, luego cada hilo lo copia y cada hilo solo procesa su propia copia sin afectar a los demás. Creo que lo has adivinado, lo que quiero expresar es la clase ThreadLocal.

class StudentAssistant {
    
    

    ThreadLocal<String> realName = new ThreadLocal<>();
    ThreadLocal<Double> totalScore = new ThreadLocal<>();

    String determineDegree() {
    
    
        double score = totalScore.get();
        if (score >= 90) {
    
    
            return "A";
        }
        if (score >= 80) {
    
    
            return "B";
        }
        if (score >= 70) {
    
    
            return "C";
        }
        if (score >= 60) {
    
    
            return "D";
        }
        return "E";
    }

    double determineOptionalcourseScore() {
    
    
        double score = totalScore.get();
        if (score >= 90) {
    
    
            return 10;
        }
        if (score >= 80) {
    
    
            return 20;
        }
        if (score >= 70) {
    
    
            return 30;
        }
        if (score >= 60) {
    
    
            return 40;
        }
        return 60;
    }
}

Esta clase de asistente de estudiante tiene dos variables miembro, realName y totalScore, ambas de tipo ThreadLocal. Cada hilo hará una copia y la almacenará localmente en tiempo de ejecución.

Un hilo está ejecutando "Zhang San" y "90", luego estos dos datos "Zhang San" y "90" se almacenan en las variables miembro del objeto de hilo A (un objeto de instancia de la clase Thread). Suponiendo que el hilo B también se está ejecutando en este momento, es "Li Si" y "85", entonces los dos datos "Li Si" y "85" se almacenan en las variables miembro del objeto del hilo B (objeto de instancia de la clase Thread) Up.

La clase de hilo (Thread) tiene una variable miembro, similar al tipo de mapa, que se utiliza específicamente para almacenar datos ThreadLocal. En términos de afiliación lógica, estos datos ThreadLocal pertenecen al nivel de variable miembro de la clase Thread. Desde la perspectiva de la "ubicación", estos datos ThreadLocal se asignan en la memoria del montón en el área pública.

Para decirlo sin rodeos, es copiar N copias de un dato en la memoria del montón, y cada subproceso reclama 1 copia, y al mismo tiempo se estipula que cada subproceso solo puede reproducir su propia parte y no se le permite hacerlo. afectar a otros.

Cabe señalar que estos N datos todavía se almacenan en la memoria del montón del área pública. El "hilo local" que se escucha a menudo es en términos de subordinación lógica. Estos datos y hilos se corresponden uno a uno, como si se han convertido en el hilo en sí mismo "Territorio". De hecho, desde la perspectiva de la "ubicación" de los datos, todos están ubicados en la memoria del montón pública, pero solo los reclama el hilo. Quiero enfatizar este punto.

De hecho, es como una bicicleta compartida en la calle. Resultó que solo había uno, todos se apresuraban a montar y siempre había problemas. Ahora copia N autos de este, uno para cada persona, cada uno viaja el suyo, el problema está resuelto. Las bicicletas compartidas son datos y ustedes son hilos. Durante el recorrido, la bicicleta lógicamente te pertenece, pero en cuanto a su ubicación sigue estando en el área pública de la calle, pues te encuentras con que en la puerta de cada comunidad se encuentra colocado "bicicletas compartidas, no entrada". Jajajaja.

¿Las bicicletas compartidas son muy similares a ThreadLocal? Para reiterar, ThreadLocal es copiar una pieza de datos N copias, cada hilo reclama una copia, cada uno reproduce la suya, sin afectar a los demás.

Solo puedo ver, no tocar

Las cosas colocadas en áreas públicas solo tienen riesgos de seguridad potenciales, no necesariamente inseguras. Algunas cosas se guardan en áreas públicas, pero también son muy seguras. Por ejemplo, si pones una estatua de piedra de cientos de toneladas en la calle, es muy seguro, porque no todos pueden moverla.

Otro ejemplo es cuando vas de viaje, a menudo encuentras algunas cosas preciosas, que estarán rodeadas de vallas de hierro con un letrero que dice "Solo ver, no tocar". Por supuesto, puede ser un poco internacional, "solo mira, no toques". Esto también es muy seguro, porque es imposible quedar mal con solo unas miradas.

Volviendo al programa, este es el caso, que solo se puede leer pero no modificar. De hecho, son constantes o variables de solo lectura, son seguras para subprocesos múltiples y no se pueden cambiar si lo desea.

class StudentAssistant {
    
    

    final double passScore = 60;
}

Por ejemplo, establezca el puntaje de aprobación en 60 puntos y agregue una final al frente, de modo que todos los hilos no puedan moverlo. Esto es muy seguro.

Aquí hay una pequeña sección: las tres soluciones anteriores son en realidad "trucos".

La primera es buscar un lugar que solo tú conozcas y esconder, claro que es seguro.

El segundo tipo es hacer una copia para cada persona, y jugar la suya, sin afectar a los demás, y por supuesto es seguro.

El tercero, más severo, estipula directamente que solo se puede leer y está prohibido modificarlo, por supuesto que es seguro.

¿Están todos tratando de evitar lo pesado y lo ligero? ¿Qué debemos hacer si ninguno de estos tres métodos puede resolverlo? No te preocupes, sigue leyendo.

No hay reglas, entonces preconcebidas

Los tres esquemas dados arriba están un poco "idealizados". La realidad es realmente muy caótica y ruidosa, sin reglas.

Por ejemplo, vas a un restaurante a cenar durante el mediodía y te das cuenta de que solo queda una mesa vacía después de entrar por la puerta. Primero quieres hacer el pedido y sentarte aquí cuando regreses. Cuando regresa después de ordenar la comida, descubre que alguien más ha tomado la iniciativa.

Como la mesa es un artículo que pertenece al área pública, cualquiera puede sentarse, por lo que quien la agarre primero puede sentarse. Aunque lo hayas visto una vez entre la multitud, no recordará tu rostro.

No necesito hablar sobre la solución, deja que una persona mire el asiento mientras los demás piden la comida. De esta forma, cuando vuelvan los demás, puedes decir con seguridad: "Disculpe, este asiento, ya lo he ocupado".

Creo de nuevo que inteligente, has adivinado lo que te voy a decir, sí, es cerraduras (exclusión mutua).

De vuelta en el programa, si los datos en el área pública (memoria de pila) van a ser manipulados por múltiples subprocesos, para garantizar la seguridad (o consistencia) de los datos, debe colocar un candado junto a los datos. desea manipular los datos, consígalos primero. Hablemos del bloqueo.

Suponga que un hilo llega al frente para mirar los datos y encuentra que el candado está libre y nadie lo retiene. Entonces consiguió el bloqueo y luego comenzó a manipular los datos, trabajó durante un tiempo y, cuando se cansó, se quedó en reposo.

En ese momento, llegó otro hilo y encontró que el candado estaba en manos de otra persona, que de acuerdo con la normativa no podía manipular los datos porque no podía obtener el candado. Por supuesto, puede optar por esperar o darse por vencido y pasar a otras cosas.

La razón por la que el primer hilo se atrevió a dormirse es porque tiene un candado en la mano y es imposible que otros hilos manipulen los datos. Cuando regresa y continúa manipulando los datos, se puede liberar el bloqueo. El candado vuelve al estado inactivo nuevamente y otros subprocesos pueden tomar el candado. Es quien obtiene el bloqueo primero y quien opera los datos.

class ClassAssistant {
    
    

    double totalScore = 60;
    final Lock lock = new Lock();

    void addScore(double score) {
    
    
        lock.obtain();
        totalScore += score;
        lock.release();
    }

    void subScore(double score) {
    
    
        lock.obtain();
        totalScore -= score;
        lock.release();
    }
}

Suponiendo que el puntaje inicial de una clase es de 60 puntos, esta clase atrae a 10 estudiantes para que participen en 10 programas de respuesta diferentes al mismo tiempo. Cada estudiante obtiene 5 puntos por la clase si lo hace bien y 5 puntos por la respuesta incorrecta. . Debido a que 10 estudiantes están trabajando juntos, esta debe ser una situación concurrente.

Por lo tanto, los dos métodos de sumar y restar puntos se denominan simultáneamente y funcionan juntos para manipular la puntuación total. Para garantizar la coherencia de los datos, es necesario adquirir el bloqueo antes de cada operación y liberarlo una vez finalizada la operación.

Cree que el mundo está lleno de amor, aunque esté herido

Volviendo al ejemplo del principio, si todavía gastas 10.000 yuanes en el suelo, ¿lo perderás? Depende de la situación, si estás en una ciudad donde la gente va y viene, puedes decir que definitivamente lo perderás. Si corres hacia la tierra de nadie y la arrojas al suelo, puedes decir que definitivamente no la arrojarás.

Se puede ver que todas las cosas se ponen en espacios públicos sin protección, pero los resultados son muy diferentes. Esto muestra que los problemas de seguridad también están relacionados con las condiciones ambientales en las áreas públicas.

Por ejemplo, pongo los datos en la memoria del montón en el área pública, pero siempre habrá un solo hilo, que es un modelo de un solo hilo, entonces los datos están definitivamente seguros.

Además, si dos subprocesos operan con los mismos datos y 200 subprocesos operan con los mismos datos, la probabilidad de seguridad de estos datos es completamente diferente. Es cierto que cuantos más subprocesos haya, mayor será la probabilidad de que los datos no sean seguros, y cuantos menos subprocesos, menor será la probabilidad de que los datos no sean seguros. Tome un caso límite, es decir, solo hay 1 subproceso, entonces la probabilidad insegura es 0, que es segura.

Tal vez hayas adivinado lo que quiero expresar de nuevo, sí, es CAS. Quizás todo el mundo piense que, dado que la cerradura puede solucionar el problema, se puede utilizar ¿Por qué vuelve a aparecer un CAS?

Esto se debe a que la adquisición y liberación de bloqueos tiene un precio determinado. Si la cantidad de subprocesos es particularmente pequeña, es posible que no haya otros subprocesos para manipular datos en absoluto. En este momento, debe adquirir y liberar bloqueos. Es un desperdicio .

En respuesta a esta situación de "área grande y escasamente poblada", se propuso un método llamado CAS (Compare And Swap). Es decir, cuando la simultaneidad es pequeña, la probabilidad de que los datos se modifiquen accidentalmente es muy baja, pero existe esa posibilidad y en este momento se utiliza CAS.

Supongamos que un hilo manipula datos, ha realizado la mitad del trabajo, está cansado y quiere descansar. (Parece que el físico del hilo de hoy no es muy bueno). De modo que registra el estado actual de los datos (es decir, el valor de los datos) y se va a dormir a casa.

Después de despertar, planeo seguir trabajando, pero me preocupa que se modifiquen los datos, así que saco el estado de los datos guardados antes de acostarme y lo comparo con el estado actual de los datos. Si es el mismo, significa que los datos no se han modificado durante la suspensión. Después de mover (por supuesto, puede cambiarse a otro primero, y luego volver a cambiar, este es el problema de ABA), luego continúe haciéndolo. Si no es lo mismo, significa que los datos han sido modificados y las operaciones que se hicieron antes en realidad son inútiles, así que déjelo y comience de nuevo desde el principio.

Por lo tanto, el método CAS es adecuado para situaciones en las que la cantidad de simultaneidad no es alta, es decir, donde es menos probable que los datos se modifiquen accidentalmente. Si la cantidad de simultaneidad es alta, tus datos definitivamente serán modificados y tendrás que rendirte cada vez y luego empezar de nuevo. Esto te costará aún más. Es mejor bloquearlo directamente.

Aquí hay una explicación del problema ABA: si los datos son 5 antes de acostarse y 5 después de despertarse, no es seguro que los datos no hayan sido modificados. Quizás los datos se modificaron primero a 8 y luego de nuevo a 5, pero no lo sabe. Este problema es realmente muy fácil de resolver, solo agregue un campo de número de versión y estipule que mientras se modifiquen los datos, el número de versión debe aumentarse en 1.

De esta forma, el dato antes de irse a la cama es 5 y el número de versión es 0, y después de despertarse, el dato es 5 y el número de versión es 0, lo que indica que el dato no ha sido modificado. Si los datos son 5 y el número de versión es 2, significa que los datos se han cambiado dos veces, primero cambia a otro y luego vuelve a cambiar a 5.

Creo de nuevo que has descubierto que las cerraduras inteligentes aquí son en realidad cerraduras optimistas La adquisición y liberación de cerraduras en el esquema anterior son en realidad cerraduras pesimistas. Optimistic Lock es optimista, que es asumir que mis datos no serán modificados accidentalmente. Si se modifican, me rendiré y comenzaré de nuevo. El bloqueo pesimista adopta una actitud pesimista, es decir, asumiendo que mis datos se modificarán accidentalmente, entonces se pueden bloquear directamente.

Supongo que te gusta

Origin blog.csdn.net/lingshengxueyuan/article/details/111476564
Recomendado
Clasificación