JAVA多线程安全
Posted 卡尼慕
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JAVA多线程安全相关的知识,希望对你有一定的参考价值。
使用多线程同时操作一个数据容易产生安全隐患,例如多窗口售票问题,票数固定,而同时多个窗口一起售票,容易导致最后数据出问题。
解决安全隐患:当一个线程进入数据操作的时候,无论是否休眠,其他线程只能等待。
JAVA程序提供技术称为同步技术。
公式:synchronized(任意的对象){
线程要操作的共享数据
}
这给写法称为同步代码块!
同步对象:任意对象,同步锁,对象监视器。
同步保证安全性:没有锁的线程不能执行,只能等待。
同步代码块中的锁对象可以是任意的对象;但多个线程时,要使用同一个锁对象才能够保证线程安全。
在同步中的线程不出同步,不会释放锁。
没有锁的线程,不能进入同步。
另外一种方式是同步方法:
public synchronized void method(){
可能会产生线程安全问题的代码
}
同步方法也有锁,同步方法中的对象锁,是本类对象引用this。
静态方法中,同步也有锁,这里的锁不是this,锁是本类自己(类名.class)。
public static synchronized void method(){
可能会产生线程安全问题的代码
}
死锁
当线程任务中出现了多个同步(多个锁)时,如果同步中嵌套了其他的同步。这时容易引发一种现象:程序出现无限等待,这种现象我们称为死锁。
这里就尝试让A,B锁相互嵌套,产生死锁。
1public class LockA {
2 private LockA(){}
3
4 public static final LockA locka = new LockA();
5}
1public class LockB {
2 private LockB(){}
3
4 public static final LockB lockb = new LockB();
5}
1public class DeadLockDemo {
2 public static void main(String[] args) {
3 DeadLock dead = new DeadLock();
4 Thread t0 = new Thread(dead);
5 Thread t1 = new Thread(dead);
6 t0.start();
7 t1.start();
8 }
9}
1public class DeadLock implements Runnable{
2 private int i = 0;
3 public void run(){
4 while(true){
5 if(i%2==0){
6 //先进入A同步,再进入B同步
7 synchronized(LockA.locka){
8 System.out.println("if...locka");
9 synchronized(LockB.lockb){
10 System.out.println("if...lockb");
11 }
12 }
13 }else{
14 //先进入B同步,再进入A同步
15 synchronized(LockB.lockb){
16 System.out.println("else...lockb");
17 synchronized(LockA.locka){
18 System.out.println("else...locka");
19 }
20 }
21 }
22 i++;
23 }
24 }
25}
Lock接口
Lock 实现提供了比使用 synchronized 方法和语句可获得的更广泛的锁定操作。就是把可能会产生线程安全问题的代码上锁的过程拆分开了,前面获取锁(调用方法lock()),执行完后释放锁(unlock())。
1Lock lock = new ReentrantLock();
2lock.lock();
3try {
4 。。。
5} finally {
6 lock.unlock();
7}
1public interface Lock {
2 //获取锁,调用该方法将会获取锁,当锁获取后,从该方法返回
3 void lock();
4 //可中断地获取锁,和lock()方法的不同之处在于该方法会响应中断,即在锁的获取过程中可以中断当前线程
5 void lockInterruptibly() throws InterruptedException;
6 //尝试非阻塞的获取锁,调用该方法后会立刻返回,如果能够获取则返回true,否则返回false
7 boolean tryLock();
8 //超时地获取锁 1、当前线程在超时时间内成功获取锁。2、当前线程在超时时间内被中断。3、超时时间结束返回false。
9 boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
10 //释放锁
11 void unlock();
12 //获取等待通知组件
13 Condition newCondition();
14}
等待唤醒机制
线程之间的通信:多个线程在处理同一个资源,但是处理的动作(线程的任务)却不相同。
通过一定的手段使各个线程能有效的利用资源。而这种手段即—— 等待唤醒机制。
常见的涉及到的方法:
1、wait() :等待,将正在执行的线程释放其执行资格 和 执行权,并存储到线程池中。
2、notify():唤醒,唤醒线程池中被wait()的线程,一次唤醒一个,而且是任意的。
3、notifyAll(): 唤醒全部:可以将线程池中的所有wait() 线程都唤醒。
这里的唤醒实际上是让线程池中的线程具有执行代码的资格。并且这些代码都是在同步中才有效,这些方法在使用时必须表明所属锁(也就是用锁对象调用)。也因此,这些方法并不是定义在Thread类中,而是定义在Object中,因为所属锁可以是任意对象。
1/*
2 * 定义资源类,有2个成员变量
3 * name,sex
4 * 同时有2个线程,对资源中的变量操作
5 * 1个对name,age赋值
6 * 2个对name,age做变量的输出打印
7 */
8public class Resource {
9 public String name;
10 public String sex;
11 public boolean flag = false;
12}
13
14/*
15 * 输入的线程,对资源对象Resource中成员变量赋值
16 * 一次赋值 张三,男
17 * 下一次赋值 lisi,nv
18 */
19public class Input implements Runnable {
20 private Resource r ;
21
22 public Input(Resource r){
23 this.r = r;
24 }
25
26 public void run() {
27 int i = 0 ;
28 while(true){
29 synchronized(r){
30 //标记是true,等待
31 if(r.flag){
32 try{r.wait();}catch(Exception ex){}
33 }
34
35 if(i%2==0){
36 r.name = "张三";
37 r.sex = "男";
38 }else{
39 r.name = "lisi";
40 r.sex = "nv";
41 }
42 //将对方线程唤醒,标记改为true
43 r.flag = true;
44 r.notify();
45 }
46 i++;
47 }
48 }
49
50}
51
52/*
53 * 输出线程,对资源对象Resource中成员变量,输出值
54 */
55public class Output implements Runnable {
56 private Resource r ;
57
58 public Output(Resource r){
59 this.r = r;
60 }
61 public void run() {
62 while(true){
63 synchronized(r){
64 //判断标记,是false,等待
65 if(!r.flag){
66 try{r.wait();}catch(Exception ex){}
67 }
68 System.out.println(r.name+".."+r.sex);
69 //标记改成false,唤醒对方线程
70 r.flag = false;
71 r.notify();
72 }
73 }
74 }
75}
76
77
78public class ThreadDemo{
79 public static void main(String[] args) {
80
81 Resource r = new Resource();
82
83 Input in = new Input(r);
84 Output out = new Output(r);
85
86 Thread tin = new Thread(in);
87 Thread tout = new Thread(out);
88
89 tin.start();
90 tout.start();
91 }
92}
总结
1、线程同步的方法:同步代码块、同步方法。同步代码块的锁对象可以是任意的对象,同步方法中的锁对象是 this,静态同步方法中的锁对象是 类名.class。
2、多线程的实现:继承Thread类、实现Runnable接口、通过线程池实现Callable接口。
3、run()与start()
start: 启动线程,并调用线程中的run()方法
run : 执行该线程对象要执行的任务
4、sleep()与wait()
sleep: 不释放锁对象, 释放CPU使用权,在休眠的时间内,不能唤醒。
wait(): 释放锁对象, 释放CPU使用权,在等待的时间内,能唤醒。
5、wait()、notify()必须是锁对象调用!!!
以上是关于JAVA多线程安全的主要内容,如果未能解决你的问题,请参考以下文章