JAVA basics--multithreading, thread safety

Threads and Processes

  • Process : refers to an application program running in memory. Each process has an independent memory space. An application program can run multiple processes at the same time; Running a program is the process of a process from creation, operation to death.

  • Thread : A thread is an execution unit in a process, responsible for the execution of programs in the current process, and there is at least one thread in a process. There can be multiple threads in a process, and this application can also be called a multi-threaded program.

    In short: After a program runs, there is at least one process, and a process can contain multiple threads

We can right click on the task bar at the bottom of the computer -----> open the task manager, and we can view the process of the current task:
process concept
insert image description here

Thread concept
insert image description here
thread scheduling:

  • time-sharing scheduling

    All threads take turns to use the CPU, and each thread takes up CPU time evenly.

  • preemptive scheduling

    Give priority to threads with high priority to use the CPU. If the threads have the same priority, one will be randomly selected (thread randomness). Java uses preemptive scheduling.

create thread

  • Inherit Thread class creation
public class MyThread extends Thread{
    
    
    @Override
    public void run() {
    
    
        for (int i=0;i<20;i++) {
    
    
            System.out.println("myThread:" + "i = " + i);
        }
        // Thread类的run方法并没有执行
        /*
        * @Override
          public void run() {
        if (target != null) {
            target.run();
        }
        }*/
        // 因为我们是直接使用该类的start方法,也就是直接调用其父类Thread类的start方法
        // 并没有对Thread类进行创建对象,所以target为空,没有执行该方法,
        // 而是在该类中添加相关指令(for循环)
        super.run();
    }
}

使用多线程
    public static void main(String[] args) {
    
    
        MyThread mt = new MyThread();
        mt.start();
        for (int i=0;i<20;i++) {
    
    
            System.out.println("main:" + "i = " + i);
        }
    }

Running result: The main thread and the new thread seize resources, and whoever grabs the resources will run.
insert image description here

Some methods of the Thread class

  • Construction method
public Thread() {
    
    
        init(null, null, "Thread-" + nextThreadNum(), 0);
    }

public Thread(Runnable target) {
    
    
        init(null, target, "Thread-" + nextThreadNum(), 0);
    }
  • run() method
    This method is a method that implements the Runnable interface
@Override
    public void run() {
    
    
        if (target != null) {
    
    
            target.run();
        }
    }
  • getName() method: Get the thread name
  • currentThread() Static method, get the current thread
    Two ways to get the thread name
public class MyThread extends Thread{
    
    

    public MyThread(String name) {
    
    
        super(name);
    }

    public MyThread() {
    
    
    }

    @Override
    public void run() {
    
    
        // 第一种获取线程名字方法
        /*String name = super.getName();
        System.out.println(name);*/
//        Thread thread = Thread.currentThread();
        // 第二种获取线程名方法
        System.out.println(Thread.currentThread().getName());
        super.run();
    }
}


public class ThreadName {
    
    

    public static void main(String[] args) throws InterruptedException {
    
    
        MyThread mt = new MyThread("新线程");
        mt.start();
//        new MyThread().start();
        Thread thread = Thread.currentThread();
        System.out.println(thread.getName());
        /*for (int i = 0; i < 10; i++) {
            System.out.println("i= " + i);
            Thread.sleep(1000);
        }*/
    }
}

insert image description here

  • sleep(): delay in milliseconds

Implement the Runnable class to create threads

public class MyRunnable implements Runnable{
    
    
    @Override
    public void run() {
    
    
        for (int i=0;i<20;i++) {
    
    
            System.out.println("myThread:" + "i = " + i);
        }
    }
}

public class Demo02_Thread {
    
    
    public static void main(String[] args) {
    
    
        // 将Runnable实现类作为Thread的构造参数传递到Thread类中,然后启动Thread类
        MyRunnable mr = new MyRunnable();
        // 这里Thread构造了target对象,所以run方法是调用我们重新接口的run方法
        new Thread(mr).start();
        for (int i=0;i<20;i++) {
    
    
            System.out.println("main:" + "i = " + i);
        }
    }
}

insert image description here

The difference between the two creation methods

It can be seen that the two methods revolve around Thread and Runnable.
Inherit the Thread class and write run() into the class.
To implement the Runnable interface, write the run() method into the interface and then wrap it with the Thread class. Two ways In the end, the start() method of the Thread class is called to start the thread.
There is no obvious difference between the two methods in essence, but there is a big difference in appearance.
The first method is to inherit the Thread class, because Java is single inheritance . If a class inherits the Thread class, then there is no way to inherit other Classes are
a bit restricted and a little inflexible in inheritance.
The second method is to solve the inflexible problem of single inheritance in the first method, so the second method is used for normal use .

Use anonymous inner classes to create methods

public class WithoutName {
    
    

    public static void main(String[] args) {
    
    
        // 使用匿名内部类的方式重写run方法
        new Thread() {
    
    
            @Override
            public void run() {
    
    
                System.out.println(Thread.currentThread().getName());
                super.run();
            }
        }.start();

        Runnable r = new Runnable() {
    
    
            @Override
            public void run() {
    
    
                for (int i = 0; i < 10; i++) {
    
    
                    System.out.println(Thread.currentThread().getName() + '-' + i);
                }
            }
        };
        Thread thread = new Thread(r);
        thread.start();

        new Thread(new Runnable() {
    
    
            @Override
            public void run() {
    
    
                for (int i = 0; i < 10; i++) {
    
    
                    System.out.println(Thread.currentThread().getName() + '-' + i);
                }
            }
        }).start();

    }
}

thread safety

In most practical multithreaded applications, two or more threads need to share access to the same data . If two threads access the same object, and each thread calls a method that modifies the state of the object, the two threads will overwrite each other, which may cause the object to be destroyed. This situation is usually called a race condition .

Simulate the process of selling movie tickets through multiple windows in a cinema, and discover multi-threaded security issues.
insert image description here
MyThread.java

public class MyThread implements Runnable{
    
    

    private int ticket = 100;

    @Override
    public void run() {
    
    
        // 重复卖票
        while (true) {
    
    
            if (ticket> 0) {
    
    
                try {
    
    
                    Thread.sleep(10);
                } catch (InterruptedException e) {
    
    
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+ "正在卖第"+ticket+ "张票");
                ticket--;
            }
        }
    }
}

There is a phenomenon of selling the same ticket and non-existent tickets. Problem
insert image description here
insert image description hereanalysis:
Selling the same ticket Analysis : Window 1 grabs CPU resources, enters if judgment, and prepares to sell the 100th ticket. At this time, Window 2 grabs resources , The 100th ticket was sold and the ticker has not yet been executed; Window No. 1 grabbed resources again and sold the 100th ticket.
Analysis of buying or not existing tickets : Window 1 sold the last ticket, but has not yet issued a ticket–; Window 2 has grabbed resources and entered if it is judged that it has not been sold yet, and Window 1 has grabbed resources for ticket–; Then window No. 2 has entered the if judgment, so a ticket that does not exist is sold.

Solution:
1. Lock the object

Use the ReentrantLock class to protect code blocks, and the ReentrantLock class inherits from Lock.

java.util.conuurrent.locks.Lock

  • void lock()
    acquires this lock, and blocks if the current lock is occupied by another thread.
  • void unlock()
    releases the lock

Use the following structure to lock

myLock.lock(); // ReentrantLock对象
try
{
    
    
   关键部分
}
fina {
    
    
myLock.unlock();
}

Note: Be sure to put unlock in the finally clause, otherwise, if an exception occurs, the lock will not be released, and other threads will be blocked forever.

Solve the problem of selling tickets

    private int ticket = 100;
    private ReentrantLock  reentrantLock= new ReentrantLock();
reentrantLock.lock();
@Override
    public void run() {
    
    
        // 重复卖票
        while (true) {
    
    
            if (ticket> 0) {
    
    
                try {
    
    
                    Thread.sleep(10);
                    System.out.println(Thread.currentThread().getName()+ "正在卖第"+ticket+ "张票");
                    ticket--;
                } catch (InterruptedException e) {
    
    
                    e.printStackTrace();
                } finally {
    
    
                    reentrantLock.unlock();
                }
            }
}


insert image description here
The condition object does not appear above.
When a thread enters the critical section and finds that it can only execute after a certain condition is met, the condition object can be used to manage those threads that have acquired a lock and cannot do useful work.

If you do not use the condition object but directly judge and call the method after the condition is met, the current thread may be interrupted.

if(满足条件)
执行方法进行操作

java.util.concurrent.locks.Condition 5

  • void await()
    puts the thread in a wait set for this condition.
  • void signAll()
    randomly selects a thread from the waiting set of the condition, and unblocks it.
private Condition sufficientFunds;

mylock.lock();
try{
    
    
	while(不满足条件)
		sufficientFunds.await();// 进入等待集,阻塞状态
	执行操作
	
	sufficientFunds.signAll();// 最后该线程执行完毕,再随机挑选其他线程执行
}

2. Synchronization methods
The Lock and Condition interfaces used earlier allow programmers to fully control locking. However, in most cases, such control is not needed, and a mechanism built into the java language can be used.
If a method is declared with the synchronized keyword, then the object's lock will protect the entire method. To call this method, the thread must acquire the internal object lock.
As shown below: the steps in the method are locked, and there will still be no phenomenon of selling duplicate tickets or selling non-existent tickets.

public synchronized void buyTicket() {
    
    
        if (ticket> 0) {
    
    
            try {
    
    
                Thread.sleep(10);
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+ "正在卖第"+ticket+ "张票");
            ticket--;
        }
    }

Use the condition object in the synchronization method to directly use the wait() and notifyAll() methods of the Object class. The default is this call.
3. Synchronized code block

// 定义对象锁
private Object obj = new Object();

synchronized (obj) {
    
    
                if (ticket> 0) {
    
    
                    try {
    
    
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
    
    
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()+ "正在卖第"+ticket+ "张票");
                    ticket--;
                }
            }

The use of conditional objects in synchronous code blocks requires the use of obj.wait(), obj.notifyAll().
There is actually no difference between the synchronization method and the synchronization code block. The synchronization code block can realize the locking of some codes more conveniently.

Supongo que te gusta

Origin blog.csdn.net/qq_44660367/article/details/109061842
Recomendado
Clasificación