Threads and Concurrency in Java(上)

1. Jvm is a process

A Java application runs by default in one process(java 一般在一个进程里运行)

image-20220830100327857

Within a Java application, you work with several threads to achieve pseudo-parallel processing or asynchronous behaviour. You can take advantage of different cores.

对于多核的CPU电脑来说,真正的多线程并行是没问题的。

单核的CPU表示只有一个大脑:不能够做到真正的多线程并发,但是可以做到给人一种“多线程并发”的感觉。

(pseudo parallel)

对于单核的CPU来说,在某一个时间点上实际上只能处理一件事情,但是由于CPU的处理速度极快,多个线程之间频繁切换执行,给别人的感觉是:多个事情同时在做!!!

The starting point of a Java program is the main method

main()是主线程的入口。

对于java程序来说,当在DOS命令窗口中输入:java HelloWorld 回车之后。会先启动JVM,而JVM就是一个进程。JVM再启动一个主线程调用main方法(main方法就是主线程)。同时再启动一个垃圾回收线程负责看护,回收垃圾。最起码,现在的java程序中至少有两个线程并发,一个是 垃圾回收线程,一个是 执行main方法的主线程。

2. Processes and Threads

Processes

A process runs independently and isolated from other processes.

A process cannot directly access shared data in other processes.

进程A和进程B的 内存独立不共享, 无法共享资源,但可以通过socket或者http通信

image-20220829181419255

The resources of the process, e.g. memory and CPU time, are allocated to it via the operating system.

thread

A thread is a “lightweight process”.

image-20220830101159367
  1. Each has its own call stack

  2. Can access shared data of other threads in the same process.

  3. Every thread has its own memory cache.

  4. If a thread reads shared data, it stores this data in its own memory cache. A thread can re‐read the shared data

线程A和线程B,**堆内存** 和 方法区 内存共享。但是 栈内存 独立一个线程一个栈

3. Key Concepts in concurrent programming

<1> Atomicity(原子的) :

• An operation is said atomic when it cannot be interrupted.

• Once it starts is always completes

• One example assignment a = 5

a++可拆所以不是原子的

Non‐atomic operation: It can be interrupted

Atomic operation: It cannot be interrupted

<2>**Visibility **(可见性)

可见性是一个线程对共享变量的修改,对于另一个线程来说是否是可以看到的。

线程间的可见性

<3>Order of execution(有序性) :

The order of execution is not guaranteed !

无法保证多个线程的执行顺序

<4> Critical code:

– A part of code that must only be executed by a single thread at one time

4. Two methods of creating threads

Two ways of giving the Thread the run method

  1. Passing your class (object )(with a run() method) into a new Thread object

  2. Extending the Thread class

<1> Useing Runnable interface

  1. Runnable:
    Runnable is an Interface that requires you to implement a run() method

must implement(override) run() , which includes the code needs to be executed in this thread

//1.定义一个实现Runnable的实现类
//2.重写run()方法
//3.创建实现类对象
//4.将实现类对象作为参数传入Thread类的构造方法
//5.开启线程

public class construct_Thread_2 {
    public static void main(String[] args) {
        MyRunnable mr=new MyRunnable();
        Thread mt=new Thread(mr);
        mt.start();
        for(int i=0;i<1000;i++){
            System.out.println("bbbbbbbb");
        }
    }
}
class MyRunnable implements Runnable{
    @Override
    public void run() {
        for(int i=0;i<1000;i++){
            System.out.println("aaaaa");
        }
    }
}
package ThreadsLecture1;

public class Order implements Runnable {
    public static void main(String[] args) {
        Order order = new Order();
        for (int i = 0; i < 25; i++) {
            Thread thread = new Thread(order);       
            thread.setName("Thread " + i);          //设置线程名字
            thread.start();                //用start开启线程(不是run)
        }
    }

    public void run() {
        String threadName =  Thread.currentThread().getName();   
        //获取当前线程名字
        System.out.println("I'm running in thread‐" + threadName);
    }
}
  1. Using anonymous inner class:
new Thread(new Runnable() {
            @Override
            public void run() {
                for(int i=0;i<10000;i++) {
                    System.out.println(Thread.currentThread().getName());
                }
            }
        }).start();
Thread t = new Thread(new Runnable() 
{ public void run() { // stuff here
} }); 
t.start();

run() 不会启动线程,只是普通的调用方法而已。不会分配新的分支栈。(这种方式就是单线程。)start() 方法的作用是:启动一个分支线程,在JVM中开辟一个新的栈空间,这段代码任务完成之后,瞬间就结束了。这段代码的任务只是为了开启一个新的栈空间,只要新的栈空间开出来,start()方法就结束了。线程就启动成功了。
启动成功的线程会自动调用run方法,并且run方法在分支栈的栈底部(压栈)。run方法在分支栈的栈底部,main方法在主栈的栈底部。run和main是平级的。

<2>Using Thread class

  1. Thread class

The Thread class is responsible for executing your stuff in a thread.

Your stuff is encapsulated in a run() method.

And the Thread class manages the running of the thread

  1. Write a class that extends the Thread class

  2. Override the run() method and insert all your code

  3. You then tell Java to execute your extended Thread class

//1.继承Thread
//2.重写run方法
//3.创建Thread子类对象
//4.start方法开启线程

public class construct_Thread_1{
    public static void main(String[] args) {
        Mythread mt = new Mythread();
        mt.start();
        for (int i = 0; i < 1000; i++) {
            System.out.println("777777");
        }
    }

}
class Mythread extends Thread {
    @Override
    public void run() {
        for (int j = 0; j < 1000; j++) {
            System.out.println("5555");
        }
    }
}

<3> which one is better

Implement the Runnable interface

  1. With Method 2, you can only extend Thread. No other classes can be inherited by MyThread. Because Java only allows a class to extend one superclass

  2. Using the Runnable interface allows a subclass of Thread to be used if required(”多继承”)

<4>原理

Thread has two constructors: one with no parameters, one with a runnable parameter

image-20220906152652231

  • target is the name of the parameter of type Runnable in the Thread constructor
  • 有target用target的run(),无target用自己的
  • the run() method in thread class:
@Override
public void run() {
    if (target != null) {
        target.run();
    }
}

Controlling threads

sleep()

Can make a thread(this.currentThread()返回的thread) sleep for a certain number of milliseconds.

阻塞调用该方法的线程(运行态变为阻塞态),让出CPU资源,让其他线程竞争CPU资源;sleep时间结束后进入就绪状态,重新竞争CPU资源(与其他线程随机占用cpu)

sleep(long millis)

You can only make the current Thread sleep

Thread.sleep(1000);

You cannot choose another thread you want to sleep

The thread will then **pause (suspend)**该线程将会被挂起

This will free up CPU time for other threads

需要抛出异常InterruptedException

public void run()
{
try
{
Thread.sleep(1000);
}
catch(InterruptedException e)
{
e.printStackTrace();
} }
  • InterruptedException

    This exception is thrown when a thread is interrupted

yield()

礼让线程让出cpu Allows other threads to take priority

the executing thread is suspended and the CPU is given to some other runnable thread.

This thread will wait until the CPU becomes available again. the executing thread is returned to the ready queue of the processor(处理器) and waits for its next turn.

image-20220912221152815

不会有interruptedException:不会被打断

interrupt()

Every Thread object contains an interrupt flag variable(boolean)

interrupt() 方法只是改变中断状态而已,它不会中断一个正在运行的线程。

Other threads may change this flag to interrupt the thread

The programmer decides how a thread responds to an interrupt

只是改变一个标志值,具体是否interrupt还看程序员怎么处理interrupt flag

t1.interrupt(), where t1 is the target thread we want to interrupt

  • Methods(指自己定义的方法) should check this flag
  1. If true: finish the method immediately (i.e. return)

  2. If false: continue as normal

    class MyThread extends Thread
    {
      public void run(){
      for (int i=1; i<=5;i++)
      System.out.println(i);
      } 
    }
    public static void main(String args[]){
      MyThead t1 = new MyThread();
      t1.start();
      t1.interrupt();
    }

    该段代码是只更改标记但实际并未中断,因为run方法内未人为的check interrupt flag

    class MyThread extends Thread
    {
    public void run(){
    try{
    Thread.sleep(1000);
    System.out.println(Finished!");
    }
    catch(InterruptedException e){
    System.out.println(I’m interrupted!)
    System.exit(-1);
    }
        }
                       }
                       
    public static void main(String args[]){
    MyThread t1 = new MyThread();
    t1.start();
    try{
    t1.interrupt();
    }
    catch(Exception e){
    System.out.println("Exception handled "+e);}
    } }

    这段代码使用sleep方法,该方法内部封装了check interrupt flag的过程,所以抛出异常,真正实现中断

How to interrupt a thread
  1. thread.interrupt() sets the interrupted flag

  2. Blocking methods, such Thread.sleep(), try to detect when a thread has been interrupted and return early.

  3. They respond to interruption by clearing the setting the flag to false and throwing InterruptedException.

先睡眠后打断,则直接打断睡眠,并且清除停止状态值,使之变成false:

Why might we want to interrupt a thread?
  1. It is likely doing something that’s taking too long

2. Blocking is when a thread is prevented from doing anything

3. What if we were waiting for a thread to complete, but it had gone to sleep?

​ We might want to interrupt() it so we can continue

Interrupting a Thread:

• If any thread is in sleeping or waiting state (i.e. sleep() or wait() is invoked), calling the interrupt() method on the thread, breaks out the sleeping or waiting state throwing InterruptedException.

如果线程被Object.wait, Thread.join和Thread.sleep三种方法之一阻塞,此时调用该线程的interrupt()方法,那么该线程将抛出一个 InterruptedException中断异常(该线程必须事先预备好处理此异常),从而提早地终结被阻塞状态。

• If the thread is not in the sleeping or waiting state, calling the interrupt() method performs normal behaviour and doesn’t interrupt the thread but sets the interrupt flag to true.

如果线程没有被阻塞,这时调用 interrupt()将不起作用,只会改变flag的值

调用interrupt()方法,立刻改变的是中断状态,但如果不是在阻塞态,就不会抛出异常;如果在进入阻塞态后,中断状态为已中断,就会立刻抛出异常

The 3 methods provided by the Thread class for interrupting a thread
public void interrupt()
    //不是静态方法
public static boolean interrupted()
    //测试当前线程是否中断。 该方法可以清除线程的中断状态 。 换句话说,如果这个方法被连续调用两次,那么第二个调用将返回false
public boolean isInterrupted()
    //获取当前中断标志
概括interrupt mechanism
  • Invoking interrupt on a target thread sets interrupt flag

  • A thread may check for an interrupt (by itself) by invoking the static method Thread.interrupted()(then automatically cleared)

    Thread.interrupted()是静态方法,只能判断当前线程是否interrupt

  • one thread can check the interrupt status of another by using the non-static isInterrupted()(not clear the flag)

    isInterrupted()是成员方法,可以在一个进程里判断另一个进程是否interrupt

join()

用来coordinate threads

use the join() method of the Thread class which will pause until the other thread has finished

也会抛出InterruptedException异常

try{
thread2.join();
} catch(InterruptedException e){
e.printStackTrace();
}

该段代码所在线程必须等待thread2执行完毕才能重新竞争cpu

isAlive()

判断线程是否存活

alive :started but not dead(正在运行或者runnable)

3 ways to terminate a thread

Method 1: finish the thread naturally

  1. doing nothing

  2. use a shared Boolean

    使用退出标识使线程退出

    变量用volatile修饰,保证了不同线程对共享变量操作的可见性,也就是说一个线程修改了 volatile 修饰的变量,当修改后的变量写回主内存时,其他线程能立即看到最新值。

class MyThread extends Thread{
public void run()
{
//Do something..
//Finish naturally…
} 
}
-------------------------------------
class MyThread extends Thread{
    
private volatile boolean pleaseFinish = false;
    
public void run()
{ 
//Periodically check for pleaseFinish to be
// set to true
}
public void finishThreadPlease()
{pleaseFinish = true;}
}
  • If a thread is in a non-runnable state, setting a stop flag variable will have no effect!

  • A thread is in a non-runnable state if

​ – Its sleep method is invoked

​ – The thread calls the wait method to wait for a specific condition to be satisfied

​ – The thread is blocking on I/O

Method 2: daemon(守护) threads

守护线程类似保姆,进程中所有用户线程parent thread结束后守护线程也没有必要存在,自动销毁(如 GC)

myThread.setDaemon(false)

– The worker thread continues to run after the parent thread has finished

myThread.setDaemon(true)

– The worker thread terminates when the parent thread terminates

setDeamon() before starting the thread

public void run(){
WorkerThread t1 = new WorkerThread();
t1.setDaemon(true);
t1.start(); 
//Do Something…
//this thread completes
//t1 will also be terminated
}

Method 3: interrupt the thread

By catching an InterruptedException and then stop the thread

thread.interrupt()will not automatically stop a thread unless you programme it to do so

public void run()
{
for (int i = 0; i < importantInfo.length; i++)
{ 
try { Thread.sleep(4000); } 
catch(InterruptedException e){return;} 
System.out.println(importantInfo[i]);
} }

an InterruptedException is **only **automatically thrown when in sleep() or wait() ,hava to throw it manuly

try 
{
doSomething();
} 
catch(InterruptedException e) 
{
//Do nothing…
}
//不知道什么意义,但熟悉一下用法
public void run() { 
while(!Thread.currentThread().isInterrupted())
{ 
//Doing some heavy operations 
try {
Thread.sleep(5000);
} 
catch(InterruptedException e) {
e.printStackTrace();
Thread.currentThread().interrupt();
} 
}}

Thread 状态

image-20220920221135441

  1. 新建( new )
    当新建一个线程后,该线程处于新建状态,此时它和 Java对象一样,仅仅由Java 虚拟机为其分配内存空间,并初始化成员变量。同时已经有了相应的内存空间和其他资源,但是尚未运行start()方法。

  2. 就绪(Runnable )
    当线程有资格运行,但调度程序还没有把它选定为运行线程时线程所处的状态。当start()方法调用时,线程首先进人就绪状态。在线程运行之后或者从阻塞状态回来后,也返回到就绪状态。

  3. 运行( Running )
    线程创建之后就具备了运行条件,当Java虚拟机(JVM)将CPU的使用权切换给该线程时,此线程就开始了自己的生命周期,Thread类的子类中的run()方法就会立即执行。

当一个线程进入“运行”状态下,并不代表它可以一直执行到run()结束。因为事实上它只是加入此应用程序执行安排的队列中,正在等待分享CPU资源,也就是等候执行权,在何时给予线程执行权则由JVM决定,同时也由线程的优先级决定。

  1. 阻塞( Blocked )
    阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。

    运行的线程执行wait()方法,JVM会把该线程放入等待池中。运行的线程执行sleep()或join()方法,或者发出了IO 请求时,JVM 会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。

  2. 死亡(Dead )
    处于死亡状态的线程不具有继续运行的能力,线程的死亡有两种,一种是正常运行的线程完成了它全部工作(run()方法中全部语句),另一种是线程被提前强制终止,即强制run()方法结束。所谓死亡状态就是线程释放了分配给线程对象的内存。不要试图对死亡的线程调用start()方法来启动它,死亡线程不可能再次运行。