博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
synchronized关键字
阅读量:3959 次
发布时间:2019-05-24

本文共 4790 字,大约阅读时间需要 15 分钟。

“线程安全”是一个老生常谈的话题,但是对线程安全比较规范的定义却不是一件容易的事。

Brian Goetz是这样描述线程安全的:

当多个线程同时访问同一个对象时,如果不用考虑这些线程在运行时环境下的调度和交替执行,也不需要额外的同步,或者在调用方法进行其它的协调操作,调用这个对象的行为都可以获得正确的结果,那么就称这个对象是安全的。

简单说:但多个线程同时访问同一个类(对象、方法)的时候,可以得到预期的结果,那么这就是线程安全的。

看一段代码:

public class Xiaoping {
//定义一个共享的变量 public static int num = 0; public static void main(String[] args) {
//创建三个线程,来进行变量的累加 T1 t1 = new T1(); T1 t2 = new T1(); T1 t3 = new T1(); //启动线程 t1.start(); t2.start(); t3.start(); try {
//三个线程结束后,打印结果 t1.join(); t2.join(); t3.join(); } catch (InterruptedException e) {
e.printStackTrace(); } //打印结果 System.out.println(Xiaoping.num); }}//自定义一个线程类class T1 extends Thread{
@Override public void run() {
for (int i = 0; i < 10000; i++){
Xiaoping.num ++; } }}//分析按照我们的预期,每个线程进行10000的累加,那么三个线程执行结束后就是30000但是:实际结果却有可能不是30000,这就表明该线程是不安全的!!!

引发线程安全问题的主要原因:

1.存在共享数据
2.多线程同时操作共享数据

线程安全解决思路

我们既然知道线程安全问题的诱因是1.存在共享数据2.多个线程同时访问共享数据。那么我们就可以从这个思路出发

保证同一时刻只有一个线程去操作共享变量:当一个线程去操作共享数据时,其它的线程必须等待至该线程执行结束后才能执行。这就是我们常说的互斥锁!互斥锁简单说就是互斥访问的锁,它可以做到互斥访问,即一个线程操作共享数据时,其它线程是不能操作共享数据的,只有该线程释放锁之后,其它线程才可以访问共享数据。

Synchronized关键字

Synchronized关键字可以用于解决线程安全。它可以保证同一时刻只有一个线程去执行方法或者代码块(主要是线程中共享变量的操作)。还有一个作用就是可以保证线程变化对其它线程的可见性(共享变量的可见性)完全可以替代volatile关键字

问题?

上述存在线程安全问题的代码怎么修改

public static volatile int num = 0;

千万注意,上述的修改是不能保证线程安全的,volatile关键字不能保证线程安全的!!!

我们可以使用synchronized关键字来进行线程安全问题的解决

@Override    public synchronized void run() {
//使用synchronized关键字来避免线程安全问题 for (int i = 0; i < 10000; i++){
Xiaoping.num ++; } }

synchronized关键字的使用

1.修饰实例方法,作用于当前实例,调用实例方法时首先获取该实例的锁

2.修饰静态方法,作用于类的Class对象,进入修饰的静态方法前需要先获取类的Class对象的锁

3.修饰代码块,需要指定加锁对象(记做lockobj),在进入同步代码块前需要先获取lockobj的锁

代码分别对synchronized关键字使用的演示

1.synchronized关键字修饰实例方法,这里的实例方法是实例对象的方法,而不是静态方法

public class SynchronizedTest {
public static void main(String[] args) {
Person person = new Person(); Demo d1 = new Demo(person); Demo d2 = new Demo(person); d1.start(); d2.start(); try {
d1.join(); d2.join(); } catch (InterruptedException e) {
e.printStackTrace(); } System.out.println(person.num); }}class Demo extends Thread{
private Person person; public Demo(){
} public Demo(Person person){
this.person = person; } @Override public void run() {
for(int i = 0; i < 10000; i++){
this.person.add(); } }}class Person {
int num = 0; //synchronized修饰的实例方法 public synchronized void add(){
num ++; }}

synchronized修饰实例方法注意事项:

1.多个线程操作的是同一个实例

2.同一个实例的多个实例方法用synchronized关键字修饰

2.synchronized关键字静态方法,这里的锁对象就是类的Class对象

public class SynchronizedTest2 {
public static void main(String[] args) {
T t1 = new T(); T t2 = new T(); T t3 = new T(); t1.start(); t2.start(); t3.start(); try {
t1.join(); t2.join(); t3.join(); } catch (InterruptedException e) {
e.printStackTrace(); } System.out.println(Student.num); }}class Student{
static int num = 0; //使用synchronized关键字修饰静态方法 public static synchronized void add(){
num ++; }}class T extends Thread{
@Override public void run() {
for (int i = 0; i < 10000; i++) {
Student.add(); } }}

修饰静态方法时,此时的锁对象是Class对象

上述可以这样转换:

class Student{
static int num = 0; public static void add(){
synchronized (Student.class){
num ++; } }}

3.synchronized关键字修饰同步代码块

我们知道synchronized关键字可以修饰静态方法和实例方法。但是存在一个问题就是他么都是在执行方法前获取指定对象的锁,那么存在一个问题就是有的方法方法体很大,内容比较多,而需要保证线程安全的代码又比较少,这样是比较浪费性能的,因此我们可以使用同步代码块,来对同步代码快中的内容进行加锁。

在演示代码1的基础上使用同步代码块保证线程安全

public class SynchronizedTest {
public static void main(String[] args) {
Person person = new Person(); Demo d1 = new Demo(person); Demo d2 = new Demo(person); d1.start(); d2.start(); try {
d1.join(); d2.join(); } catch (InterruptedException e) {
e.printStackTrace(); } System.out.println(person.num); }}class Demo extends Thread{
private Person person; public Demo(){
} public Demo(Person person){
this.person = person; } @Override public void run() {
for(int i = 0; i < 10000; i++){
this.person.add(); } }}class Person {
int num = 0; public void add(){
synchronized (this){
//这里使用的是同步代码快 num ++; } }}

转载地址:http://ojxzi.baihongyu.com/

你可能感兴趣的文章
东北赛选拔教训
查看>>
hash
查看>>
涨姿势了:求两个分子的最大公倍数
查看>>
快速幂
查看>>
vector.reserve and resize &&vector与map结合
查看>>
最短路
查看>>
最长公共子序列
查看>>
计算几何
查看>>
求解方程
查看>>
太弱了。。水题
查看>>
位运算(含应用)
查看>>
野指针与空指针
查看>>
图文混排效果
查看>>
urllib2.urlopen超时问题
查看>>
Choosing a Machine Learning Classifier
查看>>
魏兴国:深入浅出DDoS攻击防御
查看>>
使连续的参考文献能够中间用破折号连起来
查看>>
Discover Feature Engineering, How to Engineer Features and How to Get Good at It
查看>>
36辆车,6条跑道,无计时器,最少几次比赛可以选出前三
查看>>
matlab2012b与matlab7.1执行set(gca,'Yscale','log')之后画到的直方图结果居然不同
查看>>