海阔天空

当前时间为:
欢迎大家来到海阔天空https://www.9713job.com,广告合作以及淘宝商家推广请微信联系15357240395

2020java教程:多线程

未分类
2020-08-28 15:09:04
1822677238@qq.com

手机扫码查看

2020java教程:多线程

多线程

要想了解线程必须先了解进程
什么是进程
程序是禁止的,只有真正运行程序,才被称为进程。
单CPU在任何时间点上,只能运行一个进程;宏观并行,围观串行。

什么是线程
线程,又称轻量级进程(Light Weight Process)。
程序中的一个顺序控制流程,同时也是CPU的基本调度单位。进程由多个线程组成,彼此间完成不同的工作,交替执行,称为多线程。
如 迅雷是一个进程,当中的多个下载任务即是多个线程。
如 java虚拟机是一个线程,当中默认包含主线程(Main),可通过代码创建多个独立线程,与Main并发执行。

线程的组成
任何一个线程都具有基本的组成部分:
CPU时间片:操作系统(OS)会为每个线程分配执行时间。
运行数据:
堆空间:存储线程需使用的对象,多个线程可以共享堆中的对象。
栈空间:存储线程需使用的局部变量,每个线程都拥有独立的栈。
线程的逻辑代码。

创建线程:
创建线程的第一种方法:

public class demos{
    public static void main(String[] args) {
        MyThead t1=new MyThead();//3.创建子类对象
        t1.start();//4.调用start()方法
        System.out.println("程序结束");
    }
}
//1.继承Thread类
class MyThead extends Thread{
    //2.覆盖 run()方法
    @Override
    public void run() {
        for(int i=0;i<=10;i++){
            System.out.println("MyThread:"+i);
        }
    }
}

创建线程的第二种方法:

public class demos{
    public static void main(String[] args) {
        //3.接口引用指向实现类对象,形成多态
        Runnable task=new MyThread();
        //4.创建线程,将任务提交给线程
        Thread t1=new Thread(task);
        Thread t2=new Thread(task);
        //5.调用start()方法让线程启动
        t1.start();
        t2.start();
        System.out.println("程序结束");
    }
}
//1.实现 Runnable接口
class MyThread implements Runnable{
    //2.覆盖 run()方法
    @Override
    public void run() {
        for(int i=0;i<=5;i++){
            System.out.println("MyThread:"+i);
        }
    }
}

线程的状态

线程的状态(基本)

  • new
    至今尚未启动的线程处于这种状态
  • RUNNABLE
    正在java虚拟机中执行的线程处于这种状态
  • BLOCKED
    受阻塞并等待某个监视器锁的线程处于这种状态
  • WAITING
    监视器锁另一个线程来执行某一特定操作的线程处于这种状态
  • TIMED WAITING
    等待另一个线程来执行取决于指定等待时间的操作的线程处于这种状态
  • TERMINATED
    已退出的线程处于这种状态

常用方法
休眠
public static void sleep(毫秒)
当前线程主动休眠毫秒数
放弃:
public static void yield()
当前线程主动放弃时间片,回到就绪状态,竞争下一次时间片
结合:
public final void join()
允许其他线程加入到当前线程中

 

线程的状态(等待)

线程安全问题
需求:A线程将”Hello”存入数组第一个位置;B线程将”World”存入数组的第一个位置
String[] s=[null,null,null,null];

A线程:查找下标0,时间片到期
B线程:查找下标0,将world存入下标0
String[] s=[“world”,null,null,null];
A线程:将Hello存入下标0
String[] s=[“Hello”,null,null,null];

线程不安全
当多线程并发访问临界资源时,如果破坏原子操作,可能会造成数据不一致。
临界资源:共享资源(同一对象),一次仅允许一个线程使用,才可保证其正确性。
原子操作:不可分割的多步操作,被视作一个整体,其顺序和步骤不可打乱或缺省。

在应用程序当中,如何保证线程的安全性?

同步方式
第一种同步方式:同步代码块
synchronized(临界资源对象){//对临界资源对象加锁
//代码(原子操作)
}
注:
每个对象都有一个互斥锁标记,用来分配给线程的。
只有拥有对象互斥锁标记的线程,才能进入对该对象加锁的同步代码块。
线程退出同步代码块时,会释放相应的互斥锁标记。互斥锁标记

线程的状态(阻塞)

同步方式2
同步方法
synchronize 返回值类型 方法名称(形参列表){//对当前对象this加锁
//代码
}
注:
只有拥有对象互斥锁标记的线程,才能进入该对象加锁的同步方法中。
线程退出同步方法时,会释放响应的互斥锁标记。

同步规则
注意:
只有在调用包含同步代码块的方法,或者同步方法时,才需要对象的锁标记。
如调用不含同步代码块的方法,或普通方法时,则不需要锁标记,可直接调用。

已知JDK中线程安全的类:
StringBuffer、Vector、Hashtable,方法均为synchronize修饰。

死锁
当第一个线程拥有A对象锁标记,并等待B对象锁标记,同时第二个线程拥有B对象锁标记,并等待A对象锁标记时,产生死锁。
一个线程可以同时拥有多个对象的锁标记,当线程阻塞时,不会释放已经拥有的锁标记,由此可能造成死锁。

生产者、消费者
若干个生产者在生存产品,这些产品将提供给若干个消费者去消费,为了使生产者和
消费者能并发执行,在两者之间设置一个能存储多个产品的缓冲区,生产者将生产的
产品放入缓冲区中,消费者从缓冲区中取走产品进行消费,显然生产者和消费者之间
必须保持同步,即不允许消费者到一个空的缓冲区中取产品,也不允许生产者向一个满的
缓冲区中放入产品

线程通信
等待
public final void wait()
public final void wait(long timeout)
必须在对obj加锁的同步代码块中,在一个线程中,调用obj,wait()时,此线程会释放其拥有的所有锁标记。同时此线程处在无限期等待的状态中。释放锁,进入等待队列。
通知
public final void notify()
public final void notifyAll()
必须在对obj加锁 同步代码块中,从obj的 waiting中释放一个或全部进程。对自身没有任何影响。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注