Java基础之线程深入接触

Java 多线程编程:从基础到实战! 多线程编程是 Java 中的一个核心概念,它允许多个线程同时执行任务,从而提高程序的效率和响应性。在现代应用程序中,多线程编程无处不在,尤其是在处理高并发任务时。今天,我们将从基础到实战深入讲解 Java 的多线程编程,帮助你更好地理解和应用它。 阅读详情

1.异常同步现象-死锁

1.定义

所谓死锁,是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。[引用自某度]

2.产生条件

  • 互斥条件:指进程对所分配到的资源进行排它性使用,即在一段时间内某资源只由一个进程占用。如果此时还有其它进程请求资源,则请求者只能等待,直至占有资源的进程用毕释放。
  • 请求和保持条件:指进程已经保持至少一个资源,但又提出了新的资源请求,而该资源已被其它进程占有,此时请求进程阻塞,但又对自己已获得的其它资源保持不放。
  • 不剥夺条件:指进程已获得的资源,在未使用完之前,不能被剥夺,只能在使用完时由自己释放。
  • 环路等待条件:指在发生死锁时,必然存在一个进程——资源的环形链,即进程集合{P0,P1,P2,···,Pn}中的P0正在等待一个P1占用的资源;P1正在等待P2占用的资源,……,Pn正在等待已被P0占用的资源。[引用自同样的某度]

3.示例代码

package com.dream.home_1104.thread;

//本程序演示一个简单的死锁现象
//产生死锁的原因:同步嵌套同步,但同步锁却不同

//自定义同步锁类-使用静态成员方便调用
class Lock{
    public static Object aLock=new Object();
    public static Object bLock=new Object();
}

//自定义线程类
class TestThread implements Runnable{
    private Object aLock=Lock.aLock;
    private Object bLock=Lock.bLock;
    private boolean flag;//分配执行路径的标识

    public TestThread(boolean flag){
        this.flag=flag;
    }

    public void run() {         
            if(flag){
                while(true){
                    synchronized(aLock){
                        System.out.println(Thread.currentThread().getName()+"执行路径1->alock...");         
                        synchronized(bLock){
                            System.out.println(Thread.currentThread().getName()+"执行路径1->block...");
                        }               
                    }
                }               
            }else{
                while(true){
                    synchronized(bLock){
                        System.out.println(Thread.currentThread().getName()+"执行路径2->block...");         
                        synchronized(aLock){
                            System.out.println(Thread.currentThread().getName()+"执行路径2->alock...");
                        }               
                    }
                }
            }

        }       
}


public class DeadthThreadManager {

    public static void main(String[] args) {            
        TestThread t1=new TestThread(true);
        TestThread t2=new TestThread(false);
        Thread th1=new Thread(t1);
        Thread th2=new Thread(t2);
        th1.start();         
        th2.start();
    }

}

运行结果:

Thread-0执行路径1->alock…
Thread-1执行路径2->block…

说明:在本例中,两条执行路径中,都存在着双重嵌套同步代码块,但syncheonized关键字中的同步锁顺序刚好相反。当两个线程分别进入两条执行路径后,分别获得aLock,bLock同步锁,都无法进入下层同步代码快,因此程序进入死锁状态。


2.线程间的通信

1.相关方法

void wait()
在其他线程调用此对象的 notify() 方法或 notifyAll() 方法前,导致当前线程等待。

void notify()
唤醒在此对象监视器上等待的单个线程。

void notifyAll()
唤醒在此对象监视器上等待的所有线程。

说明:

  • 以上方法继承自Object类
  • 以上只能用于同步情况下,因为要对持有监视器[锁]的线程操作。而只有同步中才有锁

这些操作线程的方法为什么要定义在Object中呢?

  • 因为这些方法在操作同步中线程时,都必须要标识他们所操作线程持有的锁
  • 只有同一个锁上的被等待线程,可以被同一个锁上的notify唤醒,不可以对不同锁中的线程进行唤醒。也就是说,等待和唤醒必须是同一个锁。(因此以上的方法必须由相应同步锁对象调用)

2.等待-唤醒机制

简单说明:

  • 在某些具体场景下,运用wait与notify实现线程间的切换和通信需求
  • 等待状态线程都存在线程池中,notify一般唤醒线程池中的第一个冻结线程

示例代码[双线程数据存取操作]:

package com.dream.home_1104.thread;
//本例演示一个简单地线程通信程序

//数据读取类
class DataOutput implements Runnable{
    private Data data;//Data类成员
    //构造方法
    public DataOutput(Data data){
        this.data=data;
    }
    public void run() {             
            while(true){
                data.showData();//调用Data类中的数据输出方法
            }                                               
    }   
}

//数据输入类
class DataInput implements Runnable{
    private Data data;//Data类成员
    private int x=0;//数据输入标识
    //构造方法
    public DataInput(Data data){
        this.data=data;
    }
    public void run() {
        while(true){                                    
                    if(x==0){
                        //调用Data类中的数据写入方法
                        data.setData("数据A",12);
                        x=1;
                    }else{
                        调用Data类中的数据写入方法
                        data.setData("数据B",24);
                        x=0;
                    }                    
                }                       
        }   
}

//数据类
class Data{
    private String name;
    private int age;
    private boolean flag=false;//是否具有数据的标识

    //数据写入方法-进行同步
    public void setData(String name,int age){
        synchronized(this){
            if(flag){
                try {this.wait();}catch(InterruptedException e){}
            } 
            //不再添加else判断语句
            this.name=name;
            this.age=age;   
            this.flag=true;
            this.notify();              
        }

    }
    //数据输出方法-进行同步
    public synchronized void showData(){
        if(!flag){
            try {this.wait();} catch (InterruptedException e) {}
        }
        //不再添加else判断语句
        System.out.println(name+":"+age);
        flag=false;
        this.notify();          
    }

    public void setFlag(boolean flag){
        this.flag=flag;
    }
    public boolean getFlag(){
        return flag;
    }
}

public class DataThreadManager {

    public static void main(String[] args) {         
        Data data=new Data();
        //数据输入线程
        Thread t1=new Thread(new DataInput(data));
        //数据读取线程
        Thread t2=new Thread(new DataOutput(data));
        t1.start();
        t2.start();
    }
}

运行结果:

….
数据B:24
数据A:12
数据B:24
数据A:12
数据B:24
数据A:12
….

所谓线程通信,其实就是应用于多个线程对同一对象进行操作,但各自的具体操作不同(如本例中数据的读与写)。多个线程之间通过wait与notify进行状态切换,达到线程安全的目的。在本例中即体现为先写入、后读取,循环往复。

3.生产者与消费者

特点:对于某一资源,生产者线程(可以有多个)生产一个,消费者线程(可以有多个)才消费一个。按生产-消费顺序先后进行。

示例代码:

package com.dream.home_1104.thread;

//本例演示生产者-消费者模式

//生产者线程类
class Create implements Runnable{
    private Resource re;//资源类对象
    public Create(Resource re){
        this.re=re;
    }
    public void run(){
        while(true){
                re.setData("示例名称");                 
        }       
    }
}

//消费者线程类
class Use implements Runnable{
    private Resource re;//资源类对象
    //构造方法
    public Use(Resource re){
        this.re=re;
    }

    public void run(){
        while(true){
            re.getData();
        }
    }
}

//数据存放类-资源类
class Resource{ 
    private boolean flag=false;//判断数据写入或者读取的标记
    private int count=1;//记录生成的资源的编号
    private String name;

    //写入数据的方法
    public void setData(String name){
        synchronized(this){
            if(flag){
                try{
                    this.wait();
                }catch(Exception e){}
            }
            this.name=name+count++; //将计数变量count加入name,便于后续区别        
            System.out.println(Thread.currentThread().getName()+" create->"+this.name);
            flag=true;
            this.notify();
        }       
    }
    //读取数据的操作
    public void getData(){
        synchronized(this){
            if(!flag){
                try{
                    this.wait();
                }catch(Exception e){}
            }
            System.out.println(Thread.currentThread().getName()+" use--->"+name);
            flag=false;
            this.notify();
        }       
    }
} 

//运行类
public class FactoryManager {

    public static void main(String[] args) {
         Resource re=new Resource();
         Create cr=new Create(re);
         Use us=new Use(re);
         new Thread(cr).start();//生产者线程      
         new Thread(us).start();//消费者线程
    }

}

运行结果:

Thread-0 create->示例名称8708
Thread-1 use—>示例名称8708
Thread-0 create->示例名称8709
Thread-1 use—>示例名称8709

说明:本例与上一示例的显著区别在于为数据加上编号进行区分,所想要体现的即是生产-消费的运行模式。但本例仅运行一个生产者线程和一个消费者线程,当生产者与消费者线程存在多个时,本例会发生异常的运行情况。请继续阅读…

异常情况引例(部分代码):

//运行类
public class FactoryManager {

    public static void main(String[] args) {
         Resource re=new Resource();
         Create cr1=new Create(re);
         Create cr2=new Create(re);
         Use us1=new Use(re);
         Use us2=new Use(re);
         new Thread(cr1).start();//生产者线程
         new Thread(us1).start();//消费者线程       
         new Thread(cr2).start();//生产者线程
         new Thread(us2).start();//消费者线程
    }

}

运行结果:

….
….
Thread-0 create->示例名称14521
Thread-2 create->示例名称14522
Thread-1 use—>示例名称14522
…
Thread-2 create->示例名称45523
Thread-1 use—>示例名称45523
Thread-3 use—>示例名称45523
…
…

分析:在本例中,仅仅是添加了一个生产者线程和一个消费者线程,运行结果却出现了特殊的运行情况。从部分运行结果中,可以看出存在生产一次、消费两次与生产两次、消费一次的异常情况。简单来说,是由于从线程池中通过notify被唤醒的冻结线程并未对本例中的标记flag进行判断造成的。

解决方案:将本例中对flag的判断由条件结构改为循环结构,则每次被唤醒的线程都将再次进行判断,避免多次生产或消费。被修改的代码片段如下。

代码片段:

//写入数据的方法
    public void setData(String name){
        synchronized(this){
            //改动:将if改为while
            while(flag){
                try{
                    this.wait();
                }catch(Exception e){}
            }
            this.name=name+count++; //将计数变量count加入name,便于后续区别        
            System.out.println(Thread.currentThread().getName()+" create->"+this.name);
            flag=true;
            this.notify();
        }       
    }
    //读取数据的操作
    public void getData(){
        synchronized(this){
            //改动:将if改为while
            while(!flag){
                try{
                    this.wait();
                }catch(Exception e){}
            }
            System.out.println(Thread.currentThread().getName()+" use--->"+name);
            flag=false;
            this.notify();
        }       
    }

运行结果:

Thread-0 create->示例名称1
Thread-3 use—>示例名称1
Thread-2 create->示例名称2
Thread-1 use—>示例名称2
Thread-2 create->示例名称3
Thread-1 use—>示例名称3
Thread-2 create->示例名称4
Thread-1 use—>示例名称4
…..

解决方案存在的不足:由于程序中每次只会通过notify唤醒一个冻结中的线程,程序可能进入线程全部冻结状态,程序运行停止

优化解决方案:将notify改为notifyAll ,每次将全部冻结中的线程唤醒。随后通过循环中的flag标记判断,令不需要唤醒的线程休眠。这样处理后就不会出现线程全部冻结的状态

代码片段:

//写入数据的方法
    public void setData(String name){
        synchronized(this){
            while(flag){
                try{
                    this.wait();
                }catch(Exception e){}
            }
            this.name=name+count++; //将计数变量count加入name,便于后续区别        
            System.out.println(Thread.currentThread().getName()+" create->"+this.name);
            flag=true;
            this.notifyAll();//改动:将notify修改为notifyAll
        }       
    }
    //读取数据的操作
    public void getData(){
        synchronized(this){
            while(!flag){
                try{
                    this.wait();
                }catch(Exception e){}
            }
            System.out.println(Thread.currentThread().getName()+" use--->"+name);
            flag=false;
            this.notifyAll();//改动:将notify修改为notifyAll
        }       
    }

总结:当出现多个生产者或消费者同时执行统一操作时,必须将对标记(本例中为flag)的if判断改为while循环,并使用notifyAll执行唤醒操作(以上代码使用notify只能用于生产者消费者仅有一个的情况)

问:对于多个生产者和消费者,为什么要定义while判断标记?
答:让运行中被唤醒的线程再进行一次标记判断

问:为什么要使用notifyAll?
答:因为需要唤醒对方线程;因为只用notify,容易出现只唤醒本方线程,导致程序中的所有线程都处于等待状态


3.Lock与Condition

1.概述

JDK1.5之后,增添了显式的锁机制。Lock替代了synchronized方法和语句的使用,Condition替代了Object监视器方法的使用。

新特性:一个锁上可以有多个Condition对象(通过Lock 对象调用newCondition方法)

使用方式:
Lock接口实现类:ReentrantLock(执行相应的上锁等操作)
Conditon接口:执行相应的线程等待等操作

说明:

  • 必须将释放锁的操作放入finally中,这样锁的释放操作一定执行。
  • 若方法中需要进行锁lock()操作,则该异常操作不通过catch处理,而在该方法后通过throws抛出异常

2.常用方法

Lock:

void lock()
获取锁

void unlock()
释放锁

Condition:

void await()
造成当前线程在接到信号或被中断之前一直处于等待状态。

void signal()
唤醒一个等待线程。

void signalAll()
唤醒所有等待线程。

3.示例代码

注:本例即对上例进行部分改写,其余部分不变

//数据存放类-资源类
//数据存放类-资源类
class Resources{    
    private boolean flag=false;//判断数据写入或者读取的标记
    private int count=1;//记录生成的资源的编号
    private String name;
    private Lock lock=new ReentrantLock();//锁    
    private Condition proCondition=lock.newCondition();//对应生产者
    private Condition cusCondition=lock.newCondition();//对应消费者

    //写入数据的方法
    public void setData(String name) throws InterruptedException{   
        lock.lock();//上锁    
        try{
            while(flag)
                proCondition.await();                           
                this.name=name+count++; //将计数变量count加入name,便于后续区别        
                System.out.println(Thread.currentThread().getName()+" create->"+this.name);
                flag=true;
                cusCondition.signal();
        }
        finally{
            lock.unlock();//解锁
        }           

    }
    //读取数据的操作
    public void getData() throws InterruptedException{       
            lock.lock();//上锁
            try{
                while(!flag)
                    cusCondition.await();           
                    System.out.println(Thread.currentThread().getName()+" use--->"+name);
                    flag=false;
                    proCondition.signal();
            }
            finally{
                lock.unlock();//解锁
            }               
    }
}

运行结果:

Thread-0 create->示例名称31091
Thread-1 use—>示例名称31091
Thread-2 create->示例名称31092
Thread-3 use—>示例名称31092
Thread-0 create->示例名称31093
Thread-1 use—>示例名称31093
Thread-2 create->示例名称31094
Thread-3 use—>示例名称31094
Thread-0 create->示例名称31095
Thread-1 use—>示例名称31095
…..

说明:在本例中,定义两个Condition对象分别对应生产者和消费者线程,分别执行await和signal操作。这一方式相当于在线程池中开辟两个独立的空间存放生产者线程和消费者线程,独立进行冻结唤醒操作。

4.Lock总结

  • 将同步synchronized替换成现实的Lock操作,更加直观
  • 将Object中的wait,notify,notifyAll替换为Condition对象的相应方法
  • 在一定程度上避免死锁现象的发生
  • 该实例中,实现了本方只执行唤醒对方操作,而不再同时唤醒双方对象,有利于节省系统资源

4.停止线程

前提:线程类的run方法中一般都会写出循环语句(因为这就是使用线程的原因)

停止原理(唯一的方式):
run方法结束。开启多线程运行,运行代码通常是循环结构。因此只要控循环停止条件,就可以让run方法结束,也就是线程结束

示例代码:

package com.dream.home_1104.thread;

//本例演示停止线程的方法

class StopThread implements Runnable{
    private boolean flag=true;
    private int count=0;//统计运行次数
    public void run(){
        while(flag){
            System.out.println("本线程大人还在死命地运行着..么么哒");
            count++;            
        }
    }
    //自定义停止线程的方法
    public void stopThread(){
        this.flag=false;
        System.out.println("线程结束...");  
    }
    //获取运行次数
    public int getCount(){
        return this.count;
    }
}

public class StopThreadManager {

    public static void main(String[] args) {         
        StopThread thread=new StopThread();
        new Thread(thread).start();
        //测试线程在运行五次后结束
        while(true){
            if(thread.getCount()==4){
                thread.stopThread();                
                break;
            }                   
        }           
    }

}

运行结果:

本线程大人还在死命地运行着..么么哒
本线程大人还在死命地运行着..么么哒
本线程大人还在死命地运行着..么么哒
本线程大人还在死命地运行着..么么哒
本线程大人还在死命地运行着..么么哒
线程结束…

特殊情况:当线程处于冻结状态,它就不会读取到改变后的标记,那么线程就不会结束。

异常引例(部分代码):

public synchronized void run(){
        while(flag){
            try{
                System.out.println("本线程休眠...");
                wait();             
            }
            catch(InterruptedException e){
                System.out.println("本线程发生中断异常");
            }
            System.out.println("本线程大人还在死命地运行着..么么哒");
            count++;            
        }
    }

运行结果:

本线程休眠…

说明:在本例中,自定义线程对象在run方法中进入冻结状态,即使通过主线程中的语句改变了flag,线程也不会停止。程序进入挂起状态,但程序并未结束

解决方案:当没有指定的方式让冻结的线程恢复到运行状态时,需要用interrupt方法对冻结状态进行清除,强制让冻结线程恢复到运行状态来,然后可以操作标记让线程结束。相关示例代码如下。

示例代码:

package com.dream.home_1104.thread;

//本例演示停止线程的方法

class StopThread implements Runnable{
    private boolean flag=true;
    private int count=0;//统计运行次数
    public synchronized void run(){
        while(flag){
            try{
                System.out.println("本线程休眠...");
                wait();             
            }
            catch(InterruptedException e){              
                System.out.println("本线程发生中断异常");                
            }
            System.out.println("本线程大人还在死命地运行着..么么哒");
            count++;            
        }
    }
    //自定义停止线程的方法
    public void stopThread(){
        this.flag=false;
        System.out.println("线程结束...");  
    }
    //获取运行次数
    public int getCount(){
        return this.count;
    }
}

public class StopThreadManager {

    public static void main(String[] args) {         
        StopThread thread=new StopThread();
        Thread t=new Thread(thread);
        t.start();
        //测试线程在运行五次后结束
        while(true){
            t.interrupt();//将冻结中的线程强行恢复到正常运行状态
            if(thread.getCount()==4){                
                thread.stopThread();                                
                break;
            }       
        }           
    }

}

运行结果:

本线程休眠…
本线程发生中断异常
本线程大人还在死命地运行着..么么哒
本线程休眠…
本线程发生中断异常
本线程大人还在死命地运行着..么么哒
本线程休眠…
本线程发生中断异常
本线程大人还在死命地运行着..么么哒
本线程休眠…
本线程发生中断异常
本线程大人还在死命地运行着..么么哒
本线程休眠…
本线程发生中断异常
本线程大人还在死命地运行着..么么哒
线程结束…

5.守护线程( 后台线程)

相关方法:

void setDaemon(boolean on)
将该线程标记为守护线程或用户线程。
该方法由线程对象

说明:

  • 该方法将调用线程标记为守护线程或用户线程
  • 当正在运行的线程都是守护线程时,Java 虚拟机退出
  • 该方法必须在启动线程前调用
  • 守护线程开启后会与前台线程抢夺CPU资源,但当前台线程全部结束后,后台线程自动结束

应用场景:某些线程依赖其他线程而存在时可以这样使用。如:输入程序结束后,读取线程也应该结束


6.join方法

相关方法:

void join()
等待该线程终止。

说明:调用该方法的线程强行抢夺CPU执行权,当前线程自动进入冻结状态。只有等到这一线程结束后,当前线程才会从冻结状态中恢复过来。

应用场景:某一线程需要临时加入线程执行或需要优先执行完成。

7.优先级与yield方法

相关方法:

void setPriority(int newPriority)
更改线程的优先级。

static void yield()
暂停当前正在执行的线程对象,并执行其他线程。(临时释放执行权)

String toString()
返回该线程的字符串表示形式,包括线程名称、优先级和线程组。

说明:

  • ThreadGroup 线程组:若A开启B,则B属于A线程组;例:由main()开启的线程就属于main线程组
  • 优先级(1-10):线程优先级越高,被执行的可能性越高。但一般1,5,10才有显著影响
  • 优先级高低中对应为:MAX_PRIORITY,MIN_PRIORITY,NORM_PRIORITY
  • 所有线程(包括main)默认优先级为5

8.实际开发中常见线程封装示例

思想:对于程序中需要同时执行的代码块(例如多个循环语句块),可以单独封装出多个线程。一般通过匿名内部类的方式实现,具体的有两种实现方式。示例代码如下。

示例代码:

package com.dream.home_1104.thread;

//本例通过三个循环结构演示实际开发中的多线程操作封装方式


public class RealThreadManager {

    public static void main(String[] args) {

        //第一个为正常的循环结构
        for(int i=0;i<300;i++){
            System.out.println(Thread.currentThread().getName()+"...第一个循环执行次数:-->"+i);
        }

        //第二个循环通过匿名内部类实现
        new Thread(){
            public void run(){
                for(int i=0;i<300;i++){
                    System.out.println(Thread.currentThread().getName()+"...第二次循环执行次数:-->"+i);
                }
            }
        }.start();

        //第三个循环通过接口方式实现
        Runnable r=new Runnable(){
            public void run(){
                for(int i=0;i<300;i++){
                    System.out.println(Thread.currentThread().getName()+"...第三次循环执行次数:-->"+i);
                }
            }
        };
        new Thread(r).start();
    }

}

运行结果:

….
Thread-1…第三次循环执行次数:–>2
Thread-1…第三次循环执行次数:–>3
Thread-0…第二次循环执行次数:–>54
Thread-1…第三次循环执行次数:–>4
Thread-0…第二次循环执行次数:–>55
Thread-1…第三次循环执行次数:–>5
Thread-1…第三次循环执行次数:–>6
….

说明:本例中存在三个线程,分别进行循环输出。实际开发中可任选其中一种线程封装方式。

别再死磕U-Net了!用PyTorch手把手教你搭建ResUnet,搞定医学图像分割(附完整代码) 本文详细介绍了如何利用PyTorch搭建ResUnet模型,通过引入残差连接解决医学图像分割中的梯度消失、特征退化和细节丢失问题。与传统U-Net相比,ResUnet在Dice系数和收敛速度上均有显著提升,特别适合处理脑部MRI等复杂医学图像。文章还提供了完整的代码实现和关键训练技巧,帮助开发者快速掌握这一先进技术。 阅读详情

相关推荐

oh-my-zsh详细安装与主题插件配置

使用Linux的小伙伴都知道默认shell是bash,但是对于喜欢折腾,喜欢酷炫的我们bash怎能够用。zsh的作为一种shell,功能更强大,但是配置复杂。后来,有大佬创建了一个名为oh-my-zsh的开源项目,使用方便,功能强大。 下面就去oh-my-zsh的配置。 ①安装zsh 想要安装oh-my-zsh,首先得安装zsh,输入命令 sudo apt-get install zsh 界面如下 ②查看是否安装成功 输入命令 zsh --version 可以查看安装zsh的版本..

惜洛-Jankin的博客 2万+

Java_synchronized线程锁解释

synchronized概念方法理解 概念 1.synchronized 关键字,代表这个方法加锁,相当于不管哪一个线程(例如线程A), 运行到这个方法时,都要检查有没有其它线程B(或者C、 D等)正在用这个方法(或者该类 的其他同步方法),有的话要等正在使用synchronized方法的线程B(或者C 、D)运行完 这个方法后再运行此线程A,没有的话,锁定调用者,然后直接运行。它包括两种用法: ...

万事胜意 431

Understand-Anything:AI驱动的代码知识图谱,实现项目全局洞察与高效导航

在软件开发中,理解复杂代码库的结构和依赖关系是提升开发效率的关键。传统代码阅读方式往往陷入局部视角,难以快速把握系统全貌。知识图谱技术通过将代码实体(如文件、类、函数)及其关系(调用、继承、依赖)可视化,为代码理解提供了全局视角。这项技术的核心价值在于将静态代码转化为动态、可交互的认知地图,极大降低了系统学习与维护成本。在实际工程中,它广泛应用于新成员快速入职、遗留系统梳理、架构影响分析等场景。本文介绍的Understand-Anything工具,正是基于这一理念,通过多智能体分析管道,自动构建代码的结构图

weixin_42615290的博客 886

【Java多线程】Synchronized和lock底层剖析

Compare and Swap,翻译成比较并交换。执行函数CAS(V,E,N),3个操作数,内存值V,旧的预期值E,要修改的新值N。当且仅当预期值E和内存值V相同时,将内存值V修改为N,否则什么都不做。AQS核心思想是,如果被请求的共享资源空闲,那么就将当前请求资源的线程设置为有效的工作线程,将共享资源设置为锁定状态;如果共享资源被占用,就需要一定的阻塞等待唤醒机制来保证锁分配。这个机制主要用的是CLH队列的变体实现的,将暂时获取不到锁的线程加入到队列中。

Sun_study的博客 782

用SOLIDWORKS从零画一个履带机器人,手把手教你搞定底盘和传动

本文详细介绍了如何使用SOLIDWORKS从零开始设计履带式机器人,重点讲解底盘结构优化和传动系统设计的关键步骤。通过七个阶段的实战指南,包括前期规划、底盘建模、驱动系统装配、履带生成、动态模拟等,帮助工程师和爱好者掌握履带机器人的核心设计技巧,提升地形适应能力和动力效率。

weixin_30872499的博客 632

英雄联盟(MOBA)完整制作教程

这是一份从零到上线、可落地的 LOL 风格 MOBA 游戏开发全流程教程,覆盖策划、美术、程序、测试、运营全环节,适合独立团队或学习参考。

qq_35779316的博客 544

Java并发学习(一)-synchronized及锁的学习

Index这些天开始学习Java并发相关知识,依靠买的些书籍以及网上大佬文章,去解读Java在设计并发方面的思想以及领略魅力。synchronized介绍估计有一部分同学接触到并发与同步的时候,基本就是了解一些操作系统方面的并行与并发,例如生产者消费者,银行家算法以及死锁。而去接触从代码角度去防止死锁与并发,就是在接触synchronized关键字的时候开始的,我自己也是一样。 对于synchro

anLA_的专栏 802

用Python 绘制一个只属于你自己的世界地图

Python之所以这么流行,是因为它不仅能够应用于科技领域,还能用来做许多其他学科的研究工具,绘制地图便是其功能之一。 今天我们用matplot工具包之一的mpl_toolkits来绘制世界地图,这是一个简单的可视化工具,如果希望绘制更加复杂的地图,可以考虑使用Google Maps API,不过这不在我们今天的讨论范围之内。 1.安装 为了能够顺利开展本项目,你需要先安装以下依赖,在cmd或Terminal中输入以下命令: pipinstallnumpy pipinstallm...

qq_38887171的博客 1872

synchronized与Lock的区别

两者区别: 1.首先synchronized是java内置关键字,在jvm层面,Lock是个java类; 2.synchronized无法判断是否获取锁的状态,Lock可以判断是否获取到锁; 3.synchronized会自动释放锁(a线程执行完同步代码会释放锁 ;b 线程执行过程中发生异常会释放锁),Lock需在finally中手工释放锁(unlock()方法释放...

weixin_30722589的博客 495

quartusII简易CPU设计

优点可以实现八种运算,数据的存取及运算都能很好的实现,上板也很成功。缺点首先由于自己知识储备的缺乏,只能实现较为简单的8种运算,也是最简单的直接寻址。其次为了方便操作,没有从外部输入数据,都是直接使用存储在存储器中的数据。后续发展后续会在此简易CPU的基础上增加一些功能对CPU进行完善,如算术运算可以考虑是补码运算,输入、输出采用存储缓冲等等,直至完整的全功能的处理器。收获通过这次实践,更熟悉Quartus软件的操作。能过更加清楚每个器件的操作,比如计数器,运算器,译码器等的实现功能。

Kong08242022的博客 3484

华为2022数字芯片笔试题

单选 1、ASIC开发流程中,如下环节的先后顺序是? A. RTL-> Synthesis->P&R B. Synthesis->P&R->RTL C. Synthesis->RTL->P&R D.RTL->P&R->Synthesis 2、o10换算成十进制是多少? A. 8 B.16 C.10 D.2 3、reg [7:0] mem [1:256];initial $readmemh("me..

weixin_44072819的博客 1万+

synchronized 和Lock区别

区别如下: 来源: lock是一个接口,而synchronized是java的一个关键字,synchronized是内置的语言实现; 异常是否释放锁: synchronized在发生异常时候会自动释放占有的锁,因此不会出现死锁;而lock发生异常时候,不会主动释放占有的锁,必须手动unlock来释放锁,可能引起死锁的发生。(所以最好将同步代码块用try catch包起来,finall...

洞玄之境的博客 12万+

Aix升级和安装ssh配置过程

Aix升级和安装ssh配置过程Aix升级和安装ssh配置过程配置前背景配置步骤 Aix升级和安装ssh配置过程 部门IBM小型机的Aix操作系统需要进行ssh补丁修复,修复其实就是ssh版本你的升级,对升级的过程和中途遇到的问题做了记录如下。 配置前背景 操作系统: gdboss2/>uname -u //操作系统的Aix5.3 IBM,020693E5P gdboss2/&gt...

阿阿阿唱的博客 2993

Java基础之线程初接触

进程的定义:一个正在执行中的程序 每一个进程执行都有一个执行顺序。该顺序是一个执行路径,或者叫一个控制单元。2.线程的定义:进程中的一个独立的控制单元,线程控制着进程的执行。3.进程中至少有一个线程(且java中该线程的代码存在于main方法中,因此称其为主线程)4.使用多线程的意义....

CodingEnding的博客 580

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

如何学习Java基础

我第一次接触Java还是在大三的课堂上,当时有一门课程就是Java,教的都是Java基础,在Java之前我还稍微了解过html、css这类前端技术,所以自认为学习Java应该不会有太大难度。 结果证明我还是想多了,刚开始老师就在介绍Java的基本数据类型,比如int的长度,double的长度,作为一个非科班程序员,当时甚至连1个字节是多少都不知道。 接着我们又学习了Java的数组,数组这个概念本来看起来没什么,但是接下来又出现了二维数组,老师还结合着数组的内存分配来和我们讲解,当时就晕了,于是就这么半推

133

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

初识JAVA

在实际编程中,我可以将不同的功能封装到不同的类中,每个类都有自己的职责和功能,这样不仅提高了代码的复用性,还使得代码的维护和扩展变得更加容易。例如,在开发一个简单的学生管理系统时,我可以将学生、教师等不同的角色抽象成不同的类,通过类之间的继承和关联关系,实现系统的各项功能。在学习过程中,我可以在不同的操作系统上编写和运行 Java 程序,而不用担心兼容性问题,这为我的学习和实践提供了很大的便利。我相信,在未来的学习和实践中,我会与 Java 结下更深的缘分,不断探索和发现它的更多奥秘。

2403_88329066的博客 466

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

Java编程驿站

最近在一个新的公司接触到了很多在以前公司没有接触到的东西,这些东西也是java开发最基础的东西,但是自己用起来很生硬,原因在于当然没有好好学肯定是第一名,至于其他的则是以前的2年时间不怎么开发,要开发也只是web层面的一些框架之类的只要会使用就ok了,但是现在用到的东西都是java非常基础的东西,比如一个简单的计时器,监视器,到我软肋的多线程处理。我喜欢java的原因就是没有那么多现成的东西让你去...

weixin_30600503的博客 126

我看谁还在瞎卷?阿里多位大咖级联合撰写的 Java 多线程笔记,只讲重点

程序员这个行业持续学习已经成为了常态,去大厂镀金可以为你的履历增光添彩,但更重要的是在大厂你能学到更多的东西,这才是你以后和别人竞争工作岗位的保障。

Q54665642ljf的博客 83

阿里内网资料泄露:多位大佬联合撰写的Java多线程手册被我拿到了

俗话说得好:不想进大厂的程序员不是好程序员 为什么这么多人前赴后继的奔赴大厂呢? 大厂的薪水很高 有大厂背景以后找工作会容易很多 大厂内良好的氛围 完善的生态体系 技术最前沿 …… 最后总结下来你会发现,大厂的薪水不一定是最高的,但他能给一个很好的发展前景 举个例子:阿里,在这里你会遇到很多很多的大牛,那种技术交流的氛围你在中小厂是很难感受到的,在大厂你得到的提升会很大很大。这里不得不说一下阿里内网的资料,很多都是在外面看不到的。 今天分享的这份多线程资料就是多位阿里大佬联合撰写.

互联网架构小马的博客 137

对JAVA学习的一次小小总结

从14年学习开始学习JAVA也有些时日,在学校里零零散散也学了些东西,但真正起步还是在华信那边。经过不多不少一个月的学习,让我对编程还有软件背后的代码感兴趣了。学习的过程当然是枯燥的,有时候一个小小的BUG能折腾好久才出来,我就是属于那种刨根问底型的。有时候学长都被我问怕了,但渐渐的感觉有些东西当时领悟不到,但随着时间的积累,慢慢的就自然而然的体会到了。说白了,还得坚持不懈。 ...

silence2015的专栏 705

2022/06/22与23 day07与day08:异常、线程

异常、线程

w2079930908的博客 162

耗时3个月终于整理完了“Java八股文背诵版“,堪称2023八股文天花板

都说程序员工资高、待遇好,改变无数程序员职业生涯的金三银四已来,你的小目标是什么 30K、40K,还是 16 新的 20K?作为一名 Java 开发工程师,当能力可以满足公司业务需求时,拿到超预期的奖励 Offer 并不算难。然而,提升 Java 核心能力最快、最有效,短期内升职加薪的方法,到底是什么?首先,你需要跳出日常工作,接触更有深度、更前沿的顶级项目。一个简单的逻辑:大厂之所以能够给到高于行业水准的薪资,正是因为即使是普通开发人员,也要应对很多复杂的场景。

知识分享官 476

用Java复刻《天天酷跑》!零基础游戏开发实战(附避坑指南)

那些枯燥的抽象类、接口,在让角色跳起的那一刻突然有了灵魂。如果你在实现过程中卡住,记住:每个error都是升级的机会!遇到问题欢迎在评论区交流~(当然要先自己查文档哦))与其对着控制台打印三角形发呆,不如跟我用2天时间做个横版跑酷游戏!刚学完Java语法就迷茫?if/else、for循环都在作业里见过,但。:一定要在独立线程运行循环!否则界面会卡成PPT(别问我怎么知道的😭):一定要在EDT线程操作UI!

logicwizard的博客 432

近两周学习计划

以java学习为主,加快java知识的学习,开始向着项目方向靠近,开始逐步了解相关知识,构建项目框架,多进行实践.2.<疯狂java讲义>第七章java基础库和第八章java集合(241~332页)辅佐视频进行。2.<疯狂java讲义>第七章java基础库和第八章java集合(241~332页)辅佐视频进行。4.逐步接触一些java小项目,整体了解项目框架。3.学习javaFx的用法,掌握项目相关知识。5.成2次Codeforce比赛。4.成2次Codeforce比赛。5.每天1~2道算法题。

sin1810335764的博客 352

Java线程 初次接触

进程:计算机执行的单元块,一个进程有多个线程。 线程:计算机最小执行的单元块 实现线程的两种方法:继承,重写 01-继承实现 public class Runa { public static void main(String[] args) { /*返回当前进程对象的方法*/ System.out.println(Thread.currentT...

你的调料包 185

【多线程与高并发】- 线程基础与状态

多线程也是一项比较重要的内容,虽然CRUD不太会接触到,但是,在一些相关场景可能会有某些问题是由于线程导致的。简单理解就是一个进程中包含了许多个线程。

qq_43843951的博客 21万+
上一篇: Java知识点之单例设计模式
下一篇: Java基础之String详解
CodingEnding
博客等级 码龄11年 86粉丝 49原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值