Bootstrap

软件设计之Java入门视频(14)

软件设计之Java入门视频(14)

视频教程来自B站尚硅谷:

尚硅谷Java入门视频教程,宋红康java基础视频
相关文件资料(百度网盘)
提取密码:8op3
idea 下载可以关注 软件管家 公众号

学习内容:

该视频共分为1-717部分
本次内容涉及390-449(由于前半部分是项目,就不放上来展示)
在写代码时,总是需要来回切换界面来看代码要求,这里推荐Snipaste,可以把截图以窗口形式放在屏幕上
记录内容:

  1. 多线程创建
  2. 同步
  3. 线程通信

1、多线程创建

方式1

继承于Thread类
1)创建一个继承于Thread类的子类
2)重写Thread类的run()–>此线程执行的操作声明在run()中
3)创建Thread类的子类的对象
4)通过此对象调用start()

public class ThreadTest {
    public static void main(String[] args) {
        MyThread t1 = new MyThread();
        t1.start();
    }

}


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

方式2 实现Runnalbe接口

1)创建一个实现了Runnable接口的类
2)实现类去实现Runnable中的抽象方法:run()
3)创建实现类的对象
4)将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象
5)通过Thread类的对象调用start()

package exer;

public class ThreadMethodTest {
    public static void main(String[] args) {
        MThread mThread = new MThread();
        Thread thread = new Thread(mThread);
        thread.setName("线程1");
        thread.start();
        Thread thread1 = new Thread(mThread);
        thread1.setName("线程2");
        thread1.start();
    }
}

class MThread implements Runnable{

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

新增方式1:实现Callable接口创建线程

package exer;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

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()
        Thread thread = new Thread(futureTask);
        thread.start();
        try {
            //6.获取Callable方法的返回值
            //get()返回值为FutureTask构造器参数Callable实现类重写的call返回值
            Object sum = futureTask.get();
            System.out.println(sum);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } catch (ExecutionException e) {
            throw new RuntimeException(e);
        }
    }
}
//1.创建一个实现Callable的实现类
class NumThread implements Callable{
    //2.实现call方法,将此线程需要执行的操作声明在call()中
    @Override
    public Object call() throws Exception {
        int sum = 0;
        for (int i = 0; i <=100 ; i++) {
            if (i%2==0){
                System.out.println(i);
                sum +=i;
            }
        }
        return sum;
    }
}

新增方式2:使用线程池

好处:
1)提高响应速度
2)降低资源消耗
3)便于线程管理

package exer;

import java.util.concurrent.*;

public class ThreadNew {
    public static void main(String[] args) {
        //1.提供指定线程数量的线程池
        ExecutorService service = Executors.newFixedThreadPool(10);
        ThreadPoolExecutor service1 = (ThreadPoolExecutor) service;
        //设置线程池的属性
//        service1.setCorePoolSize(15);
//        service1.setKeepAliveTime();
        //2.执行中的的线程的操作,需要提供实现Runnalbe接口或Callable接口实现类的对象
        service.execute(new NumberThread());//适合用于Runnalbe
//      service.submit();//适合用于Callable
        //3.关闭连接池
        service.shutdown();
    }
}
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);
            }
        }
    }
}

比较创建线程的两种方式

开发中:优先选择:实现Runnable接口的方式
1、实现的方式没有类的单继承性的局限性
2、实现的方式更适合来处理多个线程有共享数据的况。
联系:public class rhread implements Runnable
相同点:两种方式都需要重写run(),将线程要执行的逻辑声明在run()中。

Thread 常用方法

1)start() :启动当前线程;调用当前线程的run()
2)run():通常需要重写Thread类中的此方法,将创建的线程要执行的操作声明在此方法中
3)current Thread():静态方法,返回执行当前代码的线程
4)getName():获取当前线程的名字
5)setName():设置当前线程的名字
6)yield():释放当前cpu的执行权
7)join():在线程a中调用线程b的join(),此时线程a就进入阻塞状态,直到线程b完全执行完以后,线程a才结束阻塞状态
8)sleep(long millitime):让当前线程“睡眠”指定的毫秒。在指定的毫秒时间内,当前线程是阻塞状态
isAlive():判断当前线程是否存活

线程的优先级

1)MAX_PRIORITY 10 MIN_PRIORITY 1 NORM_PRIORITY 5 (默认优先级)
2)如何获得和设置当前线程的优先级
  a)getPriority():获取线程的优先级
  b)setPriority(int p):设置线程的优先级
3)说明:高优先级的线程要抢占低优先级线程cpu的执行权。但是只是从概率上讲,高优先级的线程高概率的情况下被执行。并不意味着只有当高优先级的线程执行完以后,低优先级的线程才执行。

线程的生命周期

在这里插入图片描述

2、同步

通过同步来解决线程安全问题
1)同步代码块:
  a)操作共享数据的代码,为需要被同步的代码
  b)共享数据:多个线程共同操作的变量
  c)同步监视器:俗称:锁,任何一个类的对象都能充当锁
  d)多个线程必须共用同一把锁
2)同步的方式,解决了线程的安全问题。—好处
3)操作同步代码时,只能有一个线程参与,其他线程等待。相当于是一个单线程的过程。
4)同步方法:
  a)操作共享数据的代码,为需要被同步的代码
5)Lock锁

同步代码块

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

同步方法–Thread类

package exer;

public class WindowTest2 {
        public static void main(String[] args) {
            exer.Window1 window1 = new exer.Window1();
            Window2 thread1 = new  Window2();
            Window2 thread2 = new  Window2();
            Window2 thread3 = new  Window2();
            thread1.setName("窗口1");
            thread2.setName("窗口2");
            thread3.setName("窗口3");
            thread1.start();
            thread2.start();
            thread3.start();
        }
    }

class Window2 extends Thread{
    private static int ticket = 100;
    @Override
    public void run() {
        while (true){
            show();
        }
    }
    private static synchronized void show(){ //static 保证各对象调用的是同一方法
        if (ticket > 0) {
            System.out.println(Thread.currentThread().getName() + ":卖票,票号为" + ticket);
            ticket--;
        }
    }
}

同步方法–Runnalbe接口

package exer;

public class WindowTest1 {
    public static void main(String[] args) {
        Window1 window1 = new Window1();
        Thread thread1 = new Thread(window1);
        Thread thread2 = new Thread(window1);
        Thread thread3 = new Thread(window1);
        thread1.setName("窗口1");
        thread2.setName("窗口2");
        thread3.setName("窗口3");
        thread1.start();
        thread2.start();
        thread3.start();
    }
}

class Window1 implements Runnable{
    private int ticket = 100;
    @Override
    public void run() {
        while (true){
            show();
        }
    }
    private synchronized void show(){
        if (ticket > 0) {
            System.out.println(Thread.currentThread().getName() + ":卖票,票号为" + ticket);
            ticket--;
        }
    }
}

单例–懒汉式同步

package exer.java1;

public class BankTest {


}
class Bank {
    public Bank() {
    }

    private static Bank instance = null;

    public static Bank getInstance() {
        //方式一:效率稍差
//        synchronized (Bank.class) {
//            if (instance == null) {
//                instance = new Bank();
//            }
//        return instance;
//        }
        //方式二:
        if (instance ==null){
         synchronized (Bank.class) {
            if (instance == null) {
                instance = new Bank();
            }
        }
        }
        return instance;
    }
}

Lock锁

1)synchronized与Lock的异同?
相同:二者都可以解决线程安全问题
不同:synchronized机制在执行完相应的同步代码以后,自动的释放同步监视器Lock需要手动的启动同步(lock()),同时结束同步也需要手动的实现(unlock()
2)优先使用顺序:
Lock → 同步代码块(已经进入了方法体,分配了相应资源)→ 同步方法(在方法体之外)

package exer;

import java.util.concurrent.locks.ReentrantLock;

public class WindowTest1 {
    public static void main(String[] args) {
        Window1 window1 = new Window1();
        Thread thread1 = new Thread(window1);
        Thread thread2 = new Thread(window1);
        Thread thread3 = new Thread(window1);
        thread1.setName("窗口1");
        thread2.setName("窗口2");
        thread3.setName("窗口3");
        thread1.start();
        thread2.start();
        thread3.start();
    }
}

class Window1 implements Runnable{
    private int ticket = 100;
    //实例化ReentratLock
    private ReentrantLock lock = new ReentrantLock();
    @Override
    public void run() {
        while (true){
            try{
                lock.lock();
            if (ticket > 0) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
                System.out.println(Thread.currentThread().getName() + ":卖票,票号为" + ticket);
                ticket--;
        }}finally {
                //调用解锁的方法
                lock.unlock();
            }
            }
    }

}

死锁问题

1)不同的线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源,就形成了线程的死锁
2)出现死锁后,不会出现异常,不会出现提示,只是所有的线程都处于
阻塞状态,无法继续

package exer.java1;

public 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) {
                        throw new RuntimeException(e);
                    }
                    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) {
                        throw new RuntimeException(e);
                    }
                    synchronized (s1) {
                        s1.append("d");
                        s2.append("4");
                        System.out.println(s1);
                        System.out.println(s2);
                    }
                }
            }
        }).start();
    }
}

3、线程通信

1)涉及到的三个方法:
wait():一旦执行此方法,当前线程就进入阻塞状态,并释放同步监视器
notify():一旦执行此方法,就会唤醒被wait的一个线程。如果有多个线程被wait,就唤醒优先级高的
notifyAll():一旦执行此方法,就会唤醒所有被wait的线程。
2)注意点
wait,notify,notifyAll三个方法必须使用在同步代码块同步方法
wait,notify,notifyAll三个方法的调用者必须是同步代码块或同步方法中的同步监视器,否则会出IllegalMonitorStateException异常

package exer;

import java.util.concurrent.locks.ReentrantLock;

public class CommunicationTest {

    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();

    }
}


class Number implements Runnable{
    private int number = 1;
//    private ReentrantLock lock = new ReentrantLock();
    @Override
    public void run() {
        while (true){
            synchronized (this) {
                //唤醒被wait的线程
                notify();
                if (number<=100){
                    System.out.println(Thread.currentThread().getName() + ":"+ number);
                    number++;
                    try {
                        //使得调用wait方法的线程进入阻塞状态
                        wait();
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }else {
                    break;
                }
            }
        }
    }
}
;