Java多线程笔记

概念

  • 程序:一段静态的代码 用来完成特定的任务,用某种语言编写的指令集合
  • 进程:程序的一次执行过程 或者是正在运行的程序,有生命周期
  • 线程:进程细化为多个线程 是一个程序内部的一条执行路径
    线程作为调度和执行单位 每个线程拥有独立的运行栈 和程序计数器
    在这里插入图片描述
    线程的6种状态
    NEW:初始状态,线程被创建,但是还没有被调用start()方法;
    RUNNABLE:运行状态,
    BLOCKED:阻塞状态;
    WAITING:等待状态,表示线程进入等待,进入该状态表示当前线程需要其他线程做出特定动作(通知或中断)
    TIME_WAITING:超时等待,不同于waiting,它是可以在指定时间自行返回的
    TERMINATED:终止状态,表示当前线程执行完毕

线程的创建和使用

一些常用方法

测试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毫秒时间内,当前
    线程是阻塞状态。sleep必须捕获异常

  10. isAlive():判断当前线程是否存活*

  11. 线程的优先级:
    MAX_PRIORITY:10
    MIN _PRIORITY:1
    NORM_PRIORITY:5 -->默认优先级

  12. 如何获取和设置当前线程的优先级:
    getPriority():获取线程的优先级
    setPriority(int p):设置线程的优先级

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

sleep(0):有什么含义
“触发操作系统立刻重新进行一次CPU竞争”

继承Thread类

步骤

  1. 创建一个类继承Thread类
  2. 重写Thread类的Run()方法
  3. 创建子类对象
  4. 调用对象的start()
package com.dc.thread;
//创建一个继承与thread的类
子线程
class MyThread extends Thread{
     //重写thread的run()
     @Override
     public void run() {
         for (int i = 0; i < 100; i++) {
              if (i % 2 == 0) {
                  System.out.println(i);
              }
         }
     }
}

public class ThreadTest {
 主线程public static void main(String[] agrs) {
         //创建子类对象
         MyThread t1 = new MyThread();
         //调用start()
         t1.start();
     }

注意:

  1. start作用:1.启动当前线程 ;2.调用当前线程的run()
  2. 不能通过调用run()启动线程 使用t1.run() 不会报错 没有开启一个新的线程只有一个主线程 使用的是父类的run()方法
  3. 不能让已经start()的线程去执行 会报错 illegalThreadStateException 解决:new t2
  4. run()不能throws :因为父类thread的run()没有抛异常 所以子类重写不能抛 只能try catch

练习:遍历100内偶数和奇数

package com.dc.exer;
/**
* 创建两个分线程  一个遍历100内偶数 一个遍历100内奇数
* @author DC
*
*/
public class ThreadDemo {
    public static void main(String[] args) {
        //方法一
//        MyThread1 myThread1 = new MyThread1();
//        MyThread2 myThread2 = new MyThread2();
//        myThread1.start();
//        myThread2.start();
        //方法二 匿名子类
        new Thread(){
            @Override
            public void run() {
                for (int i = 0 ; i < 100; i++) {
                    if (i %  2 == 0) {
                        System.out.println(Thread.currentThread().getName()+":"+i);
                    }


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


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


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


        }
    }
}

模拟卖票

class Window extends Thread{
    private static Integer tickets = 100;


    @Override
    public void run() {
        while (true){
            if (tickets > 0){
                System.out.println(getName() + ":卖出第" + tickets +"张票 ");
                tickets--;
            }else{
                break;
            }
        }
    }
}
public class WindowsTest {
    public static void main(String[] args) {
        Window window = new Window();
        Window window1 = new Window();
        Window window2 = new Window();
        window.setName("窗口0");
        window1.setName("窗口1");
        window2.setName("窗口2");
        window.start();
        window1.start();
        window2.start();
    }


}

实现Runnable接口

步骤

  1. 创建一个实现了runnable接口的类
  2. 实现类去实现runnable中的抽象方法 run()
  3. 创建实现类对象
  4. 将此对象作为参数传递到thread类中的构造器 创建thread对象
  5. 通过thread类的对象调用start()
 private Runnable target;
* public Thread(Runnable target)
*  public void run() {
*         if (target != null) {
*             target.run();
*         }
*     }
* 
 //1
class  MThread implements Runnable{


    @Override
    //2
    public void run() {
        for (int i = 0; i < 100; i++) {
            if (i % 2 == 0) {
                System.out.println(i);
            }
        }
    }
}
public class ThreadTest1 {
    public static void main(String[] args) {
        //3
        MThread mThread = new MThread();
        //4
        Thread thread = new Thread(mThread);
        //5
        thread.start();
    }
}

模拟卖票

class  Window1 implements Runnable{
    private   Integer tickets = 100;
    @Override
    public void run() {
        while (true){
            if (tickets > 0){
                System.out.println(Thread.currentThread().getName() + ":卖出第" + tickets +"张票 ");
                tickets--;
            }else{
                break;
            }
        }
    }
}
public class WindowsTest2 {
    public static void main(String[] args) {
        Window1 window1 = new Window1();


        Thread thread0 = new Thread(window1);
        Thread thread1 = new Thread(window1);
        Thread thread2 = new Thread(window1);
        thread0.start();
        thread1.start();
        thread2.start();
    }
}

实现Callable接口

步骤

  1. 创建一个实现callable的实现类
  2. 实现call方法 将需要线程执行的操作放在call中
  3. 创建callable接口实现类的对象
  4. 将此callable接口的实现类的对象作为参数传递到futuretask中
  5. 将ft的对象作为参数传递到thread类中 创建thread对象 调用start
/**
* 创建线程的方式三 实现callable 借助future task类 jdk5 新增
*
*
* 如何理解 callable比runnaable 强大
*  1.call方法可以有返回值
*  2.call可以抛出异常
*  3.callable支持泛型
* @author DC
* @create 2020-08-01-15:47
*/
class NumThread implements Callable{
    @Override
    public Object call() throws Exception {
        //遍历100以内偶数 返回和
        int sum = 0;
        for (int i = 0; i <= 100; i++) {
            if( i % 2 == 0){
                System.out.println(i);
                sum += i;


            }
        }
        return sum;
    }
}
public class ThreadNew {
    public static void main(String[] args) {
        NumThread n1 = new NumThread();
        FutureTask futureTask = new FutureTask(n1);
        new Thread(futureTask).start();
        try {
            //get方法的返回值 即为futuretask构造器参数callable实现类重写call的返回值
            Object sum = futureTask.get();
            System.out.println(sum);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}

线程池

步骤

  1. 提供指定数量的线程池
  2. 执行指定的线程操作 需要提供实现Runnable 或callable接口实现类的对象
  3. service.execute(Runnable);
    service.submit(callable)
  4. 关闭

/**
* 实现多线程方式4  线程池

* 好处:
* 提高响应速度
* 降低资源消耗
* 便于线程管理
* @author DC
* @create 2020-08-01-16:49
*/
class NThread implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i <= 100; i++) {
            if (i % 2 == 0){
                System.out.println(i);
            }


        }
    }
}
public class ThreadPool {
    public static void main(String[] args) {
        ExecutorService service = Executors.newFixedThreadPool(10);
        NThread nThread = new NThread();
        service.execute(nThread);
//        service.execute();适合适用于runnable
//        service.submit();适合使用于callable
        service.shutdown();//关闭线程池
    }
}

线程安全

同步代码块

synchronized(同步监视器){
需要被同步的代码(操作共享数据的代码)
共享数据:多个线程操作的变量
同步监视器:(锁):任何一个类的对象
要求:多个线程共用一个锁
在实现runnable实现多线程时 可以考虑用this
在继承 thread创建多线程时 慎用this, 考虑用当前类.class

}

实现Runnable接口的 同步代码块方法

class  Window1 implements Runnable{
    private   Integer tickets = 100;
    Object obj = new Object();
    @Override
    public void run() {
        while (true){
            synchronized (this){//当前对象的this
                if (tickets > 0){
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + ":卖出第" + tickets +"张票 ");
                    tickets--;
                }else{
                    break;
                }
            }


        }
    }
}


public class WindowsTest2 {
    public static void main(String[] args) {
        Window1 window1 = new Window1();


        Thread thread0 = new Thread(window1);
        Thread thread1 = new Thread(window1);
        Thread thread2 = new Thread(window1);
        thread0.start();
        thread1.start();
        thread2.start();


    }
}

继承Thread类的 同步代码块

class Window extends Thread{
    private static Integer tickets = 100;
    private  static  Object obj = new Object();
    @Override
    public void run() {
        while (true){
            synchronized (obj){//不能用this  这里有t123三个对象  window2.class  也可以
                if (tickets > 0){
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(getName() + ":卖出第" + tickets +"张票 ");
                    tickets--;
                }else{
                    break;
                }
            }


        }
    }
}


public class WindowsTest {
    public static void main(String[] args) {
        Window window = new Window();
        Window window1 = new Window();
        Window window2 = new Window();
        window.setName("窗口0");
        window1.setName("窗口1");
        window2.setName("窗口2");
        window.start();
        window1.start();
        window2.start();






    }

同步方法

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

  1. 仍涉及到锁 只是没有显式声明
  2. runnable 接口) 非静态的同步方法 锁 :this
    继承) 静态同步方法 锁 :当前类本身

使用同步方法 解决实现runnable接口

/**
* 
*
* @author DC
* @create 2020-07-31-15:08
*/
class  Window3 implements Runnable{
    private   Integer tickets = 100;
    Object obj = new Object();
    @Override
    public void run() {
        while (tickets > 0 ){


            show();
        }
    }
    public synchronized void show(){//锁为this


            if (tickets > 0){
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + ":卖出第" + tickets +"张票 ");
                tickets--;
            }
        }
}






public class WindowTest3 {
    public static void main(String[] args) {
        Window3 window3 = new Window3();


        Thread thread0 = new Thread(window3);
        Thread thread1 = new Thread(window3);
        Thread thread2 = new Thread(window3);
        thread0.start();
        thread1.start();
        thread2.start();
    }
}

继承Thread类,

class Window4 extends Thread{
    private static Integer tickets = 100;
    private  static  Object obj = new Object();
    @Override
    public void run() {
        while (true){
                show();
        }
    }
    public static synchronized  void show(){//方法声明为静态方法  锁为window4.class
        if (tickets > 0){
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + ":卖出第" + tickets +"张票 ");
            tickets--;
        }
    }
}


public class WindowTest4 {
    public static void main(String[] args) {
        Window4 window = new Window4();
        Window4 window1 = new Window4();
        Window4 window2 = new Window4();
        window.setName("窗口0");
        window1.setName("窗口1");
        window2.setName("窗口2");
        window.start();
        window1.start();
        window2.start();
    }
}

Lock锁

解决线程安全的方式三 lock锁 jdk5 新增

  1. 实例化 ReentrantLock 将同步代码块 try finall
  2. 调用lock.lock()
  3. 调用lock.unlock();

synchronized lock 异同
同 解决线程安全问题
异 synchronized机制在执行完相应的同步代码逻辑后 自动释放同步监视器
lock 需要手动启动和结束同步

注2
如何

class Window5 implements  Runnable {
    private int tickets = 100;
    //1
    private ReentrantLock lock = new ReentrantLock();
    @Override
    public void run() {
        while (true) {
            try {
                //2
                lock.lock();


                if (tickets > 0) {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }


                    System.out.println(Thread.currentThread().getName() + ":卖出第" + tickets + "张票 ");
                    tickets--;
                } else {
                    break;
                }
            }finally {
                //3
                lock.unlock();
            }


        }
    }
}
public class LockTest {
    public static void main(String[] args) {
        Window5 window5 = new Window5();


        Thread thread1 = new Thread(window5);
        Thread thread2 = new Thread(window5);
        Thread thread3 = new Thread(window5);


        thread1.setName("窗口1");
        thread2.setName("窗口2");
        thread3.setName("窗口3");
        thread1.start();
        thread2.start();
        thread3.start();
    }
}

线程通信

wait()使用调用wait方法的线程阻塞 wait会释放锁
notify() 唤醒被wait的一个线程 如果有多个线程 先唤醒优先级高的
notifyall() 唤醒所有被wait的一个线程
说明:

  1. 这三个方法必须使用在同步代码块 和 同步方法中
  2. 这三个方法的调用者 必须是同步代码块 和 同步方法中的同步监视器 否则会出现错误IlleagalMonitorStateException
  3. 这三个方法都在java .lang.object类中

面试题:sleep() 和wait()
同: 一旦执行方法都可以使当前线程进入阻塞状态

异:1.这两个方法声明的位置不同 sleep在Thread类中 wait在objec类中
2.调用的要求不同:sleep可以在任何场景调用 wait必须在在同步代码块 和 同步方法中调用
3.sleep不会释放锁 wait会释放锁

使用两个线程交替打印1-100

class Number implements  Runnable{
    private int i = 1;
    private  Object obj = new Object();
    @Override
    public void run() {
        while (true){
            synchronized (obj) {
                obj.notify();
                if (i <= 100){
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()+":"+i);
                    i++;
                    try {
                        //使用调用wait方法的线程阻塞 wait会释放锁 sleep不会释放锁
                       obj. wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }else{
                    break;
                }
            }
        }
    }
}
public class ComTest {
    public static void main(String[] args) {
        Number number = new Number();
        Thread t1 = new Thread(number);
        Thread t2 = new Thread(number);
        t1.setName("线程1");
        t2.setName("线程2");
        t1.start();
        t2.start();
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值