java基础之多线程

线程

概念

进程和线程

进程是一个应用程序(1个进程是一个软件)。

线程是一个进程中的执行场景/执行单元。

一个进程可以启动多个线程。

线程是依赖于进程的

多线程的意义

多线程的特点:具有随机性

多个线程在抢占CPU的执行权

面试题

jvm是多线程吗?

是多线程,至少有两条线程

用户线程main,以及创建对象的时候,当对象使用完毕,需要被垃圾回收器回收;

jvm针对没有更多引用对象,开启一条垃圾回收线程!

java语言能够开启多线程?

开启线程---->开启进程----Java语言不能够开启进程—借助于底层语言C语言开启进程
封装成本地方法 ----->jdk提供类:Thread 里面封装好的方法
开启线程:start()

Thread类的构造方法

Thread(String name):创建线程类对象,设置名称

Thread类的成员方法

public final String getName():获取线程名称
public final void setName(String name):设置线程名称

线程的优先级

Thread类中静态常量字段(成员变量field)
public static final int MAX_PRIORITY 10     最大优先级
public static final int MIN_PRIORITY 1      最小优先级
public static final int NORM_PRIORITY 5     默认优先级
public final void setPriority(int newPriority):设置线程的优先级
public final int getPriority():获取优先级

优先级越大的:抢占CPU的执行权越大

优先级小的:抢占CPU的执行权越小

默认优先级:随机性大一些

实现线程有两种方式

java支持多线程机制。并且java已经将多线程实现了,我们只需要继承就行了。

第一种方式:

编写一个类,直接继承java.lang.Thread,重写run方法。
	// 定义线程类
	public class MyThread extends Thread{
		public void run(){
		
		}
	}
	// 创建线程对象
	MyThread t = new MyThread();
	// 启动线程。
	t.start();

第二种方式:

编写一个类,实现java.lang.Runnable接口,实现run方法。
	// 定义一个可运行的类
	public class MyRunnable implements Runnable {
		public void run(){
		
		}
	}
	// 创建线程对象
	Thread t = new Thread(new MyRunnable());
	// 启动线程
	t.start();

注意:第二种方式实现接口比较常用,因为一个类实现了接口,它还可以去继承
其它的类,更灵活。

线程对象的生命周期

新建状态
就绪状态
运行状态
阻塞状态
死亡状态

方法

Join线程强制执行

public final void join() throws InterruptedException;//等待该线程终止!

底层依赖于线程安全的方法join(0):永远等待(当前执行完毕结束!),又依赖于wait(long time)

eg.

考虑安全问题:

三个线程—优先级 都是默认(5)

都去设置join(): 三个线程谁先抢占到谁先执行

join方法,m1调用之后,执行完m1才会执行join方法后的start方法

public class ThreadTest01 {

    public static void main(String[] args) {

        MyThread m1 = new MyThread();
        MyThread m2 = new MyThread();
        MyThread m3 = new MyThread();

        m1.setName("老大");
        m2.setName("老二");
        m3.setName("老三");

        m1.setPriority(10);
        m2.setPriority(5);
        m3.setPriority(1);


        m1.start();
        try {
            m1.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        m2.start();
        try {
            m2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        m3.start();

    }

}

class MyThread extends Thread {

    public void run() {
        for (int i = 0; i < 3; i++) {
            System.out.println(getName() + ":" + i);
        }
    }

}
老大:0
老大:1
老大:2
老二:0
老二:1
老二:2
老三:0
老三:1
老三:2

yield线程礼让

public static void yield();//暂停当前正在执行的线程,执行对方线程

eg.

public class ThreadTest02 {

    public static void main(String[] args) {
        //创建两条线程对象
        YieldThread yt1 = new YieldThread() ;
        YieldThread yt2 = new YieldThread() ;

        //设置名称
        yt1.setName("老大") ;
        yt2.setName("老二") ;

        //启动线程
        yt1.start();
        yt2.start();
    }
}

class YieldThread  extends Thread{

    @Override
    public void run() {
        for(int x = 0 ; x <100 ; x ++){
            System.out.println(getName()+":"+x);
            Thread.yield(); //暂停当前线程,执行对方线程
        }
    }

}

sleep线程休眠

public class ThreadTest04 {

    public static void main(String[] args) {

        Movies m1 = new Movies();
        Movies m2 = new Movies();
        Movies m3 = new Movies();

        m1.setName("窗口1");
        m2.setName("窗口2");
        m3.setName("窗口3");

        m1.start();
        m2.start();
        m3.start();

    }

}

class Movies extends Thread {

    public static int tickets = 100;


    @Override
    public void run() {
        while (true) {

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

            if (tickets > 0) {
                System.out.println(getName() + "正在出售第" + tickets-- + "张票");
            }
        }
    }

}

setDaemon

public final void setDaemon(boolean on);
   //参数为true,表示标记当前线程为守护线程,当正在运行的线程如果都是守护线程,则jvm自动退出。这个方法必须在启动线程之前调用(start()之前)

举例:
玩游戏:坦克大战
这个两个坦克----守护线程

eg.

public class ThreadTest05 {

    public static void main(String[] args) {

        //创建两个线程
        ThreadDaemon td1 = new ThreadDaemon() ;
        ThreadDaemon td2 = new ThreadDaemon() ;

        //设置名称
        td1.setName("张飞");
        td2.setName("关羽");

        //设置为守护线程
        td1.setDaemon(true) ;
        td2.setDaemon(true) ;

        //启动线程
        td1.start();
        td2.start();

        //public static Thread currentThread():获取正在运行的线程执行对象的引用
        Thread.currentThread().setName("刘备");
        //提供for循环:
        for(int x = 0 ; x < 5 ; x ++){
            System.out.println(Thread.currentThread().getName()+":"+x);
        }

    }

}

class ThreadDaemon extends Thread {
    @Override
    public void run() {
        for (int x = 0 ; x < 100; x ++){
            System.out.println(getName()+":"+x);
        }
    }
}
刘备:0
刘备:1
刘备:2
刘备:3
刘备:4
张飞:0
张飞:1
张飞:2
张飞:3
张飞:4
张飞:5
张飞:6
关羽:0
关羽:1
//一会自己就停了

线程创建实例

多线程的实现方式2步骤

1)自定义类实现Runnable接口
2)重写Runnable接口的run方法
3)在main用户线程中
可以分配类的实例(创建类的实例)
4)创建当前类对象,然后创建Thread类对象,将当前类对象作为参数来传递
当前类---->“资源共享类”
Thread(Runnable target, String name)
5)分别启动线程即可!

静态代理

特点

真实角色和代理角色必须实现同一个接口

角色

真实角色:专注于自己的功能

代理角色:完成对真实角色功能的"增强",可以去做很多真实对象做不了的事情

eg.结婚

真实角色:You 你自己

代理角色:WeddingCompany 婚庆公司

public class ThreadTest01 {

    public static void main(String[] args) {

        You you = new You();
        you.marry();

        WeddingCompany weddingCompany = new WeddingCompany(you);
        weddingCompany.marry();
        
        
        new WeddingCompany(new You()).marry();
       
        new Thread(()->System.out.println("我爱你")).start();
       	//()->System.out.println("我爱你") ,实现于Runnable接口

    }

}

interface Marry{
    void marry();
}

//结婚的人
class You implements  Marry{

    @Override
    public void marry() {
        System.out.println("结婚了,很开心...");
    }
}

//代理角色:婚庆公司 在你结婚之前,它可以给你布置婚礼线程, 结婚之后,开开心心吃席
class WeddingCompany implements Marry{

    //将真实角色作为参数传递
    private You you ;

    public WeddingCompany() {
    }

    public WeddingCompany(You you){
        this.you = you ;
    }

    @Override
    public void marry() {
        System.out.println("给你布置婚礼现场...");
        you.marry();  //只要专注于自己的事情!
        System.out.println("婚礼线程布置完毕,吃席...");
    }
}
结婚了,很开心...
给你布置婚礼现场...
结婚了,很开心...
婚礼线程布置完毕,吃席...

Lamda表达式

函数式接口的定义: 任何接口,如果只包含唯一一个抽象方法,那么他就是一个函数接口

对于函数式接口,我们可以通过lamda表达式来创建接口对象

 public interface Runnable{
        public abstract void run();
    }
public class LamdaTest {

    //3.静态内部类
    static class Like2 implements lLike {

        @Override
        public void lamda() {
            System.out.println("我喜欢2");
        }
    }

    public static void main(String[] args) {
        lLike like = new Like();
        like.lamda();//1

        like = new Like2();
        like.lamda();//2


        //4.局部内部类
        class Like3 implements lLike {
            @Override
            public void lamda() {
                System.out.println("我喜欢3");
            }
        }

        like = new Like3();
        like.lamda();//3

        //5.匿名内部类
        like = new lLike() {
            @Override
            public void lamda() {
                System.out.println("我喜欢4");
            }
        };
        like.lamda();//4

        //6.lamda表达式
        like = () -> {
            System.out.println("我喜欢5");
        };
        like.lamda();//5

        //化简
        like = () -> System.out.println("我喜欢6");
        like.lamda();

        /*总结:

        */

    }
}

//1.定义一个接口
interface lLike {
    void lamda();
}

//2.子实现类
class Like implements lLike {

    @Override
    public void lamda() {
        System.out.println("我喜欢1");
    }
}
我喜欢1
我喜欢2
我喜欢3
我喜欢4
我喜欢5
我喜欢6

lambda 表达式C只能有一行代码的情况下才能简化成为一行, 如果有多行,那么就用代码块包安。

前提是接口必须为函数型接口

多个参数也可以去掉参数类型,要去掉就都去掉

多线程安全

检验多线程安全问题的标准;

1)是否是多线程环境,不能更改,使用多线程实现

2)是否存在共享数据,必须要有共享数据

3)是否存在多条语句对共享数据的操作

解决

Java提供同步机制:

同步代码块 将多条对共享数据包裹起来

synchronized(锁对象){
    将多条对共享数据包裹起来
}

锁对象:必须要多个线程使用的同一个锁对象,而不是分别自己的锁对象!

出现同票的原因:

线程的原子性操作(++,–:最简单的操作)

原子性:记录原始数据值,然后再对数据自增或者自减

eg.

class Movie implements Runnable {
    private int tickets = 1000;
    private Object obj = new Object();

    @Override
    public void run() {
        while (true) {
            synchronized (obj) {
                if (tickets > 0) {
                    System.out.println(Thread.currentThread().getName() + "正在出售第" + tickets + "张票");
                    tickets--;
                } else {
                    return;
                }
            }
        }
    }
}

public class ThreadTest02 {
    public static void main(String[] args) {
        Movie m = new Movie();

        Thread t1 = new Thread(m, "窗口1");
        Thread t2 = new Thread(m, "窗口2");
        Thread t3 = new Thread(m, "窗口3");

        t1.start();
        t2.start();
        t3.start();
    }

}

窗口2正在出售第100张票
窗口2正在出售第99张票
窗口1正在出售第98张票
窗口1正在出售第97张票
窗口1正在出售第96张票
窗口1正在出售第95张票
窗口1正在出售第94张票
窗口1正在出售第93张票
窗口1正在出售第92张票
窗口3正在出售第91张票
窗口3正在出售第90张票
窗口3正在出售第89张票
窗口3正在出售第88张票
窗口3正在出售第87张票
窗口3正在出售第86张票

……

对象锁synchronized

锁对象:必须为多个线程的同一把锁;锁对象可以是任意Java的类对象

synchronized(锁对象){
    多条语句对共享数据的操作
}

面试题:

wait()方法/notify()方法 ---- 也可以称为"同步" —>等待唤醒机制

线程等待/线程唤醒,这些方法为什么不定义在Thread类中呢,而是定义Object类中呢?

它和锁对象有关系,而锁对象,可以任何的java类对象,-----Object(顶层父类)

syncronized(锁对象){
     锁对象.wait()
   	 //锁对象.notify()
}
什么是同步方法?

如果一个方法的方法体的第一句话就是同步代码块

可以将synchronized关键字提取到方法声明上,跟在权限修饰符的后面

权限修饰符 synchronized 返回值类型 方法名(形式列表){   //非静态的同步方法
     业务逻辑...
}

关于synchronized的使用

面试题:

doother方法执行的时候需不需要等待dosome方法的结束?

eg.1

public class synchronizeExam01 {

    public static void main(String[] args) throws InterruptedException {

        MyClass ms = new MyClass();

        Thread t1 = new MyThread(ms);
        Thread t2 = new MyThread(ms);

        t1.setName("t1");
        t2.setName("t2");

        t1.start();
        Thread.sleep(1000);//为了确保t1先执行
        t2.start();

    }

}

class MyThread extends Thread {

    private MyClass ms;

    public MyThread(MyClass ms) {
        this.ms = ms;
    }

    @Override
    public void run() {
        if(Thread.currentThread().getName().equals("t1")){
            ms.dosome();
        }else if(Thread .currentThread().getName().equals("t2")){
            ms.doother();
        }
    }
}

class MyClass {

    //synchronize出现在实例方法上,表this
    public synchronized void dosome() {
        System.out.println("doSome begin");
        try {
            Thread.sleep(1000 * 10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("doSome over");
    }

    //没有synchronize
    public void doother() {
        System.out.println("doother begin");
        System.out.println("doother over");
    }

}
//不需要,因为doother没有synchronize
/*
doSome begin
doother begin
doother over
(十秒后)
doSome over
 */

eg.2

public class synchronizeExam02 {

    public static void main(String[] args) throws InterruptedException {

        MyClass ms = new MyClass();

        Thread t1 = new MyThread(ms);
        Thread t2 = new MyThread(ms);

        t1.setName("t1");
        t2.setName("t2");

        t1.start();
        Thread.sleep(1000);//为了确保t1先执行
        t2.start();

    }

}

class MyThread extends Thread {

    private MyClass ms;

    public MyThread(MyClass ms) {
        this.ms = ms;
    }

    @Override
    public void run() {
        if(Thread.currentThread().getName().equals("t1")){
            ms.dosome();
        }else if(Thread .currentThread().getName().equals("t2")){
            ms.doother();
        }
    }
}

class MyClass {

    //synchronize出现在实例方法上,表this
    public synchronized void dosome() {
        System.out.println("doSome begin");
        try {
            Thread.sleep(1000 * 10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("doSome over");
    }

    public synchronized void doother() {
        System.out.println("doother begin");
        System.out.println("doother over");
    }

}
//需要等待,因为在一个所里面
/*
doSome begin
(十秒)
doSome over
doother begin
doother over
 */

eg.3

public class synchronizeExam03 {

    public static void main(String[] args) throws InterruptedException {

        MyClass ms1 = new MyClass();
        MyClass ms2 = new MyClass();

        Thread t1 = new MyThread(ms1);
        Thread t2 = new MyThread(ms2);

        t1.setName("t1");
        t2.setName("t2");

        t1.start();
        Thread.sleep(1000);//为了确保t1先执行
        t2.start();

    }

}

class MyThread extends Thread {

    private MyClass ms;

    public MyThread(MyClass ms) {
        this.ms = ms;
    }

    @Override
    public void run() {
        if(Thread.currentThread().getName().equals("t1")){
            ms.dosome();
        }else if(Thread .currentThread().getName().equals("t2")){
            ms.doother();
        }
    }
}

class MyClass {

    //synchronize出现在实例方法上,表this
    public synchronized void dosome() {
        System.out.println("doSome begin");
        try {
            Thread.sleep(1000 * 10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("doSome over");
    }

    public synchronized void doother() {
        System.out.println("doother begin");
        System.out.println("doother over");
    }

}
//不需要,因为不是一个锁里,有两把锁
/*
doSome begin
doother begin
doother over
(十秒)
doSome over
 */

eg.4

public class synchronizeExam04 {

    public static void main(String[] args) throws InterruptedException {

        MyClass ms = new MyClass();

        Thread t1 = new MyThread(ms);
        Thread t2 = new MyThread(ms);

        t1.setName("t1");
        t2.setName("t2");

        t1.start();
        Thread.sleep(1000);//为了确保t1先执行
        t2.start();

    }

}

class MyThread extends Thread {

    private MyClass ms;

    public MyThread(MyClass ms) {
        this.ms = ms;
    }

    @Override
    public void run() {
        if(Thread.currentThread().getName().equals("t1")){
            ms.dosome();
        }else if(Thread .currentThread().getName().equals("t2")){
            ms.doother();
        }
    }
}

class MyClass {
    //synchronize出现在静态方法上,是类锁
    public synchronized static void dosome() {
        System.out.println("doSome begin");
        try {
            Thread.sleep(1000 * 10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("doSome over");
    }

    public synchronized static void doother() {
        System.out.println("doother begin");
        System.out.println("doother over");
    }

}
//需要等待,因为静态方法是类锁,不管创建了几个对象,类锁只有一把
/*
doSome begin
(十秒)
doSome over
doother begin
doother over
 */

死锁

线程安全问题:

可以通过同步方法或者是同步代码块去解决,但是执行过程中就可能出现死锁问题

死锁问题:

(使用同步机制解决线程安全) 线程和线程之间出现了互相等待的情况!

解决方案:

多个线程之间的通信:必须使用的是一个资源类对象,而不能是每一个线程在使用自己的资源类对象!

使用生成者和消费者模式思想去解决,前提条件:生成者线程和消费者线程 必须操作的同一个资源类对象!

synchronize不要嵌套使用,容易死锁!!!

产生死锁的四个必要条件:

1.互斥条件:一个资源每次只能被一个进程使用。

2.请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。

3.不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。

4.循环等待条件:若干进程之间形成-种头尾相接的循环等待资源关系。

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

        Object o1 = new Object();
        Object o2 = new Object();

        Thread t1 = new MyThread1(o1, o2);
        Thread t2 = new MyThread2(o1, o2);

        t1.start();
        t2.start();

    }
}

class MyThread1 extends Thread {

    Object o1;
    Object o2;

    public MyThread1(Object o1, Object o2) {
        this.o1 = o1;
        this.o2 = o2;
    }

    @Override
    public void run() {
        synchronized (o1) {
            System.out.println("T1O1");
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (o2) {
                System.out.println("T1O2");
            }
        }
    }
}

class MyThread2 extends Thread {

    Object o1;
    Object o2;

    public MyThread2(Object o1, Object o2) {
        this.o1 = o1;
        this.o2 = o2;
    }

    @Override
    public void run() {
        synchronized (o2) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("T2O2");
            synchronized (o1) {
                System.out.println("T2O1");
            }
        }
    }
}
T1O1
T2O2

生产者消费者问题

概念

1、使用wait万法和notify方法实现“生产者和消费者模式”

2、什么是“生产者和消费者模式?
生产线程负责生产,消费线程负责消费。
生产线程和消费线程要达到均衡。
这是一种特殊的业务需求,在这种特殊的情况下需要使用wait方法和notify方法。

3、wait称notify方法不是线程对象的方法,是普通java对象都有的方法。

4、wait方法和notify方法建立在线程同步的基础之上。因为多线程要同时操作一个仓库。有线程安全问题。

5、wait方法作用:o.wait()让正在o对象上活动的线程进入等待状态,并且释放掉t线程之前占有的o对象的锁。

6、notify方法作用:o.notify()让正在o对象上等待的线程晚醒,只是通知,不会释放o对象上之前占有的锁。

实例

eg.1信号灯法

import java.util.ArrayList;
import java.util.List;

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

        List list = new ArrayList();

        Thread t1 = new Thread(new Proudcer(list));
        Thread t2 = new Thread(new Consumer(list));

        t1.setName("生产者线程");
        t2.setName("消费者线程");

        t1.start();
        t2.start();

    }
}

class Proudcer implements Runnable {

    private List list;

    public Proudcer(List list) {
        this.list = list;
    }

    @Override
    public void run() {
        //一直生产
        while (true) {
            //给仓库list上锁
            synchronized (list) {
                if (list.size() > 0) {//大于0,仓库以及有东西了
                    try {
                        list.wait();//当前线程处于等待状态,并且释放producer之前占有的list集合的锁。
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                //程序到这里说明仓库是空的,可以生产
                Object o = new Object();
                list.add(o);
                System.out.println(Thread.currentThread().getName()+"="+o);
                //唤醒消费者进行消费
                list.notifyAll();//唤醒全部也没关系,因为没有唤醒锁
            }
        }
    }
}

class Consumer implements Runnable {

    private List list;

    public Consumer(List list) {
        this.list = list;
    }

    @Override
    public void run() {
        //一直消费
        while (true) {
            synchronized (list){
                if (list.size() == 0){
                    //仓库已空
                    //消费者线程等待,释放掉list的锁
                    try {
                        list.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                //程序能够执行到此处说明仓库中有数据,进行消费
                Object o = list.remove(0);
                System.out.println(Thread.currentThread().getName()+"="+o);
                //唤醒生产者生产
                list.notifyAll();//唤醒全部也没关系,因为没有唤醒锁
            }
        }
    }
}
生产者线程=java.lang.Object@23f6a0e3
消费者线程=java.lang.Object@23f6a0e3
生产者线程=java.lang.Object@e660ca1
消费者线程=java.lang.Object@e660ca1
生产者线程=java.lang.Object@3b8ad26c
消费者线程=java.lang.Object@3b8ad26c

eg.2信号灯法

1)StuffBun包子类属性
包含包子的名称name
包子的大小type
2)生产者资源类 SetBun 产生包子
3)消费者资源类 GetBun 使用包子
4)ThreadDemo:main 用户线程

按照上面的方式:模拟生产者产生数据,消费者使用数据出现问题 null—null

生产资源类中和消费者资源类中所操作的包子对象不是同一个对象!

可以将包子通过生产资源类或者消费者资源类 通过构造方法传递

优化1:
加入while循环,模拟包子一直生产和一直消费!

​ 出现问题:数据紊乱:加入同步代码块给每一个资源类中都加入解决! 将多条语句对共享数据的操作包起来!

//包子类
public class StuffedBun {

     String type;
     String stuffing;

}
//吃包子
public class getBun implements Runnable {

    private StuffedBun gtb;

    public getBun(StuffedBun gtb) {
        this.gtb = gtb;
    }

    @Override
    public void run() {
        while (true) {
            synchronized (gtb) {
                System.out.println(gtb.type + gtb.stuffing);
            }
        }
    }
}
//生产包子
public class setBun implements Runnable {

    private StuffedBun stb;

    public setBun(StuffedBun stb) {
        this.stb = stb;
    }

    static int i = 1;

    @Override
    public void run() {
        while (true) {
            synchronized (stb) {
                if (i % 2 == 0) {
                    stb.stuffing = "肉馅";
                    stb.type = "大包子";
                } else  {
                    stb.stuffing = "韭菜馅";
                    stb.type = "小包子";
                }
                i++;
            }
        }
    }
}
public class Test {

    public static void main(String[] args) {

        //生成包子
        StuffedBun bun = new StuffedBun();

        //生成生产者
        setBun sb = new setBun(bun);
        //生成消费者
        getBun gb = new getBun(bun);

        Thread t1 = new Thread(sb);
        Thread t2 = new Thread(gb);

        t1.start();
        t2.start();

    }

}
……
小包子韭菜馅
小包子韭菜馅
小包子韭菜馅
大包子肉馅
大包子肉馅
……

eg.3信号灯法

问题:

消费者资源类所在的消费者线程中,每次输出一大片的内容:
线程的执行随机性----线程抢占CPU的执行权,一点点时间片执行很多次

优化2:
想出现依次打印
肉包子—大包子
菜包子—小包子

wait()+notify()—>实现同步机制(并且同时信号法:将死锁问题解决!)

public class Bun {

    String name;
    String type;
    boolean flag = false;//没包子

}
public class getBun implements Runnable {

    private Bun gb;

    public getBun(Bun gb) {
        this.gb = gb;
    }

    @Override
    public void run() {
        while (true) {
            synchronized (gb) {
                if (!gb.flag) {//没包子
                    try {
                        gb.wait();//锁住,不能吃包子
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                } else {//有包子
                    System.out.println(Thread.currentThread().getName() + ":吃了一个包子" + gb.name + "," + gb.type);
                    System.out.println("没包子了!!!");
                    gb.flag = false;
                    gb.notify();//等待,开始做包子
                }
            }
        }
    }
}
public class setBun implements Runnable {

    private Bun sb;

    public setBun(Bun sb) {
        this.sb = sb;
    }


    @Override
    public void run() {
        int i = 0;

        while (true) {
            synchronized (sb) {
                if (sb.flag) {//有包子
                    try {
                        sb.wait();//等待,不能做包子
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

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

                //没包子
                System.out.println("开始做包子!!!");
                if (i % 2 == 0) {
                    sb.name = "菜包子";
                    sb.type = "大包子";
                } else {
                    sb.name = "肉包子";
                    sb.type = "小笼包";
                }
                i++;

                sb.flag = true;//有包子了
                System.out.println(Thread.currentThread().getName() + ":产出一个包子" + sb.name + "," + sb.type);
                sb.notify();//释放,让吃包子
            }
        }
    }
}
public class Test {
    public static void main(String[] args) {

        Bun bun = new Bun();

        setBun setBun = new setBun(bun);
        getBun getBun = new getBun(bun);

        Thread t1 = new Thread(setBun);
        Thread t2 = new Thread(getBun);

        t1.setName("生产包子");
        t2.setName("吃包子");

        t1.start();
        t2.start();

    }
}
开始做包子!!!
生产包子:产出一个包子菜包子,大包子
吃包子:吃了一个包子菜包子,大包子
没包子了!!!
开始做包子!!!
生产包子:产出一个包子肉包子,小笼包
吃包子:吃了一个包子肉包子,小笼包
没包子了!!!

eg.4管程法

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

        Synchronizer s1 = new Synchronizer();

        new Thread(new Productor(s1)).start();
        new Thread(new Consumer(s1)).start();

    }
}


//生产者
class Productor implements Runnable{

    Synchronizer container;

    public Productor(Synchronizer container) {
        this.container = container;
    }

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("生产了第"+i+"只鸡");
            container.setChickens(new Chicken(i));
        }
    }
}

//消费者
class Consumer implements Runnable{

    Synchronizer container;

    public Consumer(Synchronizer container) {
        this.container = container;
    }

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("吃了第"+container.getChickens().id+"只鸡");
        }
    }
}

class Chicken{

    int id;

    public Chicken() {
    }

    public Chicken(int id) {
        this.id = id;
    }
}

//缓冲区
class Synchronizer{

    Chicken[] chickens = new Chicken[10];

    int i = 0;

    //生产
    public synchronized void setChickens(Chicken chicken){

        //如果容器满了,通知消费者消费,生产者等待
        if(i==chickens.length){
            System.out.println("容器已满,生产者等待,消费者请消费");
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        //如果容器没满,生产者生产
        System.out.println("容器未满,放入容器");
        chickens[i] = chicken;
        i++;

        //消费者开始消费
        notify();

    }

    //消费者
    public synchronized Chicken getChickens(){

        //容器空了,消费者等待,生产者生产
        if(i==0){
            System.out.println("容器已空,消费者等待,生产者请生产");
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        //如果可以消费
        System.out.println("开始消费");
        i--;
        Chicken chicken = chickens[i];

        //没鸡了,通知生产着生产
        notify();
        return chicken;

    }

}
容器未满,放入容器
生产了第1只鸡
容器未满,放入容器
生产了第2只鸡
容器未满,放入容器
生产了第3只鸡
容器未满,放入容器
生产了第4只鸡
容器未满,放入容器
生产了第5只鸡
容器未满,放入容器
生产了第6只鸡
容器未满,放入容器
生产了第7只鸡
容器未满,放入容器
生产了第8只鸡
容器未满,放入容器
生产了第9只鸡
容器未满,放入容器
生产了第10只鸡
容器已满,生产者等待,消费者请消费
开始消费
吃了第9只鸡
……

lock锁

synchronize和lock的对比

Lock是显式锁(手动开启和关闭锁,别忘记关闭锁) synchronized是隐式锁, 出了
作用域自动释放

Lock只有代码块锁,synchronized有代码块锁和方法锁

使用Lock锁,JVM将花费较少的时间来调度线程,性能更好。并且具有更好的扩展
性(提供更多的子类)

优先使用顺序:

Lock >同步代码块(已经进入了方法体,分配了相应资源) >同步方法(在方法体之外)

格式:

class A{
	private final ReentrantLock lock = new ReenTrantL ock();
	public void m(){
	lock.lock();
		try{
			//保证线程安全的代码;
			}
		
		finally{
			lock.unlock);
			//如果同步代码有异常,要将unlock()写入inally语句块
		}
	}
}

eg.实现电影卖票:三个窗口出售100张票 (线程的创建方式2)

public class LockDemo {


    public static void main(String[] args) {
            //创建共享资源类对象
        SellTicket st = new SellTicket() ;
        //创建三个线程类对象
        Thread t1 = new Thread(st,"窗口1") ;
        Thread t2 = new Thread(st,"窗口2") ;
        Thread t3 = new Thread(st,"窗口3") ;

        //启动线程
        t1.start();
        t2.start();
        t3.start();
    }
}

public class SellTicket implements  Runnable {

    //定义100张票
    private static int tickets = 100 ;

    //创建一个锁对象
    Lock lock = new ReentrantLock() ;

    @Override
    public void run() {
        //模拟一只有票
        while(true){

            //try...catch...finaly:捕获异常
            //使用try..finally
            try{
                 //通过锁对象--->获取锁
                lock.lock();
                //判断
                if(tickets>0){

                    //睡眠100毫秒
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                    System.out.println(Thread.currentThread().getName()+"正在出售第"+(tickets--)+"张票");
                }else{
                    break ;
                }
            }finally {
                //释放锁
                lock.unlock();
            }
        }
    }
}

窗口2正在出售第100张票
窗口2正在出售第99张票
窗口2正在出售第98张票
窗口2正在出售第97张票
窗口2正在出售第96张票

……

线程组

概念

将线程可以都添加一组中,方便管理,线程启动完毕之后,线程终止之后,不会将这个线程对象在内存中重复利用

线程组代表一组线程;还可以包括其他线程组

Threadpublic final ThreadGroup getThreadGroup() {//获取线程组
	return group;
}

//给线程设置默认的线程组名称
//public Thread(ThreadGroup group, Runnable target) {

ThreadGroup:
public final String getName() {//获取默认的线程组名称
  	return name;
}

构造方法;

public ThreadGroup(String name) {}

任务队列--------一个任务称为"一个线程" , Quene

public class ThreadGroupDemo {
    public static void main(String[] args) { //jvm调用main方法

      //  method1();
        method2() ;
    }

    //设置一个新的线程组名称
    private static void method2() {
        //创建一个线程组对象--同时设置线程组名称
        ThreadGroup tg = new ThreadGroup("myMain") ;
        //创建两条线程对象
        MyThread my = new MyThread() ;
        Thread t1 = new Thread(tg,my) ;
        Thread t2 = new Thread(tg,my) ;
        //获取线程组对象并同时线程组名称
        String name1 = t1.getThreadGroup().getName();
        String name2 = t2.getThreadGroup().getName();
        System.out.println(name1+"---"+name2);
    }

    private static void method1() {

        //创建两个线程
        MyThread my  = new MyThread() ;
        Thread t1 = new Thread(my) ;
        Thread t2 = new Thread(my) ;
        ThreadGroup tg1 = t1.getThreadGroup();
        ThreadGroup tg2 = t2.getThreadGroup();
        String name1 = tg1.getName();
        String name2 = tg2.getName();
        System.out.println(name1+"---"+name2);  //默认线程组名称都是main


    }
}

public class MyThread implements Runnable {
    @Override
    public void run() {
        for(int x = 0 ;x < 100 ;  x ++){
            System.out.println(Thread.currentThread().getName()+":"+x);
        }
    }
}
myMain---myMain

线程池

概念

JDK5.0起提供了线程池相关API: ExecutorService和Executors

ExecutorService: 真正的线程池接口。

常见子类

ThreadPoolExecutor

​ void execute(Runnable command) :执行任务/命令,没有返回值,一般用来执行Runnable
​ 异步计算的结果,如果不做计算,无须返回结果!

​ Future submit(Callable task):执行任务,有返回值,一般又来执行Callable
​ 提交值返回任务以执行,并返回代表任务待处理结果的Future。

​ void shutdown() :关闭连接池

Executors:

工具类、线程池的工厂类,用于创建并返回不同类型的线程池
通过Executors工具类,创建一个固定的可重用的线程数,返回值就线程池对象
public static ExecutorService newFixedThreadPool(int nThreads)

特点

在内存中创建一个固定可重用的线程数,当前线程执行完毕终止了,不会被回收掉,再次回到线程池中,等待下一次利用!

弊端:

维护成本大

eg.1

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

        //通过工厂类创建线程池对象
        ExecutorService threadPool = Executors.newFixedThreadPool(2);

        //提交异步方法
        //MyRunnable:打印x的值0-99之间的数据,不需要返回结果
      	//threadPool.submit(new MyRunnable()) ;
      	//threadPool.submit(new MyRunnable()) ;
        //<T> Future<T> submit(Callable<T> task)
        
        //Callable:提交异步计算---需要重写Callable的call来计算结果;如果没有结果,直接在call无须返回
        threadPool.submit(new MyCallable()) ;
        threadPool.submit(new MyCallable()) ;

        //void shutdown()关闭线程池
        threadPool.shutdown();

    }
}
public class MyCallable implements Callable {
    @Override
    public Object call() throws Exception {
        for(int x = 0 ; x < 100 ; x++){
            System.out.println(Thread.currentThread().getName()+":"+x);
        }

        return null;
    }
}
public class MyRunnable implements  Runnable {
    @Override
    public void run() {
        for(int x = 0 ; x < 100 ; x ++){
            System.out.println(Thread.currentThread().getName()+":"+x);
        }
    }
}
pool-1-thread-1:0
pool-1-thread-1:1
pool-1-thread-1:2
pool-1-thread-1:3
pool-1-thread-2:0
pool-1-thread-1:4
pool-1-thread-1:5

……

pool-1-thread-2:97
pool-1-thread-2:98
pool-1-thread-2:99

eg.2

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

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

        ExecutorService service = Executors.newFixedThreadPool(10);

        service.execute(new MyThread());
        service.execute(new MyThread());
        service.execute(new MyThread());
        service.execute(new MyThread());

        service.shutdown();

    }
}

class MyThread implements  Runnable{

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName());
    }
}
pool-1-thread-1
pool-1-thread-2
pool-1-thread-3
pool-1-thread-4

简单工厂

设计模式是一种思想,前人总结出来,不是技术!!

设计原则

开闭原则

对现有代码修改关闭,对扩展代码开放

举例:项目开发完毕,进行更新,不能够修改现有代码,在现有代码的基础上提供扩展!

接口分离原则

一个接口中定义一个功能,接口和接口之间独立的,不能相互影响

实际开发中:某个接口中,可能将相关的功能都定义在这一个接口中

按模块划分

用户模块 UserDao 接口
login()/register()/logout()/checkUserName()

商品模块 ProductDao 接口
Product findById(Integer id) ;
List findAll() ;
void update(Product product);

订单模块 OrderDao 接口
List findPage(int pageSize,int currentPage);

里氏替换原则
任何父类出现的地方都可以子类替代!
class Father{
	public void show(){
            // ...
    	Class<Son> clazz  = Son.class ;
           //反射方式---->字节码文件对象就调用method----->将所有的成员方法---->Method
	}
}
class Son extends Father{
	public void method(){

    }
}
“低耦合,高内聚”

23种设计模式都需要遵循 原则:“低耦合,高内聚”

类型

创建型:

对象的创建 (使用最多)

结构型:

整个结构的组成:

代理模式
		静态代理
        	代理角色
            真实角色
行为型:

具备功能性的

创建型设计模式:

简单工厂(静态工厂方法模式)

优点:利用多态创建子类对象,能够灵活去创建对象(提供的静态功能)

弊端:代码量大,一旦有新的类型增加,工厂类的静态功能就需要改动…

eg.1

public class Animal {
    public void eat(){
        System.out.println("吃");
    }
    public void sleep(){
        System.out.println("睡");
    }
}

public class Cat extends Animal {
    @Override
    public void eat() {
        System.out.println("猫吃鱼...");
    }

    @Override
    public void sleep() {
        System.out.println("猫趴着睡觉");
    }
}

public class Dog extends Animal {
    @Override
    public void eat() {
        System.out.println("狗吃骨头");

    }

    @Override
    public void sleep() {
        System.out.println("狗躺着睡");
    }
}

public class Pig extends Animal {
    @Override
    public void eat() {
        System.out.println("猪吃白菜");
    }

    @Override
    public void sleep() {
        System.out.println("猪侧着睡...");

    }
}

public class AnimalFactory {
    //多态方式
    public static Animal createAnimal(String type){
        if("cat".equals(type)){
            return new Cat() ;
        }else if("dog" .equals(type)){
            return new Dog() ;
        }else if("pig".equals(type)){
            return new Pig() ;
        }
        return null ;
    }
}

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

        //没有提供任何式设计模式
        //创建具体类对象
        //使用多态
        //Cat c = new Cat() ;
        Animal a = new Cat() ;
        a.eat();
        a.sleep();
        a = new Dog() ;
        a.eat();
        a.sleep();
        System.out.println("----------------------------------------------");

        //利用多态完成
        Animal animal = AnimalFactory.createAnimal("cat");
        animal.eat();
        animal.sleep();
        animal = AnimalFactory.createAnimal("dog");
        animal.eat();
        animal.sleep();
        animal = AnimalFactory.createAnimal("pig");
        animal.eat();
        animal.sleep();
        animal = AnimalFactory.createAnimal("monkey");
        if(animal!=null){
            animal.eat();
            animal.sleep();
        }else{
            System.out.println("没有提供该动物的实例创建");
        }

    }
}
猫吃鱼...
猫趴着睡觉
狗吃骨头
狗躺着睡
----------------------------------------------
猫吃鱼...
猫趴着睡觉
狗吃骨头
狗躺着睡
猪吃白菜
猪侧着睡...
没有提供该动物的实例创建
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值