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

java---多线程

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

java---多线程

java—多线程

进程与线程的区别:一个进程可以有多个线程

****进程:****是并发执行的程序在执行过程中分配和管理资源的基本单位,是一个动态概念,竞争计算机系统资源的基本单位。

正在运行中的应用程序,通常称为进程。每个进程都有自己独立的地址空间(内存空间),每当用户启动一个进程时,操作系统就会为该进程分配一个独立的内存空间,让应用程序在这个独立的内存空间中运行。

****线程:****是进程的一个执行单元,是进程内科调度实体。比进程更小的独立运行的基本单位。线程也被称为轻量级进程。线程是一个轻量级的子进程,是最小的处理单元;是一个单独的执行路径。可以说:线程是进程的子集(部分)。

​ ****关系:****一个程序至少一个进程,一个进程至少一个线程。

线程的创建方式

java中常见的创建方式有三种:

    继承Thread类创建线程类继承Runnable接口创建线程类通过Callable和Future创建线程
继承Thread类创建线程类

继承Thread类并重写run方法。

public class Demo1 {

    public static void main(String[] args) throws IOException, ClassNotFoundException {
        MyThread thread1 = new MyThread(), thread2 = new MyThread();
        thread1.start();
        thread2.start();
    }
}
class MyThread extends Thread {
    @Override
    public void run() {
        for(int i=0;i<5;i++) {
            System.out.println("这是"+Thread.currentThread().getName()+"第"+i+"次输出");
            try {
                Thread.sleep(20);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
继承Runnable接口创建线程类

继承Runnable接口并实现run方法。然后将该类的对象作为参数传入Thread的构造方法中,然后调用Thread对象的start方法

public class Demo1 {

    public static void main(String[] args) throws IOException, ClassNotFoundException {
        MyThread thread1 = new MyThread();
        Thread t1 = new Thread(thread1), t2 = new Thread(thread1);
        t1.start();
        t2.start();
    }
}
class MyThread implements Runnable {

    int i=0;
    @Override
    public void run() {
        for(;i<10;i++) {
            System.out.println(Thread.currentThread().getName()+"正在输出:"+i);
            //Thread.sleep(20);
        }
    }
}
通过Callable和Future创建线程

先创建一个继承了Callable接口的类,将该类的实例对象作为参数传入FutureTask构造函数,然后将FutureTask对象作为参数传入Thread构造方法。最后调用Thread的start方法。

public static void main(String[] args) throws IOException, ClassNotFoundException, ExecutionException, InterruptedException {
        MyThread myThread = new MyThread();
        FutureTask futureTask = new FutureTask<>(myThread);
        Thread thread = new Thread(futureTask);
        for(int i=0;i<10;i++) {
            if(i==5) {
                thread.start();
            }
        }
        System.out.println("线程的返回值为:"+futureTask.get());

    }
}
class MyThread implements Callable {

    @Override
    public Integer call() throws Exception {
        System.out.println(Thread.currentThread().getName()+"正在执行!");
        return 55;
    }
}
三种方式的对比

由于java只实行类的单继承,所以对于继承了Thread类的类而言就不能再继承其他类了,但是实现Runnable或者Callable接口后任可以继承其他类。在这种方式下,多个线程可以共享同一个target对象,所以非常适合多个相同线程来处理同一份资源的情况,从而可以将CPU、代码和数据分开,形成清晰的模型,较好地体现了面向对象的思想。

在开发中,优先选择实现Runnable接口的方式,如果需要有返回值,则实现Callable接口。

注意!!!:关于start和run,thread.run()只是调用一个方法,而thread.start()会启用一个线程。

线程的相关方法

start:启动当前线程并调用当前线程的run方法getName() :获取线程的名字setName:设置线程的名字yield:释放当前CPU的执行权,当当前线程任能继续参与执行权的抢夺join:在a线程中调用b.join()后a线程进入阻塞状态,直到b线程结束,也可以在join中加入1个参数p1,代表最多等待p1毫秒。或者是加入两个参数p1,p2,代表最多等待p1毫秒 加 p2 纳秒sleep:让当前线程停止执行一段时间,如果是一个参数,代表毫秒,如果是两个参数代表毫秒和纳秒。isAlive判断这个线程是否还活着activeCount:返回当前程序中的活动线程数 就绪+阻塞+运行currentThread:静态方法,返回执行当前代码的线程wait:这是Object类的方法。使当前线程等待,直到其他线程调用此对象的notify或notifyAll方法,或者是指定的等待时间已过 线程的生命周期

线程的生命周期有五种状态:

    新建 New就绪 Runnable运行 running阻塞 Blocked死亡 Dead
多线程的安全问题与解决办法 产生安全问题的原因

试想这样一种情况,假如一共有10张票,两个线程同时售卖,对于每个线程的过程如下所示:

首先判断是否还有票

如果没有票了,直接结束如果还有票,卖出一张票,然后将票数减一

问题就出在判断的这里,假如已经,卖了9张票了,还剩最后一张,线程A首先进行判断,发现还有票,所以进入第二个判断体。但是A还没将票数减一的时候B也开始进行判断,发现还有票,同样进入卖票的程序,这样就会导致卖出了11张票,最终的票数为-1,这显然是不合理的。

解决办法:

对于共享变量,同时只能让一个线程修改。也就是同步

1)同步代码块

即让某段代码只能同时被一个线程执行

while(true) {
            synchronized (t1) {
                if(tickets>0) {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + "正在出售第" + tickets-- + "张票");
                }
                else {
                    Thread.currentThread().interrupt();
                }
            }
        }

​ t1可以是任意一个对象,我这里是Interger t1 = new Integer(0);

由于两个线程的同步对象都是t1,所以这两个线程同时只有一个能够执行这个代码块。

2)同步方法

其实就是用关键词synchronized修饰一个方法。被修饰的方法同时只能被一个线程执行。

3)lock锁

在java.util.concurrent.locks.lock包下有一个Lock接口,ReentrantLock是他的一个实现类。我们主要使用

lock 获得锁unlock 释放锁的操作放在finally块中进行,以保证锁一定被被释放,防止死锁的发生。

方法就行了,在lock与unlock之间的就是同步代码块。

同步是一种高开销的操作,因此应该尽量减少同步的内容。 通常没有必要同步整个方法,使用synchronized代码块同步关键代码即可。

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

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

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