并发编程之线程安全

强颜欢笑 提交于 2019-12-13 22:09:08

什么是线程安全

当多个线程同时共享,同一个全局变量或静态变量,做写的操作时,可能会发生数据冲突问题,也就是线程安全问题。但是做读操作是不会发生数据冲突问题。

public class ThreadTrain implements Runnable {
	private int trainCount = 100;

	@Override
	public void run() {
		while (trainCount > 0) {
			try {
				Thread.sleep(50);
			} catch (Exception e) {

			}
			sale();
		}
	}

	public void sale() {
		if (trainCount > 0) {
			System.out.println(Thread.currentThread().getName() + ",出售第" + (100 - trainCount + 1) + "张票");
			trainCount--;
		}
	}

	public static void main(String[] args) {
		ThreadTrain threadTrain = new ThreadTrain();
		Thread t1 = new Thread(threadTrain, "①号");
		Thread t2 = new Thread(threadTrain, "②号");
		t1.start();
		t2.start();
	}

}

执行上述代码,本来我们计划只出售100百张票,实际却出售了200张票,这就是多线程并发对共享资源竞争导致
多个线程共享同一个全局成员变量时,做写的操作可能会发生数据冲突问题。
执行结果

线程安全解决办法

如何解决多线程的之间的线程安全问题呢? 多线程之间同步synchronized或使用锁(lock)
针对可能发生的数据冲突问题,使用synchronized 或者锁机制,可以使的只有一个线程执行,代码执行完成后便释放锁,然后才能让其它线程执行

内置的锁

Java提供了一种内置的锁机制来支持原子性
每一个Java对象都可以用作一个实现同步的锁,称为内置锁,线程进入同步代码块之前自动获取到锁,代码块执行完成正常退出或代码块中抛出异常退出时会释放掉锁
内置锁为互斥锁,即线程A获取到锁后,线程B阻塞直到线程A释放锁,线程B才能获取到同一个锁
内置锁使用synchronized关键字实现,synchronized关键字有两种用法:
1.修饰需要进行同步的方法(所有访问状态变量的方法都必须进行同步),此时充当锁的对象为调用同步方法的对象
2.同步代码块和直接使用synchronized修饰需要同步的方法是一样的,但是锁的粒度可以更细,并且充当锁的对象不一定是this,也可以是其它对象,所以使用起来更加灵活

同步代码块synchronized

就是将可能会发生线程安全问题的代码,给包括起来。
synchronized(同一个数据){
 可能会发生线程冲突问题
}
就是同步代码块 
synchronized(对象)//这个对象可以为任意对象 
{ 
    需要被同步的代码 
} 

对象如同锁,持有锁的线程可以在同步中执行
没持有锁的线程即使获取CPU的执行权,也进不去
同步的前提:
1,必须要有两个或者两个以上的线程
2,必须是多个线程使用同一个锁
必须保证同步中只能有一个线程在运行
好处:解决了多线程的安全问题
弊端:多个线程需要判断锁,较为消耗资源、抢锁的资源。

public void sale() {
		synchronized (this) {
			if (trainCount > 0) {
				System.out.println(Thread.currentThread().getName() + ",出售第" + (100 - trainCount + 1) + "张票");
				trainCount--;
			}
		}
	}

对象锁

对象锁有两种,一种是在代码块中使用this对象,一种的在非静态方法中使用sychronized,它们使用的是同一把锁

public class TestSynchronized {
    public void test1() {
        synchronized (this) { //代码块上的锁使用this对象
            int i = 5;
            while (i-- > 0) {
                System.out.println(Thread.currentThread().getName() + " : " + i);
                try {
                    Thread.sleep(500);
                } catch (InterruptedException ie) {
                }
            }
        }
    }
 
    public synchronized void test2() { // 在非静态方法中使用的锁
        int i = 5;
        while (i-- > 0) {
            System.out.println(Thread.currentThread().getName() + " : " + i);
            try {
                Thread.sleep(500);
            } catch (InterruptedException ie) {
            }
        }
    }
 
    public static void main(String[] args) {
        final TestSynchronized myt2 = new TestSynchronized();
        Thread test1 = new Thread(new Runnable() {
            public void run() {
                myt2.test1();
            }
        }, "test1");
        Thread test2 = new Thread(new Runnable() {
            public void run() {
                myt2.test2();
            }
        }, "test2");
        test1.start();
        test2.start();
//         TestRunnable tr=new TestRunnable(); 
//         Thread test3=new Thread(tr); 
//         test3.start(); 
    }
 
}

类锁

类锁同样也有两种,一种是在公共静态方法中使用sychroized关键字,一种是在代码块中使用class类锁.

package com.watchdata.Thread;

public class TestSynchronized {
    public  void test1() {
        synchronized (TestSynchronized.class) { // 在代码块中使用类锁.
            int i = 5;
            while (i-- > 0) {
                System.out.println(Thread.currentThread().getName() + " : " + i);
                try {
                    Thread.sleep(500);
                } catch (InterruptedException ie) {
                }
            }
        }
    }
 
    public static synchronized void test2() { // 在公共静态方法中使用类锁
        int i = 5;
        while (i-- > 0) {
            System.out.println(Thread.currentThread().getName() + " : " + i);
            try {
                Thread.sleep(500);
            } catch (InterruptedException ie) {
            }
        }
    }
 
    public static void main(String[] args) {
        final TestSynchronized myt2 = new TestSynchronized();
        Thread test1 = new Thread(new Runnable() {
            public void run() {
                myt2.test1();
            }
        }, "test1");
        Thread test2 = new Thread(new Runnable() {
            public void run() {
                TestSynchronized.test2();
            }
        }, "test2");
        test1.start();
        test2.start();
//         TestRunnable tr=new TestRunnable(); 
//         Thread test3=new Thread(tr); 
//         test3.start(); 
    }
 
}

二者关系

对象锁和类锁并不是同一把锁,控制着不同的区域,它们是互不干扰的。同样,线程获得对象锁的同时,也可以获得该类锁,即同时获得两个锁,这是允许的。

线程死锁

何谓死锁,这里一般都会谈到了哲学家吃饭的问题,某个线程拿到了A的锁,再等待B的锁,而另一个 线程却拿到了B的锁,在等待A的锁,这时两个线程就构成了死锁.

避免死锁的办法,取得一个锁后,释放完当前锁,才去获得另一个锁.

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!