第五章JAVA高级多线程

一、线程、进程与多线程

(一)进程

应用程序的执行实列

有独立的内存空间和系统资源

CPU在某个时间点只能执行一个进程

(二)线程

线程是CPU和分派的基本单位

执行运算的最小单位,可以完成一个独立的顺序控制流程

一个进程中存在多个线程

(三)多线程

若在一个进程中同时运行多个进程,用来完成不同的工作,则称之为“多线程”

多个线程交替占用CPU资源,而非真正的并行执行

(四)多线程的好处

充分利用CPU的资源,提升代码的性能

简化编程模型,更好的软件设计和架构

为用户带来良好的体验

二、线程的创建

(一)主线程

main()方法为主线程的入口,必须在最后完成执行

public static void main(String[] args) {
        //获取当前的线程对象
        Thread thread = Thread.currentThread();
        //获取当前线程对象的而名称
        String name = thread.getName();
        System.out.println(name);
        //为当前线程重新设置名称
        thread.setName("brx");
        name = thread.getName();
        System.out.println(name);
    }

(二)继承Thread类创建线程

java.lang.Thread类

创建要点:

定义MyThread类继承Thread类

重写run()方法,编写线程执行体

创建线程对象,调用start()方法启动线程

public class MyThread extends Thread{//继承Thread
    @Override
    public void run() {//重写run方法
        for (int i = 1; i <= 100; i++){
            System.out.println(Thread.currentThread().getName()+":"+i);
        }
    }
}
​
public static void main(String[] args) {//main()方法为主线程的入口
    //创建线程对象
    MyThread mythread = new MyThread();
    MyThread my1 = new MyThread();
    //启动线程
    mythread.start();
    my1.start();
​
}

注意:启动线程时不可以调用run()方法

run()方法:只有主线程一天执行路径

start()方法:多条执行路径,主线程和子线程并行交通执行

(三)使用Runnable接口创建线程

实现java.lang.Runnable接口

使用方法:

定义MyRunnable类实现Runnable接口

实现run()方法,编写线程执行体

创建线程对象,调用start()方法启动线程

public class MyRunnable implements Runnable{//Runnable接口
    @Override
    public void run() {//run()方法
        for (int i = 0;i <= 100;i++){
            System.out.println(Thread.currentThread().getName()+":"+i);
        }
    }
}
​
public static void main(String[] args) {
    //创建线程对象
    Runnable runnable = new MyRunnable();
    //方式一:Thread(Runnable target)
    Thread thread1 = new Thread(runnable);
    //方式二:Thread(Runnable target,String name),对当前线程重命名
    Thread thread2 = new Thread(runnable,"mythread");
    //启动线程
    thread1.start();
    thread2.start();
}

(四)实现Callable<>创建线程

实现Callable<数据类型>,

重写call()方法,编写线程执行体

public class MyCallable implements Callable<String> {
    @Override
    public String call() throws Exception {
        for (int i = 0;i <= 10;i++){
            System.out.println(Thread.currentThread().getName()+":"+i);
        }
        return null;
    }
}
​
ublic class MyCallableTest {
    public static void main(String[] args) {
        MyCallable mc = new MyCallable();
        FutureTask ft = new FutureTask<String>(mc);
        Thread t = new Thread(ft);
        t.start();
​
    }
}

(五)继承Thread类与实现Runnable接口区别

继承Thread类

编写简单,可直接操作线程

适用于单继承

多个线程分别完成自己的任务

实现Runnable接口

避免单继承局限性

便于共享资源

多个线程共同完成一个任务

三、线程状态

创建状态:new Thread时为创建状态

就绪状态:start()方法时为就绪状态

运行状态:当调用run()方法时为运行状态

阻塞状态:当调用时sleap(),yield()、join()、wait()、notify时为阻塞状态

死亡状态:线程执行完毕,终止线程

四、线程的调度

(一)常用方法

说 明
void setPriority(int newPriority)更改线程的优先级
static void sleep(long millis)在指定的毫秒数内让当前正在执行的线程休眠
void join()等待该线程终止
static void yield()暂停当前正在执行的线程对象,并执行其他线程
void interrupt()中断线程
boolean isAlive()测试线程是否处于活动状态
setPriority():

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

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

public class MyThread extends Thread{//继承Thread
    @Override
    public void run() {//重写run方法
        for (int i = 1; i <= 100; i++){
            System.out.println(Thread.currentThread().getName()+":"+i);
        }
    }
}
public static void main(String[] args) {
        MyThread my1 = new MyThread();
        MyThread my2 = new MyThread();
        my1.setPriority(1);
        my2.setPriority(10);
        my1.start();
        my2.start();
    }
sleep():

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

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

public static void show(long s){
        for (int i =1;i <= s;i++){
            System.out.println(i+"秒");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
​
    public static void main(String[] args) {
​
        System.out.println("--------------开始休眠---------------");
        SleepTest.show(5);
        System.out.println("--------------结束休眠---------------");
    }

join():

强制执行当前线程,join写在哪个线程,就阻塞谁

public void run() {
        for (int i =0;i <= 20;i++){
            System.out.println(Thread.currentThread().getName()+":"+i);
        }
    }
public static void main(String[] args) {
        Runnable runnable = new joinTest();
        Thread t1 = new Thread(runnable);
        t1.start();
        for (int i = 0;i <=30;i++){
            System.out.println(Thread.currentThread().getName()+":"+i);
            if (i == 5){
                try {
                    t1.join();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        }
​
    }

yield():

暂停当前正在执行的线程对象,并执行其他线程,线程礼让

只是提供了一种可能,但不能保证一定会实现礼让

public void run() {
        for (int i = 1;i <= 5; i++){
            System.out.println(Thread.currentThread().getName()+":"+i);
            if (i == 3){
                System.out.print("礼让");
                Thread.yield();
​
            }
        }
    }
public static void main(String[] args) {
        Runnable runnable = new yieldTest();
        Thread t1 =new Thread(runnable);
​
        Thread t2 =new Thread(runnable);
        t1.start();
        t2.start();
    }

(二)阻塞对比:

sleep():让线程暂时睡眠指定时长,线程进入阻塞状态,睡眠时间过后线程会再进入可运行状态

join():礼让,放手,当前线程处于就绪状态

yield():阻塞当前线程,直到其他线程执行完毕,当前线程才进入就绪状态

wait() :让某个线程先暂停下来等一等

notify(): 就是把该线程唤醒,让它能够继续执行

五、多线程共享数据

多个线程操作同一共享资源时,将引发数据不安全问题

synchronized就是为当前的线程声明一把锁

(一)同步方法

1、访问修饰符 synchronized 返回类型 方法名(参数列表){……}

2、synchronized 访问修饰符 返回类型 方法名(参数列表){……}

private int tickets = 20;
private int coun = 0;
boolean bool = false;
@Override
public void run() {
    while (!bool){
        sale();
    }
}
public synchronized void sale(){
    if (tickets <= 0){
        bool = true;
        return;
    }
    tickets --;
    coun ++;
    System.out.printf(Thread.currentThread().getName()+":在抢到了第%d张票,剩余%d张票\n",coun,tickets);
    try {
        Thread.sleep(500);
    } catch (InterruptedException e) {
        throw new RuntimeException(e);
    }
}
​
public static void main(String[] args) {
        Runnable runnable = new MySynchronized1();
        Thread t1 = new Thread(runnable,"智行");
        Thread t2 = new Thread(runnable,"携程");
        Thread t3 = new Thread(runnable,"花小猪");
        t1.start();
        t2.start();
        t3.start();
    }

(二)同步代码块

@Override
public void run() {
    while (true){
        synchronized (this){
            if (tickets <= 0){
                return;
            }
            count ++;
            tickets --;
            System.out.printf(Thread.currentThread().getName()+":在抢到了第%d张票,剩余%d张票\n",count,tickets);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

(三)总结

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

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

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

六、线程安全

(一)类型对比

1、Hashtable && HashMap

Hashtable

继承关系实现了Map接口,Hashtable继承Dictionary

类线程安全,效率较低

键和值都不允许为null

HashMap

继承关系实现了Map接口,继承AbstractMap类

非线程安全,效率较高

键和值都允许为null

2、StringBuffer && StringBuilder

前者线程安全,后者非线程安全

  • 20
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值