有关多线程的一些笔记总结

------------恢复内容开始------------

多线程

1、创建线程

一个Java应用程序进程,至少有三个线程:main()主线程、gc()回收线程、异常处理线程

1、方式一:继承Thread

步骤1、创建一个继承于Thread类的子类

  2、重写Thread类的run方法

  3、创建Thread类的子类的对象

  4、通过此对象调用start方法

其中,start方法的作用是:1、启动当前线程 2、调用该线程的run方法

//1、创建一个继承于Thread类的子类
class MyThread extends Thread{
   //2、重写Thread类的run方法
public void run(){}
}
class ThreadTest{
   public static void main(String[] args){
       Thread t1 = new MyThread();
       t1.start();
  }
}

注意

  1. 不能直接通过run方法启动线程!启动线程的是start方法!

  2. 不能让已经start()的线程去执行start()去再次启动线程

 

2、方式二:实现Runnable接口

步骤 1、 创建一个实现Runnable接口的类

2、 实现类去实现Runnable中的抽象方法:run()

3、创建实现类的对象

4、将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象

5、通过Thread类的对象调用start

class MyThread implements Runnable{
   public void run(){}
}
class Test{
   public static void main(String[] args){
       MyThread thread = new MyThread();
       Thread t = new Thread(thread);
       t.start();
  }
}


//在Thread中的源码  
//Thread中构造器,其中target 是Runnable类型的变量
public Thread(Runnable target) {  //从这就能看出最后执行的是传入的Runnable的实现类的run方法
    init(null, target, "Thread-" + nextThreadNum(), 0);
}
//执行run方法
@Override
public void run() {
    if (target != null) {
        target.run();//target是Thread的一个属性,类型为Runnable
    }
}
 private void init(ThreadGroup g, Runnable target, String name,
                  long stackSize) {
    init(g, target, name, stackSize, null, true);
}

说明:t1.start();调用的确实是实现类覆写的run方法

 

3、比较两种创建方式

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

原因:1、实现的方式没有类的单继承的局限性(假如采用第一章创建方式,则首先要继承Thread类,那这样直接就把继承的唯一机会给用掉了,就不能再去继承了,有很大的局限性)

2、实现的方式更适合处理多个线程有共享数据的情况

  当通过继承Thread类的方式创建线程,则当有多线程时,需要创建多个不同Thread子类,若有共享数据则需要讲共享数据修饰成static。

  而通过实现Runnable接口来创建线程,当有多线程时,不需要创建多个不同的实现类,就可以使用共享数据。

联系:public class Thread implements Runnable

Thread类也实现了Runnable接口(run方法)

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

4、方式三:实现Callable接口 (jdk5后新增)

//1、创建一个Callable接口的实现类
class NumThread implements Callable{
   //2、重写call方法
   public Object call() throws Exception{
       return null;
  }
}

public class ThreadTest{
   public static void main(String[] args){
       //3、实现类进行实例化对象
       NumThread n1 = new NumThread();
       //4、引用FutureTask类也是Runnable的实现类
       FutureTask f1 = new FutureTask(n1);
       //5、将FutureTask的对象作为参数传入Thread构造器
       Thread t1 = new Thread(f1);
       t1.start();
  }
}

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

  1. call方法可以有返回值

  2. call方法可以跑出异常

  3. Callable支持泛型

5、方式四:使用线程池 (jdk5后新增)

class NumTest implements Runnable{
   public void run(){
       
  }
}
public class ThreadTest{
   public static void main(String[] args){
       NumTest n1 = new Numtest();
       //1、提供指定线程数量的线程池
ExecutorService e = Executos.newFixedThreadPool(10);
    //2、执行指定的线程的操作,需要提供实现Runnable接口或Callable
       e.execute(n1);//使用于Runnable
  e.shutdown();
  }
}

通过线程池创建线程好处:

  1. 提高响应速度

  2. 降低资源消耗

  3. 便于线程管理

 

 

2、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():判断当前线程是否存活

注意 :

      wait()、notify()、notifyAll()是在Object类中声明

 

3、线程的优先级

 

1、MAX_PRIORITY:10
  MIN_PRIORITY:1
  NORM_PRIORITY:5 ----> 默认
 
2、如何获取和设置当前线程的优先级
getPriority():获取线程的优先级
setPriority():设置线程的优先级

说明:高优先级的线程会抢占低优先级线程的CPU执行权。但只是从概率上讲高优先级的线程优先被执行,并不意味着只有当高优先级的线程执行完后,低优先级的线程才执行。

4、解决线程的安全问题

1、方式一:同步代码块

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

说明: 操作共享数据的代码,即为需要被同步的代码(不能包含代码多了,也不能包含少了)

共享数据:多个线程共同操作的变量

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

同步机制的要求:多个线程必须公用同一把锁

 

2、方式二:同步方法

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

public synchronized void test(){  

}

3、方式三:Lock锁 ----- jdk5.0

class Num implements Runnable{
   private ReentrantLock lock = new ReentrantLock();
   public void run(){
       try{
           lock.lock();
           //同步代码
      }finally{
           lock.unlock();
      }
  }
}

4、比较synchronized与lock异同

  1. 相同点:二者都可以解决线程安全问题

  2. 不同:synchronized机制在执行完相应的同步代码以后,自动的释放同步监视器,而lock需要手动的启动同步(Lock()),同步结束时也需要手动的实现(unlock())

  3. 优先使用:Lock -> 同步代码块 ->同步方法

5、同步方法的总结

1、同步的方式解决了线程的安全问题 -------好处
2、操作同步代码时,只能有一个线程参与,其他线程等待,相当于是一个单线程的过程,效率低----坏处

总结:

1、同步方法仍然涉及到同步监视器,只是不需要我们显示声明

2、非静态的同步方法的同步监视器是 this

    静态同步方法的同步监视器是当前类本身

5、线程通信

设计到的三个方法

1、wait():一旦执行此方法,当前线程就进入阻塞状态,并释放同步监视器 ----> 只出现在同步代码块和同步方法中,不能用在lock

2、notify():一旦执行此方法,就会唤醒被wait的一个线程,如果有多个线程被wait,则唤醒优先级高的那个线程

3、notifyAll():一旦执行此方法,就会唤醒所有被wait的线程

说明:

  • wait()、notify()、notifyAll()都必须在同步方法同步代码块中调用

  • wait()、notify()、notifyAll()调用者必须是同步方法和同步代码块中的同步监视器,否则会出现异常

  • wait()、notify()、notifyAll()定义java.lang.Object类中

sleep和wait方法有什么异同
1.相同点:一旦执行方法,都可以使得当前的线程进行入阻塞状态
2、不同点:
1)两个方法声明的位置不同:Thread类中声明sleep
  Object类中声明wait  
  2)调用的要求不同:sleep可以在任何需要的场景下调用,wait必须使用在同步代码块中和同步方法中

 

------------恢复内容结束------------

posted @ 2021-07-24 16:25  每天进步一点  阅读(57)  评论(0)    收藏  举报