java线程安全如何实现?有哪些实现方法?

KLQ 2020-05-12 15:47:55 java常见问答 9387

你知道java的线程安全应该如何去实现吗?java线程安全的实现方式又有哪些呢?下面就和小编一起来看看具体的实现方法吧!

一个程序在运行起来的时候,会转换成进程,一般来说,会含有很多个线程。

一般的情况下,在一个进程当中,那些比较费时的操作,经常会采用多线程来进行解决。

例如:

生活当中经常可见的银行取钱、售票等,经常就会涉及到并发的问题,那么这个时候,往往就要用到多线程技术。

在进程当中,有多个并发线程进入一个重要数据的代码块的时候,在修改数据的过程当中,有很大的可能会引发线程安全的问题,从而引起了数据异常。

那么下面以售票问题作为例子,来演示线程安全的问题。

首先,在不对多线程数据进行保护的情况下会引发出来的状况

public class ThreadUnSecurity {
    
    static int tickets = 10;
    
    class SellTickets implements Runnable{

        @Override
        public void run() {
            // 未加同步时产生脏数据
            while(tickets > 0) {
                
                System.out.println(Thread.currentThread().getName()+"--->售出第:  "+tickets+" 票");
                tickets--;
                
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                
            }
            
            if (tickets <= 0) {
                
                System.out.println(Thread.currentThread().getName()+"--->售票结束!");
            }
        }
    }
    
    
    public static void main(String[] args) {
        
        
        SellTickets sell = new ThreadUnSecurity().new SellTickets();
        
        Thread thread1 = new Thread(sell, "1号窗口");
        Thread thread2 = new Thread(sell, "2号窗口");
        Thread thread3 = new Thread(sell, "3号窗口");
        Thread thread4 = new Thread(sell, "4号窗口");
        
        thread1.start();
        thread2.start();
        thread3.start();
        thread4.start();
        
        
    }
    

}

上面的代码的运行结果:

1号窗口--->售出第:  10 票
3号窗口--->售出第:  10 票
2号窗口--->售出第:  10 票
4号窗口--->售出第:  10 票
2号窗口--->售出第:  6 票
1号窗口--->售出第:  5 票
3号窗口--->售出第:  4 票
4号窗口--->售出第:  3 票
2号窗口--->售出第:  2 票
4号窗口--->售出第:  1 票
1号窗口--->售出第:  1 票
3号窗口--->售票结束!
2号窗口--->售票结束!
1号窗口--->售票结束!
4号窗口--->售票结束!

这个时候,我们可以看出,同一张票在不对票数进行保护的时候,会出现同一张票会被售出多次的情况。

因为线程调度中的不确定性,所以在演示上面的代码的时候,出现的运行结果会有不同。

1、实现线程安全的方式

同步代码块

package com.bpan.spring.beans.thread;

import com.sun.org.apache.regexp.internal.recompile;

public class ThreadSynchronizedSecurity {
    
    static int tickets = 10;
    
    class SellTickets implements Runnable{

        @Override
        public void run() {
            // 同步代码块
            while(tickets > 0) {
                
                synchronized (this) {
                    
//                    System.out.println(this.getClass().getName().toString());
                    
                    if (tickets <= 0) {
                        
                        return;
                    }
                    
                    System.out.println(Thread.currentThread().getName()+"--->售出第:  "+tickets+" 票");
                    tickets--;
                    
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                
                if (tickets <= 0) {
                    
                    System.out.println(Thread.currentThread().getName()+"--->售票结束!");
                }
            }
        }
    }
    
    
    public static void main(String[] args) {
        
        
        SellTickets sell = new ThreadSynchronizedSecurity().new SellTickets();
        
        Thread thread1 = new Thread(sell, "1号窗口");
        Thread thread2 = new Thread(sell, "2号窗口");
        Thread thread3 = new Thread(sell, "3号窗口");
        Thread thread4 = new Thread(sell, "4号窗口");
        
        thread1.start();
        thread2.start();
        thread3.start();
        thread4.start();
        
        
    }
    

}

输出的结果可以自行调试。

2、实现线程安全的方式

同步方法

package com.bpan.spring.beans.thread;

public class ThreadSynchroniazedMethodSecurity {
    
    
    static int tickets = 10;
    
    class SellTickets implements Runnable{

        @Override
        public void run() {
            //同步方法
            while (tickets > 0) {
                
                synMethod();
                
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                
                if (tickets<=0) {
                    
                    System.out.println(Thread.currentThread().getName()+"--->售票结束");
                }
                
            }
            
            
        }
        
        synchronized void synMethod() {
            
            synchronized (this) {
                if (tickets <=0) {
                    
                    return;
                }
                
                System.out.println(Thread.currentThread().getName()+"---->售出第 "+tickets+" 票 ");
                tickets-- ;
            }
            
        }
        
    }
    public static void main(String[] args) {
        
        
        SellTickets sell = new ThreadSynchroniazedMethodSecurity().new SellTickets();
        
        Thread thread1 = new Thread(sell, "1号窗口");
        Thread thread2 = new Thread(sell, "2号窗口");
        Thread thread3 = new Thread(sell, "3号窗口");
        Thread thread4 = new Thread(sell, "4号窗口");
        
        thread1.start();
        thread2.start();
        thread3.start();
        thread4.start();
        
    }

}

输出结果可以自行调试。

3、实现线程安全的方式

Lock锁机制,通过创建Lock对象,采用lock()加锁,unlock()解锁,来保护指定的代码块

package com.bpan.spring.beans.thread;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class ThreadLockSecurity {
    
    static int tickets = 10;
    
    class SellTickets implements Runnable{
        
        Lock lock = new ReentrantLock();

        @Override
        public void run() {
            // Lock锁机制
            while(tickets > 0) {
                
                try {
                    lock.lock();
                    
                    if (tickets <= 0) {
                        
                        return;
                    }
                        
                    System.out.println(Thread.currentThread().getName()+"--->售出第:  "+tickets+" 票");
                    tickets--;
                } catch (Exception e1) {
                    // TODO Auto-generated catch block
                    e1.printStackTrace();
                }finally {
                    
                    lock.unlock();
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
                
            if (tickets <= 0) {
                
                System.out.println(Thread.currentThread().getName()+"--->售票结束!");
            }
            
        }
    }
    
    
    public static void main(String[] args) {
        
        
        SellTickets sell = new ThreadLockSecurity().new SellTickets();
        
        Thread thread1 = new Thread(sell, "1号窗口");
        Thread thread2 = new Thread(sell, "2号窗口");
        Thread thread3 = new Thread(sell, "3号窗口");
        Thread thread4 = new Thread(sell, "4号窗口");
        
        thread1.start();
        thread2.start();
        thread3.start();
        thread4.start();
        
        
    }
    

}

以上的三种实现线程安全的方式你都了解了吗?更多相关知识,可以继续关注奇Q工具网的java架构师栏目来了解呢!

推荐阅读:

java线程,java多线程详解

高并发和多线程的区别在哪里?都有什么概念?

java多线程使用场景有哪些?有何作用?