• 还不懂Java高并发的,建议看看这篇阿里大佬的总结,写的非常详细


    前言

    进程是计算机中程序关于某几何数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位。是操作系统结构的基础

    线程可以说是轻量级的进程,是程序执行的最小单位,使用多线程而不用多进程去进行并发程序的设计,是因为线程之间的切换与调度的成本远小于进程。

    线程的几种状态

    New状态表示刚刚创建的线程,这种线程还没有开始执行
    RUNNABLE:当线程创建好之后,调用线程的start方法就会进入就绪状态。
    BLOCKED:当线程运行过程如果遇到了Syschronized就会进入阻塞状态。
    TIMED_WATING:表示等待状态,有时间限制的(sleep)
    WAITING:表示进入一个无时间显示的等待状态(wait)
    TERMINATED:表示结束状态。
    
    

    线程的基本操作

    新建线程

    实现Runable接口或者new Thread

    终止线程

    为什么不建议使用stop?

    因为stop方法比较暴力,强行把执行的程序终止,可能会引发数据不一致的问题,Thread.stop()方法在结束线程时,会直接终止线程,并立即释放这个线程的所持有的锁。比如数据写到一半,强行终止掉了,数据就会被破坏。

    线程中断

    interrupt() //中断线程。相当于设置中断标志位
    isInterrypted() //判断是否被中断
    interrupted() //判断是都被中断,并清除当前的中断状态
    
    
    
    

    Thread.sleep()方法由于中断而抛出异常,此时它会清除中断标记,如果不加处理,那么下一次循环开始时,就无法捕获这个异常,可以再次设置中断标记来方便循环判断。

    等待和通知

    当在一个对象实力上条用wait()方法之后。当前线程就会在这个对象上等待。在线程A中调用了obj.wait()方法,那么变成A就会停止继续执行,转为等待状态,直到其他线程调用了obj.notify()方法才有机会继续执行(并不是一定)

    如果一个线程调用了obj.wait()那么它就会进入这个object的等待队列,这个等待队列可能会有多个线程,因为系统运行多个线程同时等待某一个对象,当obj.notify()方法被调用时,就会随机选择一个线程,将其唤醒,这个唤醒是完全随机的。

    notifyAll()方法的功能是唤醒所有在该对象等待队列中等待的线程。

    守护线程的finally不一定会执行。如果除守护线程外的所有线程都结束了,那么守护线程就立即退出没有执行finally代码块的机会。

    挂起(suspend)和继续(resume)执行

    不推荐执行suspend(),因为该方法在导致线程暂停的同时,并不会释放任何资源。影响其他想要访问该资源的线程,直到当前线程使用resume操作,是当前线程继续执行,但是此处有一个隐患。如果resume在suspend之前运行了,那么被挂起的线程将不能继续执行下去。

    public class BadSuspend {
    	public static Object u = new Object();
    	static ChangeObjectThread t1 = new ChangeObjectThread("t1");
    	static ChangeObjectThread t2 = new ChangeObjectThread("t2");
    
    	public static class ChangeObjectThread extends Thread {
    		public ChangeObjectThread(String name){
    			super.setName(name);
    		}
    		@Override
    		public void run() {
    			synchronized (u) {
    				System.out.println("in "+getName());
    				Thread.currentThread().suspend();
    			}
    		}
    	}
    
    	public static void main(String[] args) throws InterruptedException {
    		t1.start();
    		Thread.sleep(100);
    		t2.start();
    		t1.resume();
    		t2.resume();//无法直到这一行先执行,还是Thread.currentThread().suspend();先执行
    		t1.join();
    		t2.join();
    	}
    }
    
    

    等待线程结束(join)和谦让(yeild)

    public final synchronized void join(long millis) throws InterruptedException//有最大等待时长的等待
    
    public final synchronized void join(long millis, int nanos)
    throws InterruptedException//毫秒,加纳秒级别的等待
    
    public final void join() throws InterruptedException//表示无限期的等待
    
    
    package com.atmb.me;
    
    public class joinAndYeild {
        public volatile  static  int i =0;
    
        public static class myThread implements Runnable{
            @Override
            public void run() {
                for (int j = 0; j < 1000000; j++) {
                    i+=1;
                }
            }
        }
    
        public static void main(String[] args) throws InterruptedException {
            Thread thread = new Thread(new myThread());
            thread.start();
            thread.join();//等待thread线程执行结束
            System.out.println(i);//结果为1000000
        }
    }
    
    
        public static native void yield();
    
    

    这个方法一旦执行,就会使当前线程让出cpu,让出cpu之后,该线程会继续进行cpu资源的争夺,之后继续正常运行。

    线程组

    package com.atmb.me;
    
    public class ThreadGroupTest implements Runnable {
    
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getThreadGroup().getName()+"==="+Thread.currentThread().getName());
            try {
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    
        public static void main(String[] args) {
            ThreadGroup group1 = new ThreadGroup("group1");
            Thread thread1 = new Thread(group1, new ThreadGroupTest());
            Thread thread2 = new Thread(group1, new ThreadGroupTest());
            thread1.start();
            thread2.start();
            System.out.println(group1.activeCount());//活跃线程数
            group1.list();
        }
    }
    <<<
    2
    group1===Thread-0
    java.lang.ThreadGroup[name=group1,maxpri=10]
    group1===Thread-1
        Thread[Thread-0,5,group1]
        Thread[Thread-1,5,group1]
    
    

    守护线程(Daemon)

    守护线程要守护的对象已经不存在了,那么整个应用程序就应该技术,因此当java应用内只有守护线程时,java虚拟机就会自然退出。

    package geym.ch2;
    
    public class DaemonDemo {
        public static class DaemonT extends Thread{
            public void run(){
                while(true){
                    System.out.println("I am alive");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
        public static void main(String[] args) throws InterruptedException {
            Thread t=new DaemonT();
            t.setDaemon(true);
            t.start();
    
    //        Thread.sleep(2000);
        }
    }
    
    

    设置守护线程,必须要在start之前设置,否则会得到异常信息。

    线程优先级

    Java中的线程可以有自己的优先级。优先级高的线程在竞争资源时,会更有优势,更大概率抢占到资源,java中线程优先级可以设置为1-10.
    其中有三个静态标量标识三个对应的优先级。

        /**
         * The minimum priority that a thread can have.
         */
        public final static int MIN_PRIORITY = 1;
    
       /**
         * The default priority that is assigned to a thread.
         */
        public final static int NORM_PRIORITY = 5;
    
        /**
         * The maximum priority that a thread can have.
         */
        public final static int MAX_PRIORITY = 10;
    
    
    public class PriorityDemo {
        public static class HightPriority extends Thread{
            static int count=0;
            public void run(){
                while(true){
                    synchronized(PriorityDemo.class){
                        count++;
                        if(count>10000000){
                            System.out.println("HightPriority is complete");
                            break;
                        }
                    }
                }
            }
        }
        public static class LowPriority extends Thread{
            static int count=0;
            public void run(){
                while(true){
                    synchronized(PriorityDemo.class){
                        count++;
                        if(count>10000000){
                            System.out.println("LowPriority is complete");
                            break;
                        }
                    }
                }
            }
        }
    
        public static void main(String[] args) throws InterruptedException {
            Thread high=new HightPriority();
            LowPriority low=new LowPriority();
            high.setPriority(Thread.MIN_PRIORITY);
            low.setPriority(Thread.MAX_PRIORITY);
            low.start();
            high.start();
        }
    }
    
    

    线程安全与关键字synchronized

    synchronized

    • 指定加锁对象,对给定的对象进行加锁,
    • 用于实例方法,对当前的实例进行加锁
    • 用于静态方法,对当前类进行加锁

    错误的加锁

    package com.atmb.me;
    
    public class lockError implements Runnable {
        public static Integer i =0;
        public static lockError instance =  new lockError();
    
        public static void main(String[] args) throws InterruptedException {
            Thread thread1 = new Thread(instance);
            Thread thread2 = new Thread(instance);
            thread1.start();
            thread2.start();
            thread1.join();
            thread2.join();
            System.out.println(i);
        }
    
        @Override
        public void run() {
            for (int j = 0; j < 100000; j++) {
                synchronized (i){
                    i+=1;
                }
            }
        }
    }
    <<<
    142957
    
    

    原因在于Integer类型属于不可变对象,一旦创建就不可能被改变,当integer对象赋值为新值时,当前引用指向的对象就改变了。

    指令重排的前提

    指令重排需要保证串行语义的一致性,指令重排不会使串行的语义逻辑发生问题。

    指令重排的目的?

    为了减少中断流水线
    
    

    那些指令不能重排

    • 程序顺序原则,一个线程内保证语义的串行性
    • volatile规则:volatile变量的写先与读发生,宝整理volatile变量的可见性
    • 传递性:解锁必然发生在随后的加锁前
    • 线程的start()方法先于它的动作
    • 线程的所有操作先于线程的终结(interrupt)
    • 线程的中断(interrupt())先于被中断线程的代码
    • 对象的构造函数的执行,结束先于finalize()的方法

    是否会释放锁

    • yield让出cpu执行权,不会释放锁

    • sleep休眠时,不会释放锁

    • 调用wait方法之后,会释放锁,唤醒之后,会再次竞争锁,然后执行wait()方法后的代码

    • nofify nodifyall 对锁没有影响,一般放同步代码块的最后一行

    最后

    感谢你看到这里,看完有什么的不懂的可以在评论区问我,觉得文章对你有帮助的话记得给我点个赞,每天都会分享java相关技术文章或行业资讯,欢迎大家关注和转发文章!

  • 相关阅读:
    Codeforces 1491 D. Zookeeper and The Infinite Zoo (二进制处理)
    Codeforces 1479A. Searching Local Minimum(注意输入+二分)
    Codeforces 1480B. The Great Hero(阅读模拟题,注意数据范围和攻击顺序)
    Codeforces 1480A. Yet Another String Game (阅读理解题)
    windows 10 开启全盘瞬间索引功能
    JetBrains CLion C++ IDE连接wsl2(Ubuntu)时,报错"Unable to establish SSL connection"解决方案
    WorkFlowy 的 MarkDown 预览方案解决
    git 学习 完全学懂
    jeecgboot <j-popup
    面试之类加载器
  • 原文地址:https://www.cnblogs.com/lwh1019/p/13559354.html
Copyright © 2020-2023  润新知