栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Java多线程(含面试题)

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Java多线程(含面试题)

文章目录
  • 多线程
  • 1.创建线程的4种方式
    • 1.创建多线程的方式一:将一个类声明为`Thread`的子类重写run方法
    • 2.创建多线程的方式二:声明实现类`Runnable`接口,并重写run方法
    • 3.创建多线程的方式三:实现Callable接口 ---> JDK 5.0新增
    • 4.创建多线程的方式四:使用线程池
  • 2.Thread常用方法
  • 3.线程安全
  • 4.解决线程安全问题
    • 1.synchronized锁
    • 2.Lock锁
  • 5.死锁
  • 5.面试题

多线程

进程:电脑软件 idea mysql 每一个都属于一个进程

​ 1.独立性

​ 各个进程之间不受影响

​ 2.互斥性

​ 每个软件系统都会分配一个独立端口号,端口号一样会冲突

线程:进程是由多个或者一个线程组成的。每个进程至少得有一个线程

​ 1.抢占式运行

​ 线程的执行时间由系统来分配,线程切换不由线程本身决定

​ 2.非抢占式

​ 非抢占式让原来正在运行的进程继续运行,直至该进程完成或发生某种事件(如I/O请求),才主动放弃处理机。

线程和进程的区别:

一个线程只可以属于一个进程,但一个进程能包含多个线程

线程是进程中最小的基本单位

进程有独立性和互斥性
线程有抢占式 非抢占式

并发和并行:

并发:同时发生,轮流交替执行

并行:真正意义的同时执行

1.创建线程的4种方式 1.创建多线程的方式一:将一个类声明为Thread的子类重写run方法
package com.kz.a_thread;

//             继承Thread
class My extends Thread{
    //重写run方法
    @Override
    public void run() {
        for (int i = 0; i <500 ; i++) {
            System.out.println(Thread.currentThread().getName()+i);
        }
    }
}
public class Demo01 {
    public static void main(String[] args) {
        My my = new My();
        //使线程开始执行;Java虚拟机调用这个线程的run方法。
        //结果是两个线程并行运行:当前线程(从调用start方法返回)和另一个线程(执行其run方法)
        my.start();
        for (int i = 0; i <500 ; i++) {
            System.out.println("1:"+i);
        }

    }
}

2.创建多线程的方式二:声明实现类Runnable接口,并重写run方法
package com.kz.b_runnable;

import static java.lang.Thread.currentThread;

//              实现Runnable接口
class My implements Runnable{
         //重写run
    @Override
    public void run() {
        //获取线程名字
        String name = currentThread().getName();
        for (int i = 0; i <250 ; i++) {
            System.out.println(name+":"+i);
        }
    }
}

public class Demo01{
    public static void main(String[] args) {

        Thread thread = new Thread(new My());
        System.out.println(thread);
          new Thread(new My()).start();
          new Thread(new My()).start();
          new Thread(new My()).start();
    }
}

开发中优先选择:实现Runnable接口的方式
原因:
1.实现的方式没有类的单继承的局限性
2.实现的方式更适合来处理多个线程有共享数据的情况
3.创建多线程的方式三:实现Callable接口 —> JDK 5.0新增
//1.创建一个实现Callable的实现类
class NumThread implements Callable{

    //2.实现call方法,将此线程需要执行的操作声明在call()中
    @Override
    public Object call() throws Exception {
        int sum = 0;
        for(int i = 1;i <= 100;i++){
            if(i % 2 == 0){
                System.out.println(i);
                sum += i;
            }
        }
        return sum;
    }
}

public class ThreadNew {
    public static void main(String[] args) {
        //3.创建Callable接口实现类的对象
        NumThread numThread = new NumThread();

        //4.将此Callable接口实现类的对象作为传递到FutureTask构造器中,创建FutureTask的对象
        FutureTask futureTask = new FutureTask(numThread);

        //5.将FutureTask的对象作为参数传递到Thread类的构造器中,创建Thread对象,并调用start()
        new Thread(futureTask).start();

        try {
            //6.获取Callable中call方法的返回值
            //get()返回值即为FutureTask构造器参数Callable实现类重写的call()的返回值。
            Object sum = futureTask.get();
            System.out.println("总和为:" + sum);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}
如何理解实现Callable接口的方式创建多线程比实现Runnable接口创建多线程方式强大
1.call()可以有返回值的。
2.call()可以抛出异常,被外面的操作捕获,获取异常的信息
3.Callable是支持泛型的
4.需要借助FutureTask类,比如获取返回结果
4.创建多线程的方式四:使用线程池
class NumberThread implements Runnable{
    @Override
    public void run() {
        for(int i = 0;i <= 100;i++){
            if(i % 2 == 0){
                System.out.println(Thread.currentThread().getName() + ":" + i);
            }
        }
    }
}

class NumberThread1 implements Runnable{
    @Override
    public void run() {
        for(int i = 0;i <= 100;i++){
            if(i % 2 != 0){
                System.out.println(Thread.currentThread().getName() + ":" + i);
            }
        }
    }
}

public class ThreadPool {
    public static void main(String[] args) {

        //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 NumberThread());  //适合适用于Runnable
        service.execute(new NumberThread1());  //适合适用于Runnable

//        service.submit(Callable callable);   //适合适用于Callable

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

好处:
1.提高响应速度(减少了创建新线程的时间)
2.降低资源消耗(重复利用线程池中线程,不需要每次都创建)
3.便于线程管理
corePoolSize:核心池的大小
maximumPoolSize:最大线程数
keepAliveTime:线程没有任务时最多保持多长时间后会终止

创建多线程的方式有4种常用第二种

2.Thread常用方法
测试Thread中的常用方法:
1.start():启动当前线程:调用当前线程的run()
2.run():通常需要重写Thread类中的此方法,将创建的线程要执行的操作声明在此方法中
3.currentThredd():静态方法,返回执行当前代码的线程
4.getName():获取当前线程的名字
5.setName():设置当前线程的名字
6.yield():释放当前cpu的执行权
7.join():停止当前线程 执行分线程完毕 在执行该线程
8.stop():过时 强制停止
9.sleep(long millitme):让当前进行指定毫秒进行一一执行
10.isAlive():判断当前线程是否存活
线程的优先级:
1.MAX_PRIORITY=10
2.MIN_PRIORITY=1
3.NORM_PRIORITY=5  默认优先级
2.getPriority():获取线程优先级
3.setPriority():设置线程的优先级

并不是高优先级 一定会比低的快

3.线程安全

Java是允许多线程,当多个线程操作同一个资源的时候,会导致得到或者打印的数据不准确。从而发生冲突

public class Windos {
    public static void main(String[] args) {
        window window=new window();
        window window1=new window();
        window window2=new window();
        window.setName("窗口一");
        window1.setName("窗口二");
        window2.setName("窗口三");
        window.start();
        window1.start();
        window2.start();
    }
}

class window extends Thread{
    private static int ticket=100;//静态属性
    @Override
    public void run() {
          while (true){
              if (ticket>0){
                  System.out.println(getName()+":"+ticket);
                  ticket--;
              }else {break;}
          }
    }
}

存在线程安全问题

4.解决线程安全问题 1.synchronized锁
同步代码块
 synchronized(同步监视器){
      //需要被同步的代码
  }
说明
   1.操作共享数据的代码,即为需要被同步的代码
   2.共享数据 多个线程共同操作的变量
   3.同步监视器 锁 任何一个类的对象 都可以充当锁
   4.要求 多个线程必须用同一把锁

 如果是继承的多线程 需要锁 也是静态  不可用this
public class WindowTest {
    public static void main(String[] args) {
        A a=new A();

        Thread thread=new Thread(a);
        Thread thread1=new Thread(a);
        Thread thread2=new Thread(a);
        thread.setName("窗口1");
        thread1.setName("窗口2");
        thread2.setName("窗口3");
        thread.start();
        thread1.start();
        thread2.start();
    }
}

class A implements Runnable{
    private  int tic=100;
    Object object=new Object();
    @Override
    public void run() {
//        synchronized(this){
        while (true){
        synchronized(A.class){//类也是对象  类只加载一次
        try {
            Thread.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
         if (tic>0)
            System.out.println(Thread.currentThread().getName()+":"+tic);
            tic--;
        }
      }
    }
}
2.Lock锁
Lock锁---jdk5.0新增
 1.面试题 synchronized与Lock的异同
 相同:两者都可以解决线程安全问题
 不同:synchronized机制在执行完相应的同步代码以后,自动释放同步监视器
 Lock需要手动的启动同步Lock(),结束也需要手动结束unLock()
public class LockTest {
    public static void main(String[] args) {
        Window w=new Window();
        Thread thread=new Thread(w);
        Thread thread1=new Thread(w);
        Thread thread2=new Thread(w);
        thread.start();
        thread1.start();
        thread2.start();
    }
}
class Window implements Runnable{
    private int id=100;
    private ReentrantLock lock=new ReentrantLock();
    @Override
    public void run() {
        while (true){
            try {
                lock.lock();//手动上锁
                if (id>0){
                    System.out.println(Thread.currentThread().getName()+":"+id);
                    id--;
                }
            }finally {
                lock.unlock();//手动关锁
            }

       }
     }

}
5.死锁
演示线程的死锁
1.死锁的理解:不同的线程分别占用对方需要的同步资源不放弃,
       都在等待对方放弃自己需要的同步资源,就形成了线程的死锁
2.说明:
     出现死锁后,不会出现异常,不会出现提示,只是所有的线程都处于阻塞状态,无法继续
     我们使用同步时,要避免出现死锁。
package JavaSenior.多线程.线程安全问题;
    // 懒汉式的线程安全问题
public class BankTest {
}


class Bank{
    private Bank(){}
    private static Bank instance = null;

    public Bank getInstance(){
        if (instance ==null){//效率更高
        synchronized (Bank.class){if (instance==null){
            Bank instance=new Bank();
            return instance;
        }
      }
     }
        return instance;
    }
}

class ThreadTest {
    public static void main(String[] args) {

        StringBuffer s1 = new StringBuffer();
        StringBuffer s2 = new StringBuffer();

        new Thread(){
            @Override
            public void run() {

                synchronized (s1){
                    s1.append("a");
                    s2.append("1");

                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                    synchronized (s2){
                        s1.append("b");
                        s2.append("2");

                        System.out.println(s1);
                        System.out.println(s2);
                    }
                }
            }
        }.start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (s2){
                    s1.append("c");
                    s2.append("3");

                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                    synchronized (s1){
                        s1.append("d");
                        s2.append("4");

                        System.out.println(s1);
                        System.out.println(s2);
                    }
                }
            }
        }).start();
    }
}

5.面试题
涉及到线程3个方法
wait() 一旦执行此方法,当前线程就进入阻塞状态,并释放同步监视器
notify() 会换醒被wait的一个线程
notifyAll() 换醒所有

1.必须都写在同步代码块中
2.调用者也必须是同步监视器
定义在Java.lang.Object

面试题 sleep()和wait()的异同
相同:都可以进入阻塞状态
异同:1.两个方法声明的位置不同,Thread类中声明sleep(),Object类中声明wait()
2.调用的要求不同:sleep()可以在任何需要的场景下调用,wait必须在同步代码块中
3.如果两个方法都使用在同步代码块中,sleep不会释放锁,wait会释放锁

public class Numtest {
    public static void main(String[] args) {
        Num num=new Num();
        Thread thread=new Thread(num);
        Thread thread1=new Thread(num);
        thread.start();
        thread1.start();
    }
}

class Num implements Runnable{

    private int id=1;


  public void run(){
      while(true){
          synchronized (this){
              notify();//交替
          if (id<=100){
              try {
                  Thread.sleep(100);
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }
              System.out.println(Thread.currentThread().getName()+":"+id);
              id++;
              //交替
              try {
                  wait();
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }
          }else {
              break;
             }

          }
      }
  }
}
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/1002502.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号