Java尚硅谷基础笔记-day9多线程

8.1 基本概念:程序,进程,线程

在这里插入图片描述
每个线程都有独立的栈,程序计数器
多个线程共享同一个进程中的结构:方法区,堆

在这里插入图片描述
一个Java应用程序java.exe,其实至少有三个线程: main()主线程, gc()垃圾回收线程,异常处理线程。当然如果发生异常,会影响主线程。
单核CPU(假的多线程,一个时间单元内只能执行一个线程任务)与多核CPU
并行(多个CPU同时执行多个任务,多个人做不同的事)与并发(一个CPU同时执行多个任务,秒杀,多个人做同一件事)
在这里插入图片描述
在这里插入图片描述

8.2 线程的创建和使用

多线程的创建方式一:继承于Thread类

public class ThreadTest {
    public static void main(String[] args) {
        //3.创建Thread类的子类的对象
        Mythread t1 = new Mythread();
        //4.通过此对象调用start():启动当前线程;调用当前线程的run()
        t1.start();
        System.out.println("hello");
        //t1.run();不能通过直接调用run的方式(普通方法)启动线程
        //t1.start();//再启动一个线程,不可以再让以及start的线程去执行,会报错IllegalThreadStateException
        //创建一个新的线程对象
        Mythread t2 = new Mythread();
        t2.start();
        //创建Thread类的匿名子类的方式
        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();

    }

}
//1.创建一个继承于Thread类的子类
class Mythread extends Thread{
    //2.重写Thread类的run()
    @Override
    public void run() {
        for (int i = 0; i <100 ; i++) {
            if(i % 2==0) {
                System.out.println(Thread.currentThread().getName()+":"+i);
            }
        }
    }
}

Thread类的有关方法
1.start():启动当前线程:调用当前线程的run()
2.run():通过需要重写的Thread类中的此方法,将创建的线程要执行的操作声明在此方法中
3.currentThread():静态方法,返回执行当前代码的线程
4.getName():获取当前线程的名字
5.setName():设置当前线程的名字
6.yeild():释放当前cpu的执行权
7.join():在线程a中调用线程b的join,此时线程a就进入阻塞状态,知道线程b完全执行完后,线程a 才结束阻塞状态
8.stop():已过时。当执行此方法时,强制结束当前线程
9.sleep(long millitime):让当前线程“睡眠”指定的millitime毫秒,在指定的millitime毫秒时间内,当前线程是阻塞状态。
10.isAlive():判断当前线程是否存活
线程的调度
同优先级线程组成先进先出队列(先到先服务),使用时间片策略
对高优先级,使用优先调度的抢占式策略
线程的优先级
在这里插入图片描述

public class ThreadMethodTest {
    public static void main(String[] args) {
        HelloThread h1 = new HelloThread();
        h1.setName("线程一");
        h1.setPriority(Thread.MAX_PRIORITY);//概率上,优先级高被优先执行的概率高
        //不意味着高优先级的线程执行完,低优先级的线程才执行
        h1.start();
        //给主线程命名
        Thread.currentThread().setName("主线程");
        Thread.currentThread().setPriority(Thread.MIN_PRIORITY);
        for (int i = 0; i <100 ; i++) {
            if(i%2==0){
                System.out.println(Thread.currentThread().getName()+":"+i);
            }
            if(i==20){
                try {
                    h1.join();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
        System.out.println(h1.isAlive());//false
    }
}
class HelloThread extends Thread{
    @Override
    public void run() {
        for (int i = 0; i <100 ; i++) {
            if(i % 2==0) {
//                try {//不能throws异常,因为父类没有抛异常
//                    //子类重写异常不能大于父类
//                    sleep(1000);
//                } catch (InterruptedException e) {
//                    e.printStackTrace();
//                }
                //System.out.println(Thread.currentThread().getName()+":"+i);
                System.out.println(getName()+":"+i);
            }
//            if(i==20){
//                yield();
//            }
        }
    }
}

多线程的创建方式二:实现Runnable接口

public class ThreadTest1 {
    public static void main(String[] args) {
        //3.创建实现类的对象
        Mythread1 m1 = new Mythread1();
        //4.将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象
        Thread t1=new Thread(m1);
        //5.通过Thread类的对象调用start:启动线程;调用当前线程的run--->调用了Runable类型的run
        t1.start();
        //再启动一个线程
        Thread t2=new Thread(m1);
        t2.start();
    }
}
//1.定义子类,实现Runnable接口
class Mythread1 implements Runnable{
    //2.实现类去实现Runnable中的抽象方法
    @Override
    public void run() {
        for (int i = 0; i <100 ; i++) {
            if(i % 2==0) {
                System.out.println(Thread.currentThread().getName()+":"+i);
                //不能直接getname
            }
        }
    }
}

比较创建线程的两种方式
开发中:优先选择:实现Runable接口的方式
原因:1.实现的方式没有类的单继承性的局限性
2.实现的方式更适合来处理多个线程有共享数据的情况
联系:public class Thread implements Runable
相同点:两种方式都需要重写run,将线程要执行的逻辑声明在run中
在这里插入图片描述

8.3 线程的生命周期

在这里插入图片描述
关注两个概念:状态,相应的方法
关注:状态a----状态b:哪些方法执行了(回调方法)
某个方法的主动调用:状态a----状态b
阻塞为临时状态,死亡为最终状态

8.4 线程的同步

模拟火车站售票程序,开启三个窗口售票。
不安全问题:sleep的方法,加大了三个窗口同时抢得一张票的概率,即重票错票问题。问题出现原因:当某个线程操作车票过程中,尚未操作完成时,其他线程也参与进来,操作车票。

public class Windowclass1 {
    public static void main(String[] args) {
        Window1 w1 = new Window1();
        Thread t1 = new Thread(w1);
        Thread t2 = new Thread(w1);
        Thread t3 = new Thread(w1);
        t1.setName("窗口1");
        t2.setName("窗口2");
        t3.setName("窗口3");
        t1.start();
        t2.start();
        t3.start();
    }
}
class Window1 implements Runnable{
    private int ticket=100;
    @Override
    public void run() {
        while(true){
            if(ticket>0){
                try {
                    Thread.sleep(100);//静态方法
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+":卖票,票号为:"+ticket);
                ticket--;
            }else{
                break;
            }
        }
    }
}

方式一:同步代码块
synchronized(同步监视器){
//需要被同步的代码
}
说明:
1.操作共享数据的代码,即为需要被同步的代码
2.共享数据:多个线程共同操作的变量。比如:ticket
3.同步监视器,俗称:锁。任何一个类的对象,都可以充当锁
要求:多个线程必须要共用同一把锁
4.在实现Runnable接口创建多线程的方式中,我们可以考虑使用this充当同步监视器。

public class Windowclass {
    public static void main(String[] args) {
        Window w1 = new Window();
        Window w2 = new Window();
        Window w3 = new Window();
        w1.setName("窗口1");
        w2.setName("窗口2");
        w3.setName("窗口3");
        w1.start();
        w2.start();
        w3.start();
    }
}
class Window extends Thread{
    private static int ticket=100;
    private static Object obj=new Object();

    @Override
    public void run() {
        while(true){
            //synchronized (obj){
            //慎用this充当同步监视器,考虑使用当前类充当同步监视器
            //synchronized (this){//不唯一,w1,w2,w3三个对象
            synchronized (Window.class){
                //Class class=Window.class,只会加载一次
                if(ticket>0){
                    System.out.println(getName()+":卖票,票号为:"+ticket);
                    ticket--;
                }else{
                    break;
                }
            }

        }

    }
}
public class Windowclass1 {
    public static void main(String[] args) {
        Window1 w1 = new Window1();
        Thread t1 = new Thread(w1);
        Thread t2 = new Thread(w1);
        Thread t3 = new Thread(w1);
        t1.setName("窗口1");
        t2.setName("窗口2");
        t3.setName("窗口3");
        t1.start();
        t2.start();
        t3.start();
    }
}
class Window1 implements Runnable{
    private int ticket=100;
    Object obj=new Object();

    @Override
    public void run() {
        while(true){
            //synchronized (obj){
            synchronized (this){//此时的this:唯一的window1的对象
                if(ticket>0){
                    try {
                        Thread.sleep(100);//静态方法
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()+":卖票,票号为:"+ticket);
                    ticket--;
                }else{
                    break;
                }
            }

        }
    }
}

方式二:同步方法
1.同步方法仍然涉及到同步监视器,只是不需要我们显示的声明
2.非静态的同步方法:同步监视器:this
3.静态的同步方法,同步监视器是:当前类本身

public class Windowclass1 {
    public static void main(String[] args) {
        Window1 w1 = new Window1();
        Thread t1 = new Thread(w1);
        Thread t2 = new Thread(w1);
        Thread t3 = new Thread(w1);
        t1.setName("窗口1");
        t2.setName("窗口2");
        t3.setName("窗口3");
        t1.start();
        t2.start();
        t3.start();
    }
}
class Window1 implements Runnable{
    private int ticket=100;
    Object obj=new Object();

    @Override
    public  void run() {
        while(ticket>0){
            show();
        }
    }
    public synchronized void show(){//同步监视器this
        if(ticket>0){
            try {
                Thread.sleep(100);//静态方法
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+":卖票,票号为:"+ticket);
            ticket--;
        }

    }
    //public synchronized void run() {
        //synchronized(this){一个窗口一直取票
//        while(true){
//            if(ticket>0){
//                try {
//                    Thread.sleep(100);//静态方法
//                } catch (InterruptedException e) {
//                    e.printStackTrace();
//                }
//                System.out.println(Thread.currentThread().getName()+":卖票,票号为:"+ticket);
//                ticket--;
//            }else{
//                break;
//            }
//        }
//    }
}
public class Windowclass {
    public static void main(String[] args) {
        Window w1 = new Window();
        Window w2 = new Window();
        Window w3 = new Window();
        w1.setName("窗口1");
        w2.setName("窗口2");
        w3.setName("窗口3");
        w1.start();
        w2.start();
        w3.start();
    }
}
class Window extends Thread{
    private static int ticket=100;
    private static Object obj=new Object();

    @Override
    public  void run() {
        while(ticket>0){
            show();
        }
    }
    public static synchronized void show(){//同步监视器Window.class
        //public synchronized void show(){//同步监视器this:w1,w2,w3
        if(ticket>0){
            try {
                Thread.sleep(100);//静态方法
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+":卖票,票号为:"+ticket);
            ticket--;
        }

    }
}

单例模式懒汉式

class Bank{
    private Bank(){}
    private static Bank instance=null;
    public static synchronized Bank getInstance(){
        if(instance==null){
            instance=new Bank();
        }
        return instance;
    }
}
class Bank{
    private Bank(){}
    private static Bank instance=null;
    public static Bank getInstance(){
        //方式一:效率稍差
//        synchronized (Bank.class) {
//            if(instance==null){
//                instance=new Bank();
//            }
//            return instance;
//        }
        //方式二 效率稍高,后面不为null的对象直接返回,不需要等待
        if(instance==null){
            synchronized (Bank.class) {
                if (instance == null) {
                    instance = new Bank();
                }
            }    
        }
        return instance;

    }
}

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

class A {
	public synchronized void foo(B b) {
		System.out.println("当前线程名: " + Thread.currentThread().getName()
				+ " 进入了A实例的foo方法"); // ①
		try {
			Thread.sleep(200);
		} catch (InterruptedException ex) {
			ex.printStackTrace();
		}
		System.out.println("当前线程名: " + Thread.currentThread().getName()
				+ " 企图调用B实例的last方法"); // ③
		b.last();
	}

	public synchronized void last() {
		System.out.println("进入了A类的last方法内部");
	}
}

class B {
	public synchronized void bar(A a) {
		System.out.println("当前线程名: " + Thread.currentThread().getName()
				+ " 进入了B实例的bar方法"); // ②
		try {
			Thread.sleep(200);
		} catch (InterruptedException ex) {
			ex.printStackTrace();
		}
		System.out.println("当前线程名: " + Thread.currentThread().getName()
				+ " 企图调用A实例的last方法"); // ④
		a.last();
	}

	public synchronized void last() {
		System.out.println("进入了B类的last方法内部");
	}
}

public class DeadLock implements Runnable {
	A a = new A();
	B b = new B();

	public void init() {
		Thread.currentThread().setName("主线程");
		// 调用a对象的foo方法
		a.foo(b);
		System.out.println("进入了主线程之后");
	}

	public void run() {
		Thread.currentThread().setName("副线程");
		// 调用b对象的bar方法
		b.bar(a);
		System.out.println("进入了副线程之后");
	}

	public static void main(String[] args) {
		DeadLock dl = new DeadLock();
		new Thread(dl).start();
		dl.init();
	}
}

方式三:Lock锁
面试题:synchronized与Lock的区别
相同:二者都可以解决线性安全问题
不同:synchronized机制在执行完相应的同步代码以后,自动释放同步监视器
Lock需要手动的启动同步(Lock())。同时结束同步也需要手动的实现(unlock())
在这里插入图片描述

public class WindowTest2 {
    public static void main(String[] args) {
        Window2 w1 = new Window2();
        Thread t1 = new Thread(w1);
        Thread t2 = new Thread(w1);
        Thread t3 = new Thread(w1);
        t1.setName("窗口1");
        t2.setName("窗口2");
        t3.setName("窗口3");
        t1.start();
        t2.start();
        t3.start();
    }
}
class Window2 implements Runnable {
    private int ticket = 100;
    //1.实例化ReentrantLock
    private ReentrantLock lock=new ReentrantLock();

    @Override
    public void run() {
        while (true) {
            try {
                //2.调用lock()
                lock.lock();
                if (ticket > 0) {
                    try {
                        Thread.sleep(100);//静态方法
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + ":卖票,票号为:" + ticket);
                    ticket--;
                }else{ break;}
            } finally {
                //3.调用解锁方法unlock()
                lock.unlock();
            }

        }
    }

}

练习1
银行有一个账户。有两个储户分别向同一个账户存3000元, 每次存1000, 存3次。每次存完打印账户余额。

public class AccountTest {
    public static void main(String[] args) {
        Account acc = new Account();
        Customer c1 = new Customer(acc);
        Customer c2 = new Customer(acc);
        c1.setName("储户1");
        c2.setName("储户2");
        c1.start();
        c2.start();
    }
}
class Account{
    private static double balance=0;
    public void deposit(double amount){
        synchronized (this) {//同一个账户,可以使this
            balance+=amount;
            System.out.println(Thread.currentThread().getName()+":存款,余额为:"+getBalance());
        }
    }

    public double getBalance() {
        return balance;
    }
}
class Customer extends Thread{
    private static Account account;
    public Customer(Account account){
        this.account=account;
    }
    @Override
    public void  run() {
        for (int i = 0; i < 3; i++) {
            account.deposit(1000);

        }
    }
}

8.5 线程的通信

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

面试题:sleep() 和 wait()的异同?
1.相同点:一旦执行方法,都可以使得当前的线程进入阻塞状态。
2.不同点:1)两个方法声明的位置不同:Thread类中声明sleep() , Object类中声明wait()
2)调用的要求不同:sleep()可以在任何需要的场景下调用。 wait()必须使用在同步代码块或同步方法中
3)关于是否释放同步监视器:如果两个方法都使用在同步代码块或同步方法中,sleep()不会释放锁,wait()会释放锁。
**例子:**使用两个线程打印 1-100。线程1, 线程2 交替打印

public class NumberTest {
    public static void main(String[] args) {
        Number number = new Number();
        Thread t1 = new Thread(number);
        Thread t2 = new Thread(number);
        t1.start();
        t2.start();
    }
}
class Number implements Runnable{
    private int num=0;
    private Object obj=new Object();
    @Override
    public void run() {
        while (true) {
            //synchronized (this) {
            //    notify(); //this.notify();
            synchronized (obj) {
            //    this.notify();  error
                obj.notify();
                if(num<=100){
                    System.out.println(Thread.currentThread().getName()+":"+num);
                    num++;
                }else{break;}
                try {
                    //wait();//this.wait();
                    obj.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }

    }
}

在这里插入图片描述

public abstract class ProductTest {
    public static void main(String[] args) {
        Clerk clerk=new Clerk();
        Customer c1=new Customer(clerk);
        Productor p1=new Productor(clerk);
        Thread t1=new Thread(p1);
        Thread t2=new Thread(c1);
        Thread t3=new Thread(c1);
        t1.start();
        t2.start();
        t3.start();
    }
}
class Clerk {
    private  int num=0;

    public int getNum() {
        return num;
    }

    public synchronized void product(){
        if(num<20){
            num++;
            System.out.println(Thread.currentThread().getName()+":生产第"+num+"个产品");
            notify();
        }else{
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }
    public synchronized void consume(){
        if(num>0){
            System.out.println(Thread.currentThread().getName()+":,消耗第"+num+"个产品");
            num--;
            notify();
        }else{
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
class Productor implements Runnable{
    private Clerk clerk=new Clerk();

    public Productor(Clerk clerk) {
        this.clerk = clerk;
    }

    @Override
    public void run() {
        while(true){
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            clerk.product();
        }
    }
}
class Customer implements Runnable{
    private Clerk clerk=new Clerk();

    public Customer(Clerk clerk) {
        this.clerk = clerk;
    }

    @Override
    public void run() {
        while(true){
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            clerk.consume();
        }
    }
}

8.6 JDK5.0新增线程创建方式

创建线程的方式三:实现Callable接口-----JDK5.0新增
如何理解实现Callable接口的方式创建多线程比实现Runnable接口创建多线程方式强大?

call()可以有返回值的。
call()可以抛出异常,被外面的操作捕获,获取异常的信息
Callable是支持泛型的

public class ThreadNew {
    public static void main(String[] args) {
        //3.创建callable接口实现类的对象
        NumThread numThread = new NumThread();
        //4.将此callbale接口实现类的对象传递到FutureTask构造器中,创建FutureTask对象
        FutureTask futureTask=new FutureTask(numThread);
        //5.将FutureTask的对象作为参数传递到Thread类的构造器中,创建Thread对象,并调用start
        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.创建一个实现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;
    }
}

创建线程的方式四:使用线程池-----JDK5.0新增
思路:提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中。可以避免频繁创建销毁、实现重复利用。类似生活中的公共交
通工具。
好处:
1.提高响应速度(减少了创建新线程的时间)
2.降低资源消耗(重复利用线程池中线程,不需要每次都创建)
3.便于线程管理
corePoolSize:核心池的大小
maximumPoolSize:最大线程数
keepAliveTime:线程没有任务时最多保持多长时间后会终止
面试题:创建多线程有几种方式?四种!

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.submit(Callable callable);//适合适用于Callable
        //关闭连接池
        service.shutdown();
    }
}
class NumberThread implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i <=100 ; i++) {
            if(i%2==0){
                System.out.println(i);
            }
        }
    }
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值