[学习笔记] Java面向对象之多线程

# 学习 # · 2020-07-01

线程概述

1、进程:是系统运行程序的基本单位,每一个进程都有自己独立的一块内存空间、一组系统资源,每一个进程的内部数据和状态都是完全独立的。

2、线程:CPU调度和分派的基本单位,进程中执行运算的最小单位,可完成一个独立的顺序控制流程。


主线程

1、public static void main()方法即为主线程入口。

2、主线程产生其他子线程的线程。

3、主线程必须最后完成执行,因为它执行各种关闭动作。

public static void main(String args[]) {
    Thread t = Thread.currentThread();   //获得主线程对象
    System.out.println("当前线程是: "+t.getName());
    t.setName("MyJavaThread");   //设置线程名
    System.out.println("当前线程名是: " + t.getName());   //获取线程名
}

线程的创建和启动

1、Thread类:提供大量方法控制和操作线程。

void run()      //执行任务操作的方法,只有主线程一条执行路径
void start()    //使线程开始执行,Java虚拟机调用该线程的run()方法,多条执行路径,主线程和子线程并行交替执行。
void sleep(long millis)    //线程休眠,暂停执行
void getName()      //返回线程名称
void getPriority()    //返回线程优先级
void setPriority(int new)    //更改线程优先级
static Thread currentThread()    //返回当前正在执行的线程对象的引用
boolean isAlive()    //测试线程是否处于活动状态
void join()  //等待线程终止
void interrupt()  //中断线程
void yield()    //暂停当前正在执行的线程对象,并执行其他线程

2、继承java.lang.Thread类:

(1)多个线程交替执行,不是真正的“并行”,线程每次执行时长由分配的CPU时间片长度决定。

(2)特点:编写简单,可直接操作线程,适用于单继承。

//步骤一:定义MyThread类继承Thread类
//步骤二:重写run()方法
//步骤三:创建线程对象,调用start()方法启动线程
public class MyThread extends Thread{
    public void run(){
        for(int i=1;i<100;i++){
            System.out.println(Thread.currentThread().getName()+":"+i);
        }
    }
}

public static void main(String[] args) {
    MyThread thread = new MyThread();
    thread.start();
}

2、实现Runnable接口:

(1)特点:避免单继承局限性,便于共享资源。

//步骤一:定义MyRunnable类实现Runnable接口
//步骤二:实现run()方法
//步骤三:创建MyRunnable类的对象myRunnable
//步骤四:创建一个Thread类的对象myThread,将myRunnable对象作为Thread类构造方法的参数传入
//步骤五:调用start()方法启动线程
public class MyRunnable implements Runnable{
    public void run(){
        for(int i=1;i<100;i++){
            System.out.println(Thread.currentThread().getName()+":"+i);
        }
    }
}

public static void main(String[] args) {
    MyRunnable myRunnable = new MyRunnable();
    Thread myThread = new Thread(myRunnable);
    thread.start();
}

线程状态

1、创建状态:新的线程对象处于创建状态。

2、就绪状态:线程创建后,通过调用start()方法启动线程,线程进入线程队列排队,等待CPU资源。

3、运行状态:就绪的线程获得CPU资源,转入运行状态,执行run()方法。

4、阻塞状态:一个正在运行的线程因某种原因不能继续运行时进入阻塞状态。


线程调度

1、线程的优先级:setPriority(int newPriority)

(1)线程优先级由1~10表示,1最低,默认优先级为5。

(2)优先级高的线程获得CPU资源的概率较大。

public static void main(String[] args) {
    Thread t1 = new Thread(new MyThread(),"线程A");    //通过构造方法指定线程名
    Thread t2 = new Thread(new MyThread(),"线程B");
    //设置线程的优先级
    t1.setPriority(Thread.MAX_PRIORITY);
    t2.setPriority(Thread.MIN_PRIORITY);
    System.out.println("****线程的优先级****");
    System.out.println("线程A的优先级:"+t1.getPriority());
    System.out.println("线程B的优先级:"+t2.getPriority());
    System.out.println("****************");
    t1.start();
    t2.start();
}

2、线程的休眠:static void sleep(long millis)

(1)让线程暂时睡眠指定时长,线程进入阻塞状态。

(2)睡眠时间过后线程会再进入可运行状态。

(3)millis为休眠时长,以毫秒为单位。

(4)调用sleep()方法需处理InterruptedException异常。

public class ThreadSleepDemo {
    public static void main(String[] args) {
        System.out.println("Wait");
        Wait.bySec(5); // 让主线程等待5秒种再执行
        System.out.println("start");
    }
}

class Wait {
    public static void bySec(long s) {
        for (int i = 0; i < s; i++) {
            System.out.println(i + 1 + "秒");
            try {
                Thread.sleep(1000); //睡眠1秒
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

3、线程的强制运行:void join(long mills,int nanos)

(1)使当前线程暂停执行,等待其他线程结束后再继续执行本线程

(2)mills:以毫秒为单位的等待时长;nanos:要等待的附加纳秒时长

(3)需要处理InterruptedException异常。

public class MyThread implements Runnable{
    public void run(){
        for(int i=0;i<10;i++){
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //输出当前线程的信息
            System.out.println(Thread.currentThread().getName()+"运行:"+i);
        }
    }
}

public class ThreadJoinDemo {
    public static void main(String[] args) {
        System.out.println("*****线程强制执行******");
        //创建子线程并启动
        Thread temp = new Thread(new MyThread());
        temp.start();
        for(int i=0;i<20;i++){
            if(i==5){
                try {
                    //阻塞主线程,子线程强制执行
                    temp.join();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"运行:"+i);
        }
    }
}

4、线程的礼让static void yield()

(1)暂停当前线程,允许其他具有相同优先级的线程获得运行机会。

(2)该线程处于就绪状态,不转为阻塞状态。

public class MyThread implements Runnable{
    public void run(){
        for(int i=0;i<5;i++){
            System.out.println(Thread.currentThread().getName()+"正在运行:"+i);
            if(i==3){
                System.out.print("线程礼让:");
                Thread.yield();
            }
        }
    }
}

public class ThreadYieldDemo {
    public static void main(String[] args) {
        System.out.println("*****线程的礼让*****");
        MyThread my = new MyThread();
        Thread t1 = new Thread(my,"线程A");
        Thread t2 = new Thread(my,"线程B");
        t1.start();
        t2.start();
    }
}

线程同步

1、多线程共享数据引发的问题:引发数据不安全问题。

2、同步方法:使用synchronized修饰的方法控制对类成员变量的访问。

//方法一,修饰非静态同步方法(类的实例方法),锁定的是当前对象,其他对象则没有这个约束。
访问修饰符 synchronized 返回类型 方法名(参数列表){……}
//方法二
synchronized 访问修饰符 返回类型 方法名(参数列表){……}
//方法三:修饰静态方法,锁定的是当前类,可以控制类的所有实例的访问。
访问修饰符 static synchronized 返回类型方法名(参数列表){……}

(1)缺陷:将一个运行时间比较长的方法声明称synchronized会影响效率。

3、同步代码块:使用synchronized关键字修饰的代码块使用,synchronized修饰的方法控制对类成员变量的访问。

// syncObject为需同步的对象,通常为this,效果与同步方法相同。
synchronized(syncObject){
    //需要同步的代码
}

(1)多个并发线程访问同一资源的同步代码块时:

  a、同一时刻只能有一个线程进入synchronized(this)同步代码块。

  b、当一个线程访问一个synchronized(this)同步代码块时,其他synchronized(this)同步代码块同样被锁定。

  c、当一个线程访问一个synchronized(this)同步代码块时,其他线程可以访问该资源的非synchronized(this)同步代码。

4、线程安全的类型:

(1)线程安全:方法同步,效率低,适用于多线程并发共享资源。

(2)非线程安全:方法不同步,效率高,适用于单线程。

5、常见类型对比:

(1)Hashtable和HashMap:

  Hashtable:实现了Map接口,Hashtable继承Dictionary类,线程安全,效率较低,键和值都不允许为null。

  HashMap:实现了Map接口,继承AbstractMap类,非线程安全,效率较高,键和值都允许为null。

(2)StringBuffer和StringBuilder:前者线程安全,后者非线程安全。

如无特殊说明,本博所有文章均为博主原创。

如若转载,请注明出处:一木林多 - https://www.l5v.cn/archives/33/

评论