Java多线程

2021-03-05 05:29

阅读:478

标签:rac   顺序   cpu   start   lock   控制   setdaemon   version   pack   

Java多线程

多线程的实现1

  • 继承Thread
  • 重写run()
  • start()

1.为什么要重写run()方法?

因为run()方法是用来封装线程要执行的代码的。

2.run()方法和start()方法的区别?

直接使用run()方法并不是启动多线程,使用start()才是启动线程,然后由JVM调用此线程的run()方法。

技术图片

设置和获取线程名称

  • setName(),getName()
  • 通过带参构造。前提是子类提供name的带参构造,并super给Thread

提供一个静态方法获取当前线程名称:

Thread.currentThread().getName();

线程调度

线程有两种调度模型

  • 分时调度模型。所有线程轮流使用CPU的使用权,平均分配时间。
  • 抢占式调度模型,根据优先级使用CPU,优先级相同会随机选择。
  • Java使用的是抢占式调度模型。

线程的默认优先级是5,范围是1-10,仅仅表示获取CPU时间片的几率。

线程控制

static void sleep(long millis)

使当前正在执行的线程停留或者暂停执行指定的毫秒数

在run()方法内使用

void join()

当前线程调用join方法时,其余线程要等待当前线程执行完。

start之后就可以使用。

void setDaemon(boolean on)

需要在start之前设置,否则会报出IllegalThreadStateException异常。

将当前线程标记为守护线程。当主线程执行完毕后,不会等守护线程结束,jvm会退出。线程会后续消失,不是立即消失。

在deamon线程中设置的子线程也是deamon

void interrupt();

给当前线程添加中断标签,如果线程在调用 Object 类的 wait()、wait(long) 或 wait(long, int) 方法,或者该类的 join()、join(long)、join(long, int)、sleep(long) 或 sleep(long, int) 方法过程中受阻,则其中断状态将被清除,它还将收到一个 InterruptedException。

interrupt() 方法只是改变中断状态而已,它不会中断一个正在运行的线程。这一方法实际完成的是,给受阻塞的线程发出一个中断信号,这样受阻线程就得以退出阻塞的状态。 更确切的说,如果线程被Object.wait, Thread.join和Thread.sleep三种方法之一阻塞,此时调用该线程的interrupt()方法,那么该线程将抛出一个 InterruptedException中断异常(该线程必须事先预备好处理此异常),从而提早地终结被阻塞状态。如果线程没有被阻塞,这时调用 interrupt()将不起作用,直到执行到wait(),sleep(),join()时,才马上会抛出 InterruptedException。

static void yield();
线程睡眠(sleep)

让当前正在执行的线程暂停一段时间,并进入阻塞状态,则可以通过调用Thread类的静态sleep()方法来实现。当前线程调用sleep()方法进入阻塞状态后,在其睡眠时间内,该线程不会获得执行的机会,而其它任何优先级的线程都可以得到执行的机会,即使系统中没有其它可执行的线程,处于sleep()的线程也不会执行,sleep()是用来暂停线程的执行。

线程让步(yield)

yield()方法是一个和sleep()方法有点相似的方法,它也是Thread类提供的一个静态方法。可以让当前正在执行的线程暂停,但它不会阻塞该线程,只是将该线程转入就绪状态。yeild()只是让当前线程暂停一下,让系统的线程调度器重新调度一次,完全可能的情况是:当某个线程调用了yield()线程暂停之后,线程调度器又将其调度出来重新执行。
当某个线程调用了yield()方法暂停之后,只有优先级与当前线程相同,或者优先级比当前线程更高的处于就绪状态的线程才会获得执行机会。

线程的生命周期

技术图片

多线程的实现2

用实现Runnable接口的子类对象传入Thread对象中

new Thread(new Runnable(){
    @Overide
    public void run(){
        ....
    }
}).start();

好处:

1.实现接口不影响继承其他类。

2.将Runable的实现类当作参数可传入多个Thread,可看作同一个资源由多个线程使用。

卖票案例:

三个窗口买100张票,在run方法中使用sleep,由于线程堵塞性和随机性,导致会出现连续100张出票情况,然后变成97。

package com.jiang.test.strings;

import static java.lang.Thread.sleep;

/**
 * @version 1.0
 * @Description:
 * @author: jiangsonglin
 * @date: 2021/1/20 19:58
 */
public class Demo {
    public static void main(String[] args) throws InterruptedException {
        MyThread myThread = new MyThread();
        Thread m1 = new Thread(myThread, "窗口一");
        Thread m2 = new Thread(myThread, "窗口二");
        Thread m3 = new Thread(myThread, "窗口三");
        m1.start();
        m2.start();
        m3.start();

    }
}

class MyThread implements Runnable {
    int ticks=100;
    @Override
    public void run() {
        while (true) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if (this.ticks > 0) {

                System.out.println(Thread.currentThread().getName() + "正在出售" + ticks + "张票");
                ticks--;
            }
        }
    }
}

线程安全

以上情况就是非线程安全,线程安全就是在多线程过程中,不管怎么调用方法,都不会出现上面的情况。

线程同步

数据安全

  • 是否是多线程环境。如果是单线程环境不会出现数据安全问题。
  • 是否有共享数据。多线程环境下,有共享数据,有可能会出现数据安全
  • 是否有多条语句操作共享数据

以上三条同时满足会出现数据安全问题。

怎么保护数据呢

  • 把共享数据锁起来,同一时间点只能有一个线程执行。Java提供了同步代码块来解决这个问题。
同步代码块
synchronized(任意对象){
    多条语句操作共享数据的代码
}
class xxx implements Runable{
    Object ob = new Object();//任意对象
    @Overide
    public void run(){
        synchronized(ob){
            ....
        }
    }
}
同步方法

将synchronized关键词加上方法上

修饰符 synchronized 返回值类型 方法名(){}

同步方法锁什么? this

静态同步方法

修饰符 static synchronized 返回值类型 方法名(){}

静态方法跟类加载有关,this代表的是当前对象。所以静态同步方法应该锁类名.class

Collections

Lock

JDK5以后的更方便的同步线程API,同步代码块和同步方法看见哪里锁对象和解锁并不是很明显。

Lock是一个接口,ReentrantLock是Lock的实现类。

void lock();
void unlock();
class xxx implements Runable{
   Lock lock = new ReentrantLock();
    @Overide
    public void run(){
        try{
            lock.lock();
            ....
        }finally{
            lock.unlock();
        }
    }
}
死锁

线程中互相等待资源的现象

技术图片

1.更改加锁的顺序

2.再加一把锁

生产者与消费者

生产者与消费者模型是经典的多线程协作模式。

生产线程---->共享数据

void wait();
//在其他线程调用此对象的 notify() 方法或 notifyAll() 方法前,导致当前线程等待。
void notify();
//唤醒在此对象监视器(锁对象)上等待的单个线程。
void notifyAll();
//唤醒在此对象监视器上等待的所有线程。
sleep与wait

sleep是Thread 中的静态方法,wait是Object类中的方法。

sleep不会改变当前锁对象的状态,wait会立即释放锁。

线程池

Executors

newCachedThreadPoolO: 创建一个具有缓存功能 线程池系统根据 要创建线程,这些线程将会被缓存在线程池中
newFixedThreadPool(int nThreads): 创建一个可重用的、具有固定线程数的线程池
newSingleThreadExecutorO: 创建 个只有单线程的线程池,它相 于调用 newFixedThread PoolO 方法时传入参数为
newScheduledThreadPool(int corePooISize): 建具有指定线程数的线程池 它可以在指定延迟后执行线程任务 corePoolSize 指池中所保存的线程数,即使线程是空闲也被保存在线程池
newSingleThreadScheduledExecutorO: 创建只 个线程的线程池,它可以在指定延迟后执行线程任务
ExecutorService newWorkStealingPool(int parallelism): 建持有足够的线程的线程池来支持给定的并行级别,该方法还会使用多个队列来减少竞争
ExecutorService new orkStealingPoolO: 该方法是前 个方法的简化版本 如果 前机器有
CPU ,则目标并行级别被设置为 ,也就是相当于为前 个方法传入 作为参数

定时任务

Java多线程

标签:rac   顺序   cpu   start   lock   控制   setdaemon   version   pack   

原文地址:https://www.cnblogs.com/jiangsonglin/p/14332693.html


评论


亲,登录后才可以留言!