Java_多线程基础

正文

  1. 什么是线程

  2. 为什么使用线程

  3. java如何创建创建线程

  4. 线程中一些常见的方法

  5. 线程的状态有哪些

  6. 线程安全问题? 如何解决

  7. 线程通信

 

1. 什么是线程

(1) 什么是进程

正在运行的程序叫做进程, 进程是系统分配资源的基本单位. 使用 PID 来区分进程

(2) 什么是线程

进程中的一条执行路径,也是cpu的基本调度单位,一个进程由一个或多个线程组成,彼此之间完成不同工作,同时执行,称为多线程

(3) 进程和线程之间的区别 面试题

  1. 进程是操作系统资源分配的基本单位,而线程是CPU的基本调度单位。
2. 一个程序运行后至少有一个进程。
3. 一个进程可以包含多个线程,但是至少需要有一个线程,否则这个进程是没有意义。
4. 进程间不能共享数据段地址,但是同进程的线程之间可以。

2. 为什么使用线程

为了解决负载均衡问题,充分利用CPU资源.为了提高CPU的使用率,采用多线程的方式去同时完成几件事情而不互相干扰.为了处理大量的IO操作时或处理的情况需要花费大量的时间等等,比如:读写文件,视频图像的采集,处理,显示,保存等

单核时代: 在单核时代多线程主要是为了提高 CPU 和 IO 设备的综合利用率。举个例子:当只有一个线程的时候会导致 CPU 计算时,IO 设备空闲;进行 IO 操作时,CPU 空闲。我们可以简单地说这两者的利用率目前都是 50%左右。但是当有两个线程的时候就不一样了,当一个线程执行 CPU 计算时,另外一个线程可以进行 IO 操作,这样两个的利用率就可以在理想情况下达到 100%了。多核时代: 多核时代多线程主要是为了提高 CPU 利用率。举个例子:假如我们要计算一个复杂的任务,我们只用一个线程的话,CPU 只会一个 CPU 核心被利用到,而创建多个线程就可以让多个 CPU 核心被利用到,这样就提高了 CPU 的利用率。

3. java如何创建创建线程?

通过继承Thread类

  package com.qy143.demo01;

// 继承Thread类
public class MyThread extends Thread {

   // 整个构造函数
   public MyThread(String name) {
       super(name);
  }

   // 重写run方法
   @Override
   public void run(){
       for (int i=0;i<10;i++){
           // 必须是Thread的子类才能使用   默认线程的名称位 Thread-n n从零开始
           //System.out.println(this.getName()+"线程"+i);

           // 在Thread类中存在一个静态方法,该方法可以获取当前线程对象
           System.out.println(Thread.currentThread().getName()+"线程"+i);
      }
  }
}

测试

  
package com.qy143.demo01;

public class Test01 {
   public static void main(String[] args) {
       MyThread my1 = new MyThread("C");// 创建一个线程对象
       //my1.setName("A");// 为线程起名
       my1.start(); // 开启线程 当前线程和main线程同时正对实现

       MyThread my2= new MyThread("D");// 创建一个线程对象
       //my2.setName("B"); // 为线程起名
       my2.start(); // 开启线程 当前线程和main线程同时正对实现


       for (int i=0;i<10;i++){
           System.out.println("main线程"+i);
      }
  }
}

为线程起名以及获取线程

  
1.获取线程名称
[1]this.getName();必须为Thread子类
[2]Thread.currentThread.getName();可在任意位置获取线程名称
2.为线程起名
[1]通过setName方法 该类对象必须为Thread的对象
[2]通过构造方法

实现Runnable接口

  
package com.qy143.demo03;

public class MyRunbable implements Runnable{
   @Override
   public void run() {
       for (int i=0;i<10;i++){
           System.out.println(Thread.currentThread().getName()+"线程"+i);
      }
  }
}
  
package com.qy143.demo03;

public class TestRunnable {
   public static void main(String[] args) {
       MyRunbable m1 = new MyRunbable(); // 线程任务
       Thread t1 = new Thread(m1,"MyRunnable线程");
       t1.start();

       // 再整一个线程
       Thread t2 = new Thread(m1,"MyRunnable线程2");
       t2.start();

       for (int i=0;i<10;i++){
           System.out.println("main线程"+i);
      }
  }
}

例子: 四个窗口共卖100张票

  
package com.qy143.demo04;

public class TicketRunnable implements Runnable{
   private int ticket=10;

   public void run(){
       while (ticket>0){
           System.out.println(Thread.currentThread().getName()+"卖了一张票,剩余:"+--ticket);
      }
  }
}
  
package com.qy143.demo04;


import com.qy143.demo02.TickThread;
import com.qy143.demo03.MyRunbable;

public class TestTicket {
   public static void main(String[] args) {
       TicketRunnable m = new TicketRunnable();
       Thread t1 = new Thread(m,"窗口A");
       Thread t2 = new Thread(m,"窗口B");
       Thread t3 = new Thread(m,"窗口C");
       Thread t4 = new Thread(m,"窗口D");


       // 出现了重复?? 还有少的??
       // 这就涉及到线程安全了
       t1.start();
       t2.start();
       t3.start();
       t4.start();
  }
}

测试的结果 : 同一张票被多个窗口购买,而且出现负票,因为线程安全问题

4. 线程中一些常见的方法

休眠 : public static void sleep(long);

放弃 :public static void yield(); 放弃本次时间片,参与下次时间片的争夺

加入 :public void join();运行其他线程加入当前线程,直到其他线程运行结束后,当前线程才可以运行

优先级 :setPriority(); 值1-10 值越大获取cpu概率越大 默认可能为5

守护线程 : setDaemon(true); 所有前台线程结束后守护线程也会结束

 

5. 线程的状态(等待)

  1. NEW---->start--->join--->Waiting

    ​ sleep----Time-waiting

    Runnable

    wating

     

限期等待 Timed Waiting 到期之后变为就绪状态

无限期等待 Waiting 条件满足之后才能变为就绪状态

blocked

TERMINATED

 

6 .线程安全问题? 如何解决

1.如何保证线程安全

(1) 使用同步代码块

​ 使用一个对象类型作为锁

(2) 使用同步方法

​ 默认使用this或者当前类作为锁

2.线程安全类有哪些

(1) Hashtable (2) StringBuffer (3) Vector (4) ConcurrentHashMap 这个贼叼 效率高还安全

Hashtable 和Hashmap的区别 :

  1. 线程安全

    1. key和value是否允许null

    2. 效率上

HashMap的底层实现原理:

数组链表红黑树 ????

 

7. 线程通信

wait方法和sleep方法得区别?

(1)wait属于Object类,sleep属于Thread类 (2) wait会是否锁资源,sleep不会释放锁资源 (3) wait需要notify和notifyAll唤醒,sleep时间到了会自动唤醒。 (4) wait必须使用在同步代码块或者同步方法中, sleep可以在任何地方使用。

notify和notityAll得区别?

(1) notify随机唤醒等待队列中的某个线程, (2) notifyAll唤醒等待队列中所有的线程。

创建线程池得方式 :

  
package com.qy143.demo13;


import java.util.concurrent.*;

// Executor : 线程池顶级接口   execute方法
// ExecutorService : 顶级线程池子接口     shutdown 关闭线程池子 submit();该方法可以执行Runable类型和Callable类型的任务
// Executor : 工具类,相应的线程池
public class Test {
   public static void main(String[] args) {
       // 1. 固定长度的线程
       ExecutorService executorService = Executors.newFixedThreadPool(3);

       // 2. 长度可变线程   如果存在空闲的线程 则不会创建新的
       //ExecutorService executorService = Executors.newCachedThreadPool();

       // 3. 创建单一线程池 给队列使用
       //ExecutorService executorService = Executors.newSingleThreadExecutor();

       // 4. 创建定长延时线程池
       //ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(3);

       // 5. ExecutorsService实现类创建.ThreadPoolExecutor
//       ArrayBlockingQueue blockingQueue =new ArrayBlockingQueue(2);
//       ExecutorService executorService =new ThreadPoolExecutor(3,5,5,TimeUnit.SECONDS,blockingQueue);



       Runnable tast1 = new Runnable() {
           @Override
           public void run() {
               System.out.println(Thread.currentThread().getName()+"执行");
          }
          };
       for (int i=0;i<7;i++){
             executorService.execute(tast1);// 执行任务
           //scheduledExecutorService.schedule(tast1,3, TimeUnit.SECONDS); // 定时执行
      }
  }
}

 

 

 

扩充 :

Lock接口:

常用方法 : void lock(); // 获取锁 如锁被占用 则等待

​ boolean tryLock; // 尝试获取锁

​ void unnlock(); // 释放锁

实现类 :

​ 重入锁 ReentrantLock

​ 读写锁 ReentrantReadWriteLock

 

 

来个重点

啥是CAS

sysn和ReentLock的区别

????? 弄不明白去球吧

posted @ 2021-12-20 18:56  Oner_z  阅读(24)  评论(0)    收藏  举报