Home> Java> javaTutorial> body text

Java multithreading: using the Thread class.

王林
Release: 2023-04-25 14:52:07
forward
1217 people have browsed it

    Basic usage of Thread class

    1. Create a subclass, inherit from Thread and override the run method:

    class MyThread extends Thread { @Override public void run() { System.out.println("hello thread"); } } public class Demo1 { public static void main(String[] args) { // 最基本的创建线程的办法. Thread t = new MyThread(); //调用了start方法才是真正的在系统中创建了线程,执行run方法 t.start(); } }
    Copy after login

    2. Create a class, implement the Runnable interface, create a Runnable instance and pass it to Thread

    class MyRunnable implements Runnable{ @Override public void run() { System.out.println("hello"); } } public class Demo3 { public static void main(String[] args) { Thread t = new Thread(new MyRunnable()); t.start(); } }
    Copy after login

    3. Anonymous inner class

    is created An anonymous inner class, inherits from the Thread class, overrides the run method, and then creates an instance of the anonymous inner class

    public class Demo4 { public static void main(String[] args) { Thread t = new Thread(){ @Override public void run() { System.out.println("hello"); } }; t.start(); } }
    Copy after login

    new's Runnable. For this created anonymous inner class, at the same time The Runnable instance produced by new is passed to the constructor of Thread

    public class Demo5 { public static void main(String[] args) { Thread t = new Thread(new Runnable() { @Override public void run() { System.out.println("hello"); } }); t.start(); } }
    Copy after login

    4.lambda expression lambda replaces Runnable

    public class Demo6 { public static void main(String[] args) { Thread t = new Thread(() ->{ System.out.println("hello"); }); t.start(); } }
    Copy after login

    Thread indicator

    1.isDaemon();
    Whether the background thread background thread does not affect the process exit, not the background thread will affect the process exit

    2.isAlive();
    Is it alive? There is no corresponding thread in the system before calling start. The thread will be destroyed after the run method is executed. The t object may still exist

    3.isinterrupted();
    Whether it is interrupted

    The difference between run and start

    run is simply an ordinary method that describes the content of the task, while start is a special Method, a thread will be created internally in the system

    Interrupt thread

    The key to stopping the thread is to let the corresponding run method finish executing. For the main thread, the main method It will terminate only after the execution is completed

    1. Manually set the flag
    Controlling this flag in the thread can affect the end of the thread, but here Multiple threads share a virtual space, so the isQuit modified by the main thread and the isQuit judged by the t thread are the same value

    public class Demo10 { // 通过这个变量来控制线程是否结束. private static boolean isQuit = false; public static void main(String[] args) { Thread t = new Thread(() -> { while (!isQuit) { System.out.println("hello thread"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } }); t.start(); // 就可以在 main 线程中通过修改 isQuit 的值, 来影响到线程是否退出 try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } // main 线程在 5s 之后, 修改 isQuit 的状态. isQuit = true; } }
    Copy after login

    2. Use a built-in flag in Thread To determineThread.interruted() This is a static method Thread.currentThread().isInterrupted() This is an instance method, in which currentThread can obtain the instance of the current thread

    public class Demo7 { public static void main(String[] args) { Thread t = new Thread(() -> { while(!Thread.currentThread().isInterrupted()){ System.out.println("hello"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); // 当触发异常之后, 立即就退出循环~ System.out.println("这是收尾工作"); break; } } }); t.start(); try{ Thread.sleep(5000); }catch (InterruptedException e){ e.printStackTrace(); } // 在主线程中, 调用 interrupt 方法, 来中断这个线程. // t.interrupt 的意思就是让 t 线程被中断!! t.interrupt(); } }
    Copy after login

    It should be noted that calling this method t.interrupt() may produce two situations:
    1) If the t thread is ready, set the thread's flag bit to true
    2 ) If the t thread is in a blocked state (sleep), an InterruptExeception will be triggered

    Thread waiting

    The scheduling order among multiple threads is uncertain. Sometimes we need to control the order between threads. Thread waiting is a means of controlling the order of thread execution. The thread waiting here only needs to control the order in which threads end.
    Which thread joins, which thread will block and wait until the corresponding thread completes execution.

    t.join();
    The thread calling this method is the main thread. When calling the object t, the main thread is made to wait for t.
    The code stops when it reaches the join line, letting t end first and then main continues.

    t.join(10000);
    join provides another version with one parameter. The parameter is the waiting time of 10s. After 10s, join will return directly. Wait for

    Thread.currentThread()

    to get the application of the current thread. Whichever thread calls currentThread will get the instance of that thread. Compare this as follows:
    For this code, threads are created by inheriting the Thread method. At this time, what you get directly through this in the run method is the current Thread instance

    public class Demo4 { public static void main(String[] args) { Thread t = new Thread(){ @Override public void run() { System.out.println(Thread.currentThread().getName()); System.out.println(this.getName()); } }; t.start(); } }
    Copy after login

    However, this here does not point to the Thread type, but to Runnable, which is just a simple task. name attribute, you can only get the thread name through Thread.currentThread()

    public class Demo5 { public static void main(String[] args) { Thread t = new Thread(new Runnable() { @Override public void run() { //err //System.out.println(this.getName()); //right System.out.println(Thread.currentThread().getName()); } }); t.start(); } }
    Copy after login

    Process status

    For the system level:

    • Ready

    • Blocking

    ##The Thread class in java is further refined:

    • NEW:The Thread object has been created but start has not been called yet

    • TERMINATED:Operating system The thread in has been executed and destroyed, but the Thread object is still in the obtained state

    • RUNNABLE:Ready state, the thread in this state is in the ready queue , it can be scheduled to the CPU at any time

    • ##TIME_WAITING:

      When sleep is called, it will enter this state, join (timeout time) BLOCKED: The current thread is waiting for the lock Resulting in blocking

    • WAITING:

      The current thread is waiting to wake up

    State transition diagram:

    线程安全问题

    定义:操作系统中线程调度是随机的,导致程序的执行可能会出现一些bug。如果因为调度随机性引入了bug线程就是不安全的,反之则是安全的。
    解决方法:加锁,给方法直接加上synchronized关键字,此时进入方法就会自动加锁,离开方法就会自动解锁。当一个线程加锁成功的时候,其他线程尝试加锁就会触发阻塞等待,阻塞会一直持续到占用锁的线程把锁释放为止。

    synchronized public void increase() { count++; }
    Copy after login

    线程不安全产生的原因:

    • 1.线程是抢占式执行,线程间的调度充满随机性。

    • 2.多个线程对同一个变量进行修改操作

    • 3.针对变量的操作不是原子的

    • 4.内存可见性也会影响线程安全(针对同一个变量t1线程循环进行多次读操作,t2线程少次修改操作,t1就不会从内存读数据了而是从寄存器里读)

    • 5.指令重排序,也是编译器优化的一种操作,保证逻辑不变的情况下调整顺序,解决方法synchronized。

    内存可见性解决方法:

    • 1.使用synchronized关键字 使用synchronized不光能保证指令的原子性,同时也能保证内存的可见性。被synchronized包裹起来的代码编译器就不会从寄存器里读。

    • 2.使用volatile关键字 能够保证内存可见性,禁止编译器作出上述优化,编译器每次执行判定相等都会重新从内存读取。

    synchronized用法

    在java中每个类都是继承自Object,每个new出来的实例里面一方面包含自己安排的属性,另一方面包含了“对象头”即对象的一些元数据。加锁操作就是在这个对象头里面设置一个标志位。

    1.直接修饰普通的方法

    使用synchronized的时候本质上是对某个“对象”进行加锁,此时的锁对象就是this。加锁操作就是在设置this的对象头的标志位,当两个线程同时尝试对同一个对象加锁的时候才有竞争,如果是两个线程在针对两个不同对象加锁就没有竞争。

    class Counter{ public int count; synchronized public void increase(){ count++; } }
    Copy after login

    2.修饰一个代码块

    需要显示制定针对那个对象加锁(java中的任意对象都可以作为锁对象)

    public void increase(){ synchronized(this){ count++; } }
    Copy after login

    3.修饰一个静态方法

    相当于针对当前类的类对象加锁,类对象就是运行程序的时候。class文件被加载到JVM内存中的模样。

    synchronized public static void func(){ }
    Copy after login

    或者

    public static void func(){ synchronized(Counter.class){ } }
    Copy after login

    监视器锁monitor lock

    可重入锁就是同一个线程针对同一个锁,连续加锁两次,如果出现死锁就是不可重入锁,如果不会死锁就是可重入的。因此就把synchronized实现为可重入锁,下面的例子里啊连续加锁操作不会导致死锁。可重入锁内部会记录所被哪个线程占用也会记录加锁次数,因此后续再加锁就不是真的加锁而是单纯地把技术给自增。

    synchronized public void increase(){ synchronized(this){ count++; } }
    Copy after login

    死锁的其他场景

    • 1.一个线程一把锁

    • 2.两个线程两把锁

    • 3.N个线程M把锁(哲学家就餐问题,解决方法:先拿编号小的筷子)

    死锁的四个必要条件(前三个都是锁本身的特点)

    • 1.互斥使用,一个锁被另一个线程占用后其他线程就用不了(锁的本质,保证原子性)

    • 2.不可抢占,一个锁被一个线程占用后其他线程不可把这个锁给挖走

    • 3.请求和保持,当一个线程占据了多把锁之后,除非显示的释放否则这些锁中都是该线程持有的

    • 4.环路等待,等待关系成环(解决:遵循固定的顺序加锁就不会出现环路等待)

    java线程类:

    • 不安全的:ArrayList,LinkedList,HashMap,TreeMap,HashSet,TreeSet,StringBuilder

    • 安全的:Vector,HashTable,ConcurrentHashMap,StringBuffer,String

    volatile

    禁止编译器优化保证内存可见性,产生原因:计算机想执行一些计算就需要把内存的数据读到CPU寄存器中,然后再从寄存器中计算写回到内存中,因为CPU访问寄存器的速度比访问内存快很多,当CPU连续多次访问内存结果都一样,CPU就会选择访问寄存器。

    JMM(Java Memory Model)Java内存模型

    就是把硬件结构在java中用专业的术语又重新抽象封装了一遍。

    工作内存(work memory)其实指的不是内存,而是CPU寄存器。
    主内存(main memeory)这才是主内存。
    原因:java作为一个跨平台编程语言要把硬件细节封装起来,假设某个计算机没有CPU或者内存同样可以套到上述模型中。

    寄存器,缓存和内存之间的关系

    CPU从内存取数据太慢,因此把数据直接放到寄存器里来读,但寄存器空间太紧张于是又搞了一个存储空间,比寄存器大比内存小速度比寄存器慢比内存快称为缓存。寄存器和缓存统称为工作内存。

    寄存器,缓存和内存之间的关系图

    Java multithreading: using the Thread class.

    • 存储空间:CPU

    • 速度:CPU>L1>L2>L3>内存

    • 成本:CPU>L1>L2>L3>内存

    volatile和synchronized的区别

    • volatile只是保证可见性不保证原子性,只是处理一个线程读和一个线程写的过程。

    • synchronized都能处理

    wait和notify

    等待和通知处理线程调度随机性问题的,join也是一种控制顺序的方式更倾向于控制线程结束。wait和notify都是Object对象的方法,调用wait方法的线程就会陷入阻塞,阻塞到有线程通过notify来通知。

    public class Demo9 { public static void main(String[] args) throws InterruptedException { Object object = new Object(); System.out.println("wait前"); object.wait(); System.out.println("wait后"); } }
    Copy after login

    wait内部会做三件事;

    • 1.先释放锁

    • 2.等待其他线程的通知

    • 3.收到通知后重新获得锁并继续往下执行

    因此想用wait/notify就得搭配synchronized

    public class Demo9 { public static void main(String[] args) throws InterruptedException { Object object = new Object(); synchronized (object){ System.out.println("wait前"); object.wait(); System.out.println("wait后"); } } }
    Copy after login

    注意:wait notify都是针对同一对象来操作的,例如现在有一个对象o,有10个线程都调用了o.wait,此时10个线程都是阻塞状态。如果调用了o.notify就会把10个线程中的一个线程唤醒。而notifyAll就会把所有10个线程全都给唤醒,此时就会竞争锁。

    The above is the detailed content of Java multithreading: using the Thread class.. For more information, please follow other related articles on the PHP Chinese website!

    Related labels:
    source:yisu.com
    Statement of this Website
    The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
    Latest Downloads
    More>
    Web Effects
    Website Source Code
    Website Materials
    Front End Template
    About us Disclaimer Sitemap
    php.cn:Public welfare online PHP training,Help PHP learners grow quickly!