Java's thread-safe

What is thread safe?

If there are multiple threads running at the same time, and these threads may run this code at the same time. The results of each program and run single-threaded operating results are the same, and also the values ​​of other variables and expectations are the same, that is thread-safe.

What is a security thread?

Through a case, security issues demonstrate thread: movie theater to sell tickets, we simulate the process of selling tickets cinema. Suppose you want to play the movie is "baby gourd war Altman," The cinema seating a total of 100 (this movie can only sell 100 tickets). We have to simulate a movie theater ticket window to achieve multiple windows simultaneously selling "baby gourd war Altman" This movie tickets (100 tickets that sell multiple windows together) need window, using thread object to simulate; for tickets, Runnable Interface subclass simulated.

Analog votes:
package demo03ThreadSafe;

public  class RunnableImpl the implements the Runnable {
     // definition of a plurality of threads share votes 
    Private  int Ticket = 10 ;

    // Set the thread task: selling tickets 
    @Override
     public  void RUN () {
         // use an infinite loop, so that the ticket operation is repeated 
        the while ( to true ) {
             // first determine whether there are votes 
            IF (Ticket> 0 ) {
                 the try {
                     / / first determine whether there is the ticket 
                    the Thread.sleep (1000 );
                } catch (InterruptedException e) {
                    e.printStackTrace ();
                }
                // votes present, selling tickets ticket-- 
                System.out.println ( "Thread name:" + Thread.currentThread () getName ( ) + " is selling" + ticket + "ticket." );
                ticket--;
            }
        }

    }
}
Test categories:
package demo03ThreadSafe;

public  class Demo01Ticket {
     public  static  void main (String [] args) {
         // create a Runnable interface implementation class object 
        RunnableImpl Runnable = new new RunnableImpl ();
         // Create Thread class object constructor implemented class object passing Runnable interface 
        Thread = Thread1 new new the Thread (Runnable, "window 1" );
        Thread thread2 = new Thread(runnable, "窗口2");
        Thread3 the Thread = new new the Thread (Runnable, "Window 3" );
         // call the start method to open multi-threaded 
        thread1.start ();
        thread2.start();
        thread3.start();

    }
}

Code Execution Results

You can find that program, there were two problems:
  1. The same number of votes, such as 5 this ticket was sold twice.
  2. Absence tickets, such as tickets and 0 -1 votes, does not exist.
This problem, several windows (threads) votes out of sync, and this problem is called thread-safe.

The cause of thread safety issues

If the result of the result of running multi-threaded and single-threaded program running is not the same, this is to have a multi-threaded thread-safety issues. Thread safety problems are caused by the global variables and static variables. If each thread to global variables, static variables only read, no write operation, in general, the global variable is thread-safe; if multiple threads simultaneously perform write operations generally need to consider thread synchronization, or else then it may affect the thread safety. Thread-safety issues can not produce, we can make a thread when accessing shared data, regardless of whether the loss of the execution of the CPU, so that other threads can only wait, wait for completion of the implementation of the current thread to shared data. Other threads to access shared data.

How to solve thread safety issues

When we use multiple threads access the same resources, and multiple threads write operations on the resource, thread-safety issues are likely to arise. To solve these security problems multithreaded concurrent access to a resource: that is, to solve the repeat vote and vote no problem, Java provides synchronization mechanisms (synchronized) to resolve. According to Case Description:
/*

Time window into the operation of the thread 1, 2 window and the window 3 thread can wait out the end of the operation window 1, window 1 and the window 2 and 3 have a chance to enter the code window
去执行。也就是说在某个线程修改共享资源的时候,其他线程不能修改该资源,等待修改完毕同步之后,才能去抢夺CPU
资源,完成对应的操作,保证了数据的同步性,解决了线程不安全的现象。

*/
为了保证每个线程都能正常执行原子操作,Java引入了线程同步机制。那么怎么去使用呢?有三种方式完成同步操作:
  • 同步代码块。
  • 同步方法。
  • 锁机制。

同步代码块

同步代码块: synchronized 关键字可以用于方法中的某个区块中,表示只对这个区块的资源实行互斥访问。
格式:

synchronized(锁对象){
  可能会出现线程安全问题的代码(访问了共享数据的代码)
}

注意:

  • 通过代码块中的锁对象,可以使用任意的对象
  • 但是必须保证多个线程使用的锁对象是同一个
  • 锁对象作用:把同步代码块锁住,只让一个线程在同步代码块中执行,在任何时候,最多允许一个线程拥有同步锁,谁拿到锁就进入代码块,其他的线程只能在外等着(BLOCKED)。
使用同步代码块模拟票
package demo04Synchronized;

public class Demo01Synchronized implements Runnable {

    //定义一个多个线程共享的票源
    private int ticket = 10;
    //定义同步锁
    Object obj = new Object();

    //设置线程任务:卖票
    @Override
    public void run() {
        //使用死循环,让卖票操作重复执行
        while (true) {
            //同步代码块
            synchronized (obj) {
                //先判断票是否存在
                if (ticket > 0) {
                    try {
                        //先判断票是否存在
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    //票存在,卖票 ticket--
                    System.out.println("线程名字是:" + Thread.currentThread().getName() + "正在卖" + ticket + "票");
                    ticket--;
                }
            }

        }

    }
}

测试类

package demo04Synchronized;


public class SynchronizedTest {
    public static void main(String[] args) {
        //创建Runnable接口的实现类对象
        Demo01Synchronized runnable = new Demo01Synchronized();
        //创建Thread类对象,构造方法中传递Runnable接口的实现类对象
        Thread thread1 = new Thread(runnable, "窗口1");
        Thread thread2 = new Thread(runnable, "窗口2");
        Thread thread3 = new Thread(runnable, "窗口3");
        //调用start方法开启多线程
        thread1.start();
        thread2.start();
        thread3.start();

    }
}

代码执行后的结果

同步技术的原理

使用了一个锁对象,这个锁对象叫同步锁,也叫对象锁,也叫对象监视器。多个线程一起抢夺CPU的执行权,谁抢到了锁对象,谁才能进入同步代码块中操作共享数据。没有抢夺到锁对象的线程即使拥有CPU的执行权也会进入线程阻塞状态。总结:同步中的线程,没有执行完毕不会释放锁,同步外的线程没有锁无法进入同步代码块,同步保证了只能有一个线程在同步中执行共享数据,保证了安全。但是程序会频繁的判断锁,获取锁,其执行效率会降低。

同步方法

同步方法:使用synchronized修饰的方法,就叫做同步方法,保证A线程执行该方法的时候,其他线程只能在方法外等着。
格式:

修饰符 synchronized 返回值类型 方法名(参数列表){
  可能会出现线程安全问题的代码(访问了共享数据的代码)
}

同步方法中的同步锁
  • 对于非static方法,同步锁就是this。
  • 对于static方法,我们使用当前方法所在类的字节码对象(类名.class)。

 使用同步方法模拟票

package demo05Method;


/*
    卖票案例出现了线程安全问题
    卖出了不存在的票和重复的票

    解决线程安全问题的二种方案:使用同步方法
    使用步骤:
        1.把访问了共享数据的代码抽取出来,放到一个方法中
        2.在方法上添加synchronized修饰符

    格式:定义方法的格式
    修饰符 synchronized 返回值类型 方法名(参数列表){
        可能会出现线程安全问题的代码(访问了共享数据的代码)
    }
 */
public class RunnableImpl implements Runnable {
    //定义一个多个线程共享的票源
    private static int ticket = 10;


    //设置线程任务:卖票
    @Override
    public void run() {
        //使用死循环,让卖票操作重复执行
        while (true) {
            //调用同步方法
            payTicketStatic();
        }
    }

    //定义同步方法
    public static synchronized void payTicketStatic() {

        //先判断票是否存在
        if (ticket > 0) {
            //提高安全问题出现的概率,让程序睡眠
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            //票存在,卖票 ticket--
            System.out.println(Thread.currentThread().getName() + "-->正在卖第" + ticket + "张票");
            ticket--;
        }
    }


}

定义测试类

package demo05Method;

public class MethodTest {
    public static void main(String[] args) {
        //创建Runnable接口的实现类对象
        RunnableImpl runnable = new RunnableImpl();
        //创建Thread类对象,构造方法中传递Runnable接口的实现类对象
        Thread thread1 = new Thread(runnable, "窗口1");
        Thread thread2 = new Thread(runnable, "窗口2");
        Thread thread3 = new Thread(runnable, "窗口3");
        //调用start方法开启多线程
        thread1.start();
        thread2.start();
        thread3.start();
    }
}

代码执行后的结果

解决线程安全问题的三种方案:使用Lock锁

java.util.concurrent.locks.Lock 机制提供了比synchronized代码块和synchronized方法更广泛的锁定操作, 同步代码块/同步方法具有的功能Lock都有,除此之外更强大,更体现面向对象。Lock锁也称同步锁,加锁与释放锁方法化了,如下:
  • public void lock() :加同步锁。
  • public void unlock() :释放同步锁

使用步骤:

  1. 在成员位置创建一个ReentrantLock对象(java.util.concurrent.locks.ReentrantLock implements Lock接口)
  2. 在可能会出现安全问题的代码前调用Lock接口中的方法lock获取锁
  3. 在可能会出现安全问题的代码后调用Lock接口中的方法unlock释放锁

使用Lock锁模拟卖票

package demo06Lock;

import java.util.concurrent.locks.ReentrantLock;

public class LockTest implements Runnable {
    //定义一个多个线程共享的票源
    private int ticket = 10;
    //1:在成员位置创建一个ReentrantLock对象
    ReentrantLock rl = new ReentrantLock();

    //设置线程任务:卖票
    @Override
    public void run() {

        //使用死循环,让卖票操作重复执行
        while (true) {
            //2:在可能会出现安全问题的代码前调用Lock接口中的方法lock获取锁
            rl.lock();
            //先判断票是否存在
            if (ticket > 0) {
                try {
                    //先判断票是否存在
                    Thread.sleep(100);
                    //票存在,卖票 ticket--
                    System.out.println("线程名字是:" + Thread.currentThread().getName() + "正在卖" + ticket + "票");
                    ticket--;
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    //3.在可能会出现安全问题的代码后调用Lock接口中的方法unlock释放锁
                    rl.unlock();//无论程序是否异常,都会把锁释放
                }

            }
        }

    }
}

定义测试类

package demo06Lock;

public class DemoLockTest {
    public static void main(String[] args) {
        //创建接口实现类对象
        LockTest l = new LockTest();
        //创建Thread类对象,构造方法中传递Runnable接口的实现类对象
        Thread thread1 = new Thread(l, "窗口1");
        Thread thread2 = new Thread(l, "窗口2");
        Thread thread3 = new Thread(l, "窗口3");
        //调用start方法开启多线程
        thread1.start();
        thread2.start();
        thread3.start();
    }
}

代码执行后的结果

 

 

 

Guess you like

Origin www.cnblogs.com/wurengen/p/12046260.html