Rumah  >  Artikel  >  Java  >  Java multithreading: menggunakan kelas Thread.

Java multithreading: menggunakan kelas Thread.

王林
王林ke hadapan
2023-04-25 14:52:071243semak imbas

    Penggunaan asas kelas Thread

    1 Cipta subkelas, warisi daripada Thread dan ganti kaedah larian:

    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();
        }
    }

    2. Buat kelas, laksanakan antara muka Runnable, buat contoh Runnable dan hantarkannya ke 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();
        }
    }

    3 . dicipta untuk kelas dalaman Tanpa Nama ini dan contoh Runnable baharu diserahkan kepada pembina Thread

    public class Demo4 {
        public static void main(String[] args) {
            Thread t = new Thread(){
                @Override
                public void run() {
                    System.out.println("hello");
                }
            };
            t.start();
        }
    }
    4.ekspresi lambda lambda menggantikan Runnable

    Penunjuk benang
    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();
        }
    }

    1.isDaemon();
    public class Demo6 {
        public static void main(String[] args) {
            Thread t = new Thread(() ->{
                System.out.println("hello");
            });
            t.start();
        }
    }
    Sama ada benang latar belakang benang latar tidak menjejaskan keluar proses, bukan benang latar belakang akan menjejaskan keluar proses

    2.isAlive();

    Sama ada ia masih hidup atau tidak? objek t mungkin masih wujud

    3.isinterrupted();

    Sama ada ia telah terganggu

    Perbezaan antara lari dan mula

    run hanyalah penerangan kaedah biasa Selepas mengetahui kandungan tugasan, mulakan adalah kaedah khas yang akan mencipta benang dalam sistem secara dalaman

    Menyampuk benang

    Kunci untuk menghentikan utas adalah dengan membiarkan kaedah larian yang sepadan Selepas pelaksanaan, untuk utas utama, kaedah utama tidak akan ditamatkan sehingga ia dilaksanakan

    1. Tetapkan bendera secara manual

    Kawal bendera ini dalam utas Ia boleh menjejaskan penghujung utas ini, tetapi beberapa utas di sini berkongsi ruang maya, jadi isQuit diubah suai oleh utas utama dan isQuit dinilai oleh t thread adalah nilai yang sama

    2 Gunakan bit bendera terbina dalam dalam Thread untuk menentukan
    Thread.interruted() Ini ialah kaedah statik. Thread.currentThread().isInterrupted() Ini ialah kaedah contoh, di mana currentThread boleh mendapatkan Contoh thread semasa

    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;
        }
    }

    Perlu diingat bahawa memanggil kaedah ini t.interrupt() boleh menyebabkan dua situasi:

    1) Jika benang t Apabila sedia, tetapkan bendera benang kepada benar
    2) Jika benang t berada dalam keadaan tersekat (tidur), InterruptExeception akan dicetuskan
    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();
        }
    }

    Urut sedang menunggu
    Turutan penjadualan antara berbilang utas adalah tidak pasti Kadangkala kita perlu mengawal susunan antara utas ialah a cara mengawal susunan pelaksanaan benang. Benang yang menunggu di sini hanya perlu mengawal susunan benang berakhir.
    Benang mana yang bercantum, utas mana yang akan disekat dan tunggu sehingga utas yang sepadan menyelesaikan pelaksanaan.

    t.join();


    Utas yang memanggil kaedah ini ialah utas utama Apabila memanggil objek t, utama akan menunggu untuk t.

    Kod berhenti apabila ia mencapai baris sambung, membiarkan t tamat dahulu dan kemudian utama diteruskan.

    t.join(10000);

    join menyediakan versi lain dengan satu parameter Parameter ialah masa menunggu selama 10 saat join akan kembali terus. Tunggu

    Thread.currentThread()

    untuk mendapatkan aplikasi thread semasa yang akan dapatkan contoh utas itu. Bandingkan ini dengan yang berikut:
    Untuk kod ini, utas dicipta dengan mewarisi kaedah Thread. Pada masa ini, perkara yang anda perolehi secara langsung melalui kaedah ini dalam larian ialah contoh Benang semasa

    Walau bagaimanapun, ini di sini tidak menunjuk kepada jenis Benang, tetapi kepada Runnable , yang merupakan tugasan mudah yang tidak mempunyai atribut nama Untuk mendapatkan nama utas, anda hanya boleh menggunakan Thread.currentThread()


    Status Proses
    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();
        }
    }

    Untuk tahap sistem:

    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();
        }
    }

    Sedia

    Menyekat
    • Kelas Thread dalam java diperhalusi lagi:

    BARU: Objek Thread telah dibuat tetapi permulaan belum dipanggil lagi

    • DITAMATKAN:Benang dalam sistem pengendalian telah dilaksanakan dan dimusnahkan, tetapi objek Benang masih dalam keadaan yang diperolehi

    • BOLEH LALU: Keadaan sedia, benang dalam keadaan ini Ia berada dalam baris gilir sedia dan boleh dijadualkan ke CPU pada bila-bila masa

    • MASA_MENUNGGU: Ia akan memasuki keadaan ini selepas memanggil tidur, sertai (masa tamat masa) DISEKAT: semasa Benang sedang menunggu kunci, menyebabkan penyekatan

    • MENUNGGU:The utas semasa sedang menunggu untuk bangun

    • Rajah peralihan keadaan :

    线程安全问题

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

    synchronized public void increase() {
            count++;
    }

    线程不安全产生的原因:

    • 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++;
    	}
    }

    2.修饰一个代码块

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

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

    3.修饰一个静态方法

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

    synchronized public static void func(){
    }

    或者

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

    监视器锁monitor lock

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

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

    死锁的其他场景

    • 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: menggunakan kelas Thread.

    • 存储空间: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后");
        }
    }

    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后");
            }
        }
    }

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

    Atas ialah kandungan terperinci Java multithreading: menggunakan kelas Thread.. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

    Kenyataan:
    Artikel ini dikembalikan pada:yisu.com. Jika ada pelanggaran, sila hubungi admin@php.cn Padam