Java教程

尚硅谷Java初学笔记——多线程

本文主要是介绍尚硅谷Java初学笔记——多线程,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

尚硅谷Java初学笔记——多线程

文章目录

  • 尚硅谷Java初学笔记——多线程
    • 多线程的理解
    • 线程的创建和使用
    • Thread类中常用方法
    • 线程的优先级
    • 线程的生命周期
    • 线程安全问题的解决
    • 线程通信
    • JDK5.0后线程的创建方式

多线程的理解

理解: 将一个程序比作高速收费站,那么一个收费窗口就是一个线程, 多个窗口就是多线程。
注意要点

  • 每个线程单独有一个栈(stack)和程序计数器(pc)。
  • 多个线程共用一个堆(heap)
  • 每个线程独立进行

线程的创建和使用

  • 创建线程1(不推荐): 写一个子类继承Thread, 重写run()方法
public class MyThread extends Thread{
    @Override
    public void run() {//重写run()方法
        for (int i = 0; i < 5; i++)
            System.out.println(Thread.currentThread().getName() + ":" + i);
    }
}
  • 使用线程1: 调用start()方法
public class Test {
    public static void main(String[] args) {
        MyThread t1 = new MyThread();
        MyThread t2 = new MyThread();//这里创建第二个线程
        t1.start();
        t2.start();
    }
}

运行结果:

  • 创建线程2(推荐):写一个实现Runable 接口的类,重写run()方法,将该类的对象作为参数构造Thread对象, 参数一样的Thread对象共用相同堆区域。
class MyThread implements Runnable{
    int i = 10;
    @Override
    public void run() {//重写run()方法
        while(i > 0) {
            System.out.println(Thread.currentThread().getName() + ":" + i--);
        }
    }
}
public class Test {
    public static void main(String[] args) {
        MyThread m = new MyThread();
        Thread t1 = new Thread(m);
        t1.start();
    }
}
  • 也可以用匿名类的形式直接创建并执行一个线程:
new Thread(){
            @Override
            public void run() {
                //语句
            }
        }.start();

Thread类中常用方法

方法名作用
getName()返回当前线程的名称
setName()设置当前线程的名称
currentThread()返回当前线程 (static方法)
yeild()a线程中调用a.yeild后CPU重新选择一个线程执行
join()a线程中调用b.join后,a阻塞,b执行到结束后a再执行
sleep(x)当前线程阻塞x毫秒 (static方法)
isAlive()返回当前线程是否存活

线程的优先级

  • 线程有优先级,CPU切换线程时切换为优先级高的线程的概率更大
  • 优先级为int类型 1 <= 优先级的值 <= 10
  • Thread定义了三个(static int)值
标识符
MAX_PRIORITY10
NORM_PRIORITY5
MIN_PRIORITY1
  • 优先级有关方法
方法作用
getPriority()返回线程的优先级
setPriority(x)将优先级设置为x

线程的生命周期

在这里插入图片描述

线程安全问题的解决

多个线程在操作共享数据时可能会引发线程安全问题, 解决方法:

  • 1. synchronized关键字解决
synchronized (obj) {//obj为锁,可以为任何对象
 		//操作共享数据的代码块
}

原理被相同锁锁住的多个代码块一个执行完另一个才能开始执行
Runnable的方法实现多线程一般锁为 this
继承Thread的方法实现多线程锁一般为 子类类名.class

    1. 用带synchronized关键字的方法(不推荐), 声明为synchronized 的方法相当于给整个函数加上默认锁 ——类名.class
public synchronized void method(){

}
    1. JDK5.0后的Lock接口解决
public class MyThread implements Runnable{
  int i = 30;
  //JDK5.0开始的同步锁接口Lock()
  static ReentrantLock lock = new ReentrantLock();//ReentrantLock为Lock接口实现类
  @Override
  public void run() {
      while(true) {
          lock.lock();
          if (i > 0)//手动锁定
              System.out.println(i--);
          else
              break;
          lock.unlock();//手动解锁
      }
  }
}
public class Test {
  public static void main(String[] args) {
      MyThread m = new MyThread();
      new Thread(m).start();
      new Thread(m).start();
  }
}

死锁问题

public static void main(String[] args) {
      Object a = new Object();
      Object b = new Object();
      new Thread(){//线程一
          @Override
          public void run() {
              synchronized(a){
                  System.out.println("a-");
                  try {
                      sleep(1000);//sleep增加死锁概率
                  }catch (Exception e){
                      e.printStackTrace();
                  }
                  synchronized(b){
                      System.out.println("a-b");
                  }
              }
          }
      }.start();
      new Thread(){//线程二
          @Override
          public void run() {
              synchronized(b){
                  System.out.println("b-");
                  try {
                      sleep(1000);
                  }catch (Exception e){
                      e.printStackTrace();
                  }
                  synchronized(a){
                      System.out.println("b-a");
                  }
              }
          }
      }.start();
  }

线程1:a锁嵌套b锁,
线程2:b锁嵌套a锁
如果线程一执行到b锁的同时,线程二执行到a锁,此时线程一的b锁无法开始执行(线程二占用),同理线程二里面的a锁也无法执行。 造成两线程阻塞,这就是死锁问题
输出如下:
在这里插入图片描述

线程通信

线程通信的有关方法 (Object类的成员方法)

方法作用
wait()阻塞当前线程并解锁线程里的锁
notify()恢复含有的当前锁的另一个阻塞的线程(根据线程优先级)
notifyAll()恢复含有当前锁的所有阻塞的线程

所有方法只能在同步代码块中调用且调用者是锁

JDK5.0后线程的创建方式

  • 实现Callable接口, 实现类中重写call()方法, call()有返回值
    具体使用见代码:
public class MyThread implements Callable {
    @Override
    public Object call() throws Exception {
        System.out.println("线程执行中....");
        return Integer.valueOf("20");
    }
}
public class Test {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        MyThread m = new MyThread();
        FutureTask f = new FutureTask(m);//返回值存储类
        Thread t = new Thread(f);
        t.start();
        //获取返回值
        Object i = f.get();
        System.out.println("返回值:" + i);

    }

这种方式可以实现泛型编程

  • 线程池
    实际开发过程中常用的方法,预先创建多个线程, 有任务时再分配线程.避免多次创建和销毁线程,节约时间且方便线程的管理。
public class Test {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //ExecutorService为线程池的接口
        //Executors为工具类,可以新建线程池
        ExecutorService s = Executors.newFixedThreadPool(10);//10个线程的线程池
        Object o = s.submit(new Callable(){//submit方法适用于callable
            @Override
            public Object call() throws Exception {
                System.out.println("callable执行中");
                return null;
            }
        }).get();
        
        s.execute(new Runnable() {//execute适用于Runnable
            @Override
            public void run() {
                System.out.println("Runable执行中");
            }
        });
        s.shutdown();//终止线程池
    }

设置线程池属性方便管理

public class Test {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ThreadPoolExecutor tp =(ThreadPoolExecutor) Executors.newFixedThreadPool(10);
        tp.setCorePoolSize(20);//线程池属性设置
        tp.setMaximumPoolSize(100);
    }
}
这篇关于尚硅谷Java初学笔记——多线程的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!