栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

java多线程基础

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

java多线程基础

多线程 1、线程介绍

(1)首先区分程序、进程和线程的概念

1、程序:程序首先是一个静态的概念(例如我们写的代码程序),只能串行执行,是永存的,且不是竞争计算机资源的基本单位。

进程:进程是一个动态的概念,可以并发执行。进程的存在是暂时的,有创建有撤销,是竞争计算机资源的基本单位。进程的生命周期中三个状态:就绪、运行、阻塞

进程与程序的关系:

一个程序产生一个进程:比如Win10的记事本程序,每打开一个txt文本文件,就只会启动一个记事本进程。

一个程序可以被多个进程共用:比如一个记事本程序在执行时,就只会产生一个进程。但当我们再用记事本程序打开一个文件时,此时就会再次在操作系统中创建一个新的进程,这个新的进程同样也会调用记事本程序。即在此刻,计算机磁盘中只有一个记事本程序,但是操作系统中却有两个记事本进程在共用这个程序,且这两个进程互不影响。

一个程序产生多个进程:比如浏览器启动时,一般都会产生多个进程,这些进程相互配合,互相影响,共同实现浏览器的功能。

线程:一个进程包含一个(main函数中的主线程)或者多个线程,线程是CPU调度和执行的基本单位。

多线程:

单线程的时候是只有一条主线程执行,而多线程的时候是主线程和子线程交替并行执行,执行效率比单线程高。当多个线程操作同一个资源的时候会出现线程不安全的情况。

守护线程和非守护线程:守护线程是为非守护线程服务的,是一种特殊的线程。典型的守护线程就是垃圾回收线程,当所有的非守护线程不存在是,守护线程就不会被杀死。

2、多线程创建

1、继承Thread类

public class ThreadDemo1 extends Thread {
    public static void main(String[] args) {
        ThreadDemo1 threadDemo1=new ThreadDemo1();
        threadDemo1.start();
        for (int i = 0; i < 20; i++) {
            System.out.println("主线程--"+i);
        }
    }
    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            System.out.println("子线程--"+i);
        }
    }
}

上述程序中是创建线程的第一种方式,继承thread类,住主线程和子线程交替执行,每个线程都有优先级,优先级高的线程优于优先级较低的线程执行。

2、实现Runnable接口

public class ThreadDemo2 implements Runnable {
    public void run() {
        for (int i = 0; i < 20; i++) {
            System.out.println("子线程--"+i);
        }
    }
​
    public static void main(String[] args) {
        ThreadDemo2 threadDemo2=new ThreadDemo2();
        Thread demo2=new Thread(threadDemo2);
        demo2.start();
        for (int i = 0; i < 20; i++) {
            System.out.println("主线程--"+i);
        }
    }
}

实际上thread类也是实现了Runnable接口。Runnable接口避免了单继承的局限性

3、实现Callable接口。

3、理解Thread类(静态代理模式)

静态代理主要分为三个角色:真实对象、代理对象、抽象接口(真实对象和代理对象都需要实现的接口),在Thread类中,Thread类是代理类,Runnable接口是都需要实现的接口,我们自定义的Runnable实现类就是真实的对象。

// thread是代理类,threadDemo2是真实对象,他俩都实现了共同的接口Runnable接口
new Thread(threadDemo2).start();
4、线程状态

1、Sleep方法可以使得线程休眠,且Sleep不会释放锁,是占用CPU的进行休眠,Sleep方法是静态方法可以直接使用Thread.Sleep()进行调用。

2、Yield方法:如下图所示,当前有A、B两个线程,A线程被CPU调度,然后执行,但是如果A执行了Yiled方法则会使得A、B两个线程重新竞争资源,并不是A调用该方法B方法就一定会执行。因此,使用yield()的目的是让相同优先级的线程之间能适当的轮转执行。但是,实际中无法保证yield()达到让步目的,因为让步的线程还有可能被线程调度程序再次选中。Yield会让出CPU的调度

3、Join方法:可以比喻成插队(强制执行),通过下面代码进行理解

public class JoinDemo implements Runnable {
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println("子线程"+i);
        }
​
    }
    public static void main(String[] args) throws InterruptedException {
        JoinDemo joinDemo=new JoinDemo();
        Thread thread=new Thread(joinDemo);
        thread.start();
        for (int i = 0; i < 20; i++) {
            System.out.println("主线程"+i);
            if(i==10){
                thread.join();
            }
        }
    }
}

代码中有子线程和主线程,当主线程执行到10次的时候,子线程调用该方法进行强制执行,然后等到子线程执行完之后,主线程继续执行。执行结果如下:

主线程0 主线程1 主线程2 主线程3 主线程4 主线程5 主线程6 主线程7 主线程8 主线程9 主线程10 (此时开始执行子线程)子线程0 子线程1 子线程2 子线程3 子线程4 子线程5 子线程6 子线程7 子线程8 子线程9 (子线程执行完,继续执行主线程)主线程11 主线程12 主线程13 主线程14 主线程15 主线程16 主线程17 主线程18 主线程19

5、线程的同步

问题:当多个线程同时操作一个对象的时候会出现线程不安全的现象,比如如果100个人同时去买一张火车票,且这100个人都能够成功购买,那么火车票的数量就会变为-99张,这样的话就出现了线程不安全的现象。

方案:此时,需要使用synchronized关键字进行线程的同步,线程同步的意思是可以保证在同一时刻,只有一个线程可以执行某个方法或某个代码块,回到买票的例子,如果进行线程同步之后,那么当第一个人进行买票的时候,其余的人只能等待不能购买,等到第一个人买完成功之后,其余的人才能进行买票。这样的话就会让多个线程之间排队,一个一个对共享资源进行操作,而不是同时进行操作。

public class SynDemo1 {
    public static void main(String[] args) {
        BuyTicket buyTicket=new BuyTicket();
        new Thread(buyTicket,"one").start();
        new Thread(buyTicket,"two").start();
        new Thread(buyTicket,"three").start();
    }
}
​
class BuyTicket implements Runnable{
    boolean flag=true;
    int ticket=10;
    public void run() {
        try {
            Thread.sleep(1000);
            while(flag){
                buy();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    //买票的方法
    public void buy(){
        if(ticket>0){
            System.out.println(Thread.currentThread().getName()+"卖到"+ticket--+"张票");
        }else{
            flag=false;
            return;
        }
    }
}

在运行结果中出现了three和one同时买到了第十张票。

如何解决这种情况呢?需要在方法上添加synchronized

//买票的方法
public synchronized void buy(){
    if(ticket>0){
        System.out.println(Thread.currentThread().getName()+"卖到"+ticket--+"张票");
    }else{
        flag=false;
        return;
    }
}

除了使用synchronized锁定方法,还可以锁定特定的对象(synchronized代码块),即可以锁定其中的一部分,比如变量、实例对象、类对象。

synchronized(需要锁定的对象){
​
}
6、死锁

死锁:是多个线程同时占有一些共享资源,且只能等待其他线程释放当前资源的时候才能执行,比如线程A占用资源x,线程B占用资源y,当线程A释放资源x,线程B才能运行,且线程B释放资源y,线程A才能执行。这样的话就形成了死锁。

(1)互斥条件:在一段时间内某个资源仅为一个进程所占有(比如:打印机,同一时间只能一个人打印)。此时若有其他进程请求该资源,则请求只能等待,直到有资源释放了位置;

(2)请求和保持条件:进程已经持有了一个资源,但是又要访问一个新的被其他进程占用的资源那么就会阻塞,并且对自己占用的一个资源保持不放;

(3)不剥夺条件:进程对已经获取的资源未使用完之前不能被剥夺,只能使用完之后自己释放。

(4)循环等待条件:存在一种进程资源的循环等待链,链中每一个进程已获得的资源同时被链中下一个进程所请求。

(部分图是网络中的图)

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/822036.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号