目录

1、Thread的常用方法

2、创建多线程的四种方式

方式一:继承于Thread类

方式二:实现Runnable接口

方式三:实现Callable接口,重写call方法。 --- JDK 5.0新增

方式四:使用线程池

3、通过同步机制,解决线程安全问题

方式一:同步代码块

方式二:同步方法。

4、线程的生命周期

1、五种状态

2、状态转换图

5、线程的通信

1、wait() 与 notify() 和 notifyAll()

2、wait() 方法

3、线程的通信notify()/notifyAll()

 

1、Thread的常用方法

/**
 * 测试Thread中的常用方法:
 * 1. start():启动当前线程;调用当前线程的run()
 * 2. run(): 通常需要重写Thread类中的此方法,将创建的线程要执行的操作声明在此方法中
 * 3. currentThread():静态方法,返回执行当前代码的线程
 * 4. getName():获取当前线程的名字
 * 5. setName():设置当前线程的名字
 * 6. yield():释放当前cpu的执行权
 * 7. join():在线程a中调用线程b的join(),此时线程a就进入阻塞状态,直到线程b完全执行完以后,线程a才
 *           结束阻塞状态。
 * 8. stop():已过时。当执行此方法时,强制结束当前线程。
 * 9. sleep(long millitime):让当前线程“睡眠”指定的millitime毫秒。在指定的millitime毫秒时间内,当前
 *                          线程是阻塞状态。
 * 10. isAlive():判断当前线程是否存活
 *
 *
 * 线程的优先级:
 * 1.
 * MAX_PRIORITY:10
 * MIN _PRIORITY:1
 * NORM_PRIORITY:5  -->默认优先级
 * 2.如何获取和设置当前线程的优先级:
 *   getPriority():获取线程的优先级
 *   setPriority(int p):设置线程的优先级
 *
 *   说明:高优先级的线程要抢占低优先级线程cpu的执行权。但是只是从概率上讲,高优先级的线程高概率的情况下
 *   被执行。并不意味着只有当高优先级的线程执行完以后,低优先级的线程才执行。
 */

2、创建多线程的四种方式

方式一:继承于Thread类

  1. 创建一个继承于Thread类的子类
  2. 重写Thread类的run() --> 将此线程执行的操作声明在run()中
  3. 创建Thread类的子类的对象
  4. 通过此对象调用start()
//1. 创建一个继承于Thread类的子类
calss myThread extends Thread{
    

    //2. 重写Thread类的run()
    @Override
    public void run(){
        //核心代码
    }
}


     //3. 创建Thread类的子类的对象
     MyThread t1 = new MyThread();

     //4.通过此对象调用start():启动当前线程并调用当前线程的run()
     t1.start();

方式二:实现Runnable接口

  1. 创建一个实现了Runnable接口的类
  2. 实现类去实现Runnable中的抽象方法:run()
  3. 创建实现类的对象
  4. 将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象
  5. 通过Thread类的对象调用start()
//1. 创建一个实现了Runnable接口的类
class myThread implements Runnable{
    
    //2. 实现类去实现Runnable中的抽象方法:run()
    @Override
    public void run() {
        //核心代码
    }
}

    //3. 创建实现类的对象
    MThread mThread = new MThread();

    //4. 将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象
    Thread t1 = new Thread(mThread);
        
    //5. 通过Thread类的对象调用start(): 启动线程 并 调用当前线程的run()-->调用了Runnable类型的target的run()
    t1.start();

比较创建线程的两种方式。

开发中:优先选择:实现Runnable接口的方式

原因:

  1. 实现的方式没有类的单继承性的局限性
  2. 实现的方式更适合来处理多个线程有共享数据的情况。

联系:public class Thread implements Runnable

相同点:两种方式都需要重写run(),将线程要执行的逻辑声明在run()中。

方式三:实现Callable接口,重写call方法。 --- JDK 5.0新增

//1.创建一个实现Callable的实现类
public class MyCallable implements Callable{
    //2.实现call方法,将此线程需要执行的操作声明在call()中
    @Override
    public Object call(){
        
	}
}

//3.创建Callable接口实现类的对象
MyCallable myCallable = new MyCallable();
//4.将此Callable接口实现类的对象作为传递到FutureTask构造器中,创建FutureTask的对象
FutureTask futureTask = new FutureTask(myCallable);
 //5.将FutureTask的对象作为参数传递到Thread类的构造器中,创建Thread对象,并调用start()
new Thread(futureTask).start();
//6.获取Callable中call方法的返回值
//get()返回值即为FutureTask构造器参数Callable实现类重写的call()的返回值。
Object object = futureTask.get();

如何理解实现Callable接口的方式创建多线程比实现Runnable接口创建多线程方式强大?

  1. call()可以有返回值的。
  2. call()可以抛出异常,被外面的操作捕获,获取异常的信息
  3. Callable是支持泛型的

方式四:使用线程池

  • corePoolSize:核心池的大小

  • maximumPoolSize:最大线程数

  • keepAliveTime:线程没有任务时最多保持多长时间后会终止

//1. 提供指定线程数量的线程池
ExecutorService service = Executors.newFixedThreadPool(10);
ThreadPoolExecutor service1 = (ThreadPoolExecutor) service;

//设置线程池的属性
//System.out.println(service.getClass());
//service1.setCorePoolSize(15);
//service1.setKeepAliveTime();

//2.执行指定的线程的操作。需要提供实现Runnable接口或Callable接口实现类的对象
service.execute(new myThread1());//适合适用于Runnable
service.execute(new myThread2());//适合适用于Runnable
//service.submit(Callable callable);//适合使用于Callable

//3.关闭连接池
 service.shutdown();

好处:

1.提高响应速度(减少了创建新线程的时间)

2.降低资源消耗(重复利用线程池中线程,不需要每次都创建)

3.便于线程管理

3、通过同步机制,解决线程安全问题

方式一:同步代码块

synchronized(同步监视器){
​
//需要被同步的代码
​
}

说明:

1.操作共享数据的代码,即为需要被同步的代码。 -->不能包含代码多了,也不能包含代码少了。

2.共享数据:多个线程共同操作的变量。比如:ticket就是共享数据。

3.同步监视器,俗称:锁。任何一个类的对象,都可以充当锁。

要求:多个线程必须要共用同一把锁。

补充:在实现Runnable接口创建多线程的方式中,我们可以考虑使用this充当同步监视器。

方式二:同步方法。

//例如
private synchronized void method(){
    //需要同步的代码
}

如果操作共享数据的代码完整的声明在一个方法中,我们不妨将此方法声明同步的。

4.同步的方式,解决了线程的安全问题。---好处

操作同步代码时,只能有一个线程参与,其他线程等待。相当于是一个单线程的过程,效率低。 ---局限性

4、线程的生命周期

1、五种状态

  • 新建:当一个Thread类或其子类的对象被声明并创建时,新生的线程对象处于新建状态。

  • 就绪:处于新建状态的线程被start()后,将进入线程队列等待CPU时间片,此时他已经具备了运行的条件,只是,没分配到CPU资源。

  • 运行:当就绪的线程被调度并获得CPU资源时,便进入运行状态,run()方法定义了线程的操作和功能。

  • 阻塞:在某种特殊情况下,被人为挂起或执行输入输出操作时,让出CPU并临时中止自己的执行,进入阻塞状态

  • 死亡:线程完成了它的全部工作或者线程被提前强制性地中止或出现异常导致结束。

2、状态转换图

JAVA之线程

5、线程的通信

1、wait() 与 notify() 和 notifyAll()

wait():令当前线程挂起并放弃CPU、 同步资源并等待, 使别的线程可访问并修改共享资源,而当 前线程排队等候其他线程调用notify()或notifyAll()方法唤醒,唤醒后等待重新获得对监视器的所有 权后才能继续执行。

notify():唤醒正在排队等待同步资源的线程中优先级最高者结束等待

notifyAll ():唤醒正在排队等待资源的所有线程结束等待.

这三个方法只有在synchronized方法或synchronized代码块中才能使用,否则会报 java.lang.IllegalMonitorStateException异常。

因为这三个方法必须有锁对象调用,而任意对象都可以作为synchronized的同步锁,

因此这三个方法只能在Object类中声明。

2、wait() 方法

在当前线程中调用方法: 对象名.wait()

使当前线程进入等待(某对象)状态 ,直到另一线程对该对象发出 notify (或notifyAll) 为止。

调用方法的必要条件:当前线程必须具有对该对象的监控权(加锁)

调用此方法后,当前线程将释放对象监控权 ,然后进入等待

在当前线程被notify后,要重新获得监控权,然后从断点处继续代码的执行。

3、线程的通信notify()/notifyAll()

在当前线程中调用方法: 对象名.notify()

功能:唤醒等待该对象监控权的一个/所有线程。

调用方法的必要条件:当前线程必须具有对该对象的监控权(加锁)

相关文章:

  • 2021-06-30
  • 2022-02-09
  • 2021-11-23
  • 2022-01-22
  • 2021-11-29
  • 2021-06-05
  • 2021-09-25
猜你喜欢
  • 2021-08-10
  • 2021-10-14
  • 2022-12-23
  • 2021-12-15
  • 2021-09-28
相关资源
相似解决方案