Small knowledge, big challenge! This article is participating in the creation activity of “Essential Tips for Programmers”.

Multithreading brings high efficiency to programs, but it also brings unsafe problems. For example, when multithreading operations share resources, it may cause problems if they are not protected and restricted. The following three examples illustrate the problems when multithreading operations share resources.

1. Buying tickets

In reality, we all have to buy concert tickets, train tickets, if the number of tickets is only 100, but 10,000 people are strong, it must be the use of multi-threaded method for processing. In this example, if there are 20 tickets and three people want to buy the 20 tickets, writing a simple example will reveal the problem.

class Ticket implements Runnable{

    private int alltickets = 20;
    private boolean flag = true;

    @Override
    public void run(a) {
        while(alltickets>0) {
            try {
                Thread.sleep(100);// Extend the duration to simulate the actual situation
                buy();
            } catch(InterruptedException e) { e.printStackTrace(); }}}public void buy(a) throws InterruptedException {

        if(this.alltickets<=0)
        {
            System.out.println(Thread.currentThread().getName()+"There are no tickets left.");
            this.flag = false;
            return;
        }

        else
        {
            System.out.println(Thread.currentThread().getName()+"Bought the number"+this.alltickets--+"Ticket"); }}}public class ThreadProblem {
    public static void main(String[] args) throws InterruptedException, ExecutionException {
        Ticket t = new Ticket();
        new Thread(t, "Xiao Ming").start();
        new Thread(t, "Xiao Hua").start();
        new Thread(t, "Cow").start(); }}Copy the code

As you can see from the results, two problems arise

  1. The order of purchase is out of order. For example, the 18th ticket was bought earlier than the 19th ticket
  2. There are duplicate tickets, xiao Hua and Xiao Ming have bought the fifth ticket

This problem occurs when the shared resource AllTickets is not protected properly. For example, when the thread Xiao Hua accesses the fifth code block, it sleeps (the actual situation may be that the time slice ends), and the thread Xiao Ming also enters the code block, making both threads buy the fifth ticket.

2. Withdrawal problem

In this scenario, in a bank, the balance is a shared resource and must be protected. Otherwise, more than one person can withdraw money from this account at the same time, and everyone can withdraw money, resulting in a negative balance.

In this example, there is an Account type, which records the balance, and then defines a Person type, whose member variables have the name and the amount withdrawn each time. Finally, there is a Drawing class, which needs a Person object and an Account object. The function is to withdraw money (the balance – the amount taken out). In this example, we want xiaoming and Xiaohua to withdraw money, so we give them the same Account object, so that these two objects can operate on the shared resource.

class Account{
    public int money;

    public Account(int money)
    {
        this.money = money; }}class Person{
    public String name;
    public int needmoney;

    public Person(String name, int needmoney) {
        this.name = name;
        this.needmoney = needmoney; }}class Drawing implements Runnable{

    Account account;
    Person p;
    static ReentrantLock lock = new ReentrantLock();

    public Drawing(Account account, Person p) {
        this.account = account;
        this.p = p;
    }

    @Override
    public void run(a) {

        while(true) {

                if (this.account.money - p.needmoney <= 0) {
                    System.out.println(p.name + "I don't have any money left.");

                    break;
                } else {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                    this.account.money -= p.needmoney;

                    System.out.println(p.name + "Took" + p.needmoney + "Money" + ",还有" + this.account.money + "Money"); }}}}public class ThreadProblem {
    public static void main(String[] args) throws InterruptedException, ExecutionException {
        Account account = new Account(100);
        Person i = new Person("Xiao Ming".20);
        Person you = new Person("Xiao Hua".15);
        Drawing d1 = new Drawing(account,i);
        Drawing d2 = new Drawing(account,you);
        new Thread(d1).start();
        newThread(d2).start(); }}Copy the code

From this result, the following problems appear:

  1. In the third line, Ming takes 20 from the balance of 65, but shows that the balance is 30
  2. In the fourth row, Xiaohua took 15, but the balance remained unchanged
  3. After the last withdrawal, the balance turned negative

This happens when multiple threads perform unprotected operations on a shared resource. Here’s an example: Xiao Ming classmate this thread in the case of the balance and 65 to enter the code block, but sleep (not yet out of money), then xiaohua students this thread to enter a code block (because the balance has not been modified, or 65), and immediately took out 15 (balance of 50), and then sleep (not print), xiao Ming classmate this thread round at this moment, Withdraw money (then the balance becomes 50), and print (the third line of information), so “30 money” is displayed. At this time, Xiaoming sleeps again, Xiaohua wakes up for printing operation, and the fourth line is output.

3, ArrayList

ArrayList is thread-unsafe, in the form of multiple threads operating on the same ArrayList object. Consider the following example of two threads adding () to the same ArrayList object 20 times.

public class ThreadProblem {
    public static void main(String[] args) throws InterruptedException, ExecutionException {
        ArrayList<Integer> list = new ArrayList<>();
        MyOperator m1 = new MyOperator(list);
        MyOperator m2 = new MyOperator(list);
        new Thread(m1).start();
        newThread(m2).start(); }}class MyOperator implements Runnable
{
    ArrayList<Integer> list;

    public MyOperator(ArrayList<Integer> list)
    {
        this.list = list;
    }


    @Override
    public void run(a) {
        for (int i=0; i<20; i++) {try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            list.add(i);
            System.out.println(Thread.currentThread().getName()+"In the first"+list.size()+"Has been added to the position of". Now the capacity is ".+list.size()); }}}Copy the code

You can see there are two problems with this:

  1. Sometimes two threads do add in the same place
  2. Because of problem 1, the total number of arrayLists ends up being less than 40

These two problems still occur when multiple threads operate on the same shared variable. When thread 1 is ready to operate at position 1, thread 2 adds a number at position 1, and then thread 1 wakes up and still adds at position 1, which causes overwriting.