java多线程学习笔记

普通方法和多线程简单图解

普通方法调用和多线程
多线程的三个特性:原子性、可见性、有序性
原子性:是指一个操作是不可中断的。即使是多个线程一起执行的时候,一个操作一旦开始,就不会被其他线程干扰。

比如,对于一个静态全局变量int i,两个线程同时对它赋值,线程A给他赋值为1,线程B给他赋值为-1。那么不管这两个线程

以何种方式。何种步调工作,i的值要么是1,要么是-1.线程A和线程B之间是没有干扰的。这就是原子性的一个特点,不可被中断。

可见性:是指当一个线程修改了某一个共享变量的值,其他线程是否能够立即知道这个修改。显然,对于串行来说,可见性问题是不存在的。

有序性:在并发时,程序的执行可能会出现乱序。给人的直观感觉就是:写在前面的代码,会在后面执行。有序性问题的原因是因为程序在

执行时,可能会进行指令重排,重排后的指令与原指令的顺序未必一致。

java多线程三种创建方式

继承Thread类(重点)

TestThread1(Thread简单实现)

1、继承Thread类
2、重写run方法
3、创建线程对象,调用start开启线程

public class TestThread1 extends Thread{
    @Override
    public void run() {
        //run方法线程体
        for (int i = 0; i < 200; i++) {
            System.out.println("111111111");
        }
    }

    public static void main(String[] args) {
    	//创建线程对象,通过线程对象开启线程
        TestThread1 testThread1 = new TestThread1();
        testThread1.start();
        //main主线程
        for (int i = 0; i < 2000; i++) {
            System.out.println("22222222");
        }
    }
}
//运行结果
......
111111111
111111111
111111111
111111111
111111111
111111111
111111111
111111111
22222222
22222222
22222222
......
22222222
22222222
22222222
111111111
111111111
111111111
111111111
111111111
......

线程不一定立即执行,由cpu调度安排

TestThread2(Thread下载图片)

		<dependency>
          	<groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.6</version>
        </dependency>
//练习Thread,实现多线程同步下载图片
public class TestThread2 extends Thread{
    private String url;
    private String name;

    public TestThread2(String url, String name) {
        this.url = url;
        this.name = name;
    }
    @Override
    public void run() {
        WebDownloader webDownloader = new WebDownloader();
        webDownloader.downloader(url,name);
        System.out.println("文件名为:"+name);
    }

    public static void main(String[] args) {
        TestThread2 t1 = new TestThread2("https://www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png","1.jpg");
        TestThread2 t2 = new TestThread2("https://www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png","2.jpg");
        TestThread2 t3 = new TestThread2("https://www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png","3.jpg");
        t1.start();
        t2.start();
        t3.start();
    }
}

//下载器
class WebDownloader{
    //下载方法
    public void downloader(String url,String name){
        try {
            FileUtils.copyURLToFile(new URL(url),new File(name));
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("io异常,downloader方法出现问题");
        }
    }
}
//运行结果
文件名为:2.jpg
文件名为:3.jpg
文件名为:1.jpg

结果

实现Runnable接口(重点)

1、定义MyRunnable类实现Runnable接口
2、实现run方法
3、创建线程对象,调用start开启线程

TestThread3(Runnable简单实现)

public class TestThread3 implements Runnable{
    @Override
    public void run() {
        //run方法线程体
        for (int i = 0; i < 200; i++) {
            System.out.println("111111111");
        }
    }

    public static void main(String[] args) {
        //创建Runnable接口的实现类对象
        TestThread3 testThread3 = new TestThread3();
        //创建线程对象,通过线程对象开启线程,实现代理
        Thread thread = new Thread(testThread3);
        thread.start();
        //main主线程
        for (int i = 0; i < 2000; i++) {
            System.out.println("22222222");
        }
    }
}

小结

两种方式对比

TestThread4(火车票并发问题)

//多个线程同时操作同一个对象

//发现问题:多个线程操作同一个资源情况下线程不安全,数据紊乱
public class TestThread4 implements Runnable{
    private int ticket=10;
    @Override
    public void run() {
        while (true){
            if(ticket<=0) {
                break;
            }
            System.out.println(Thread.currentThread().getName()+"拿到了第"+ticket--+"张票");
            //模拟每个线程做完一次之后的延时
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        TestThread4 testThread4 = new TestThread4();
        new Thread(testThread4,"aa").start();
        new Thread(testThread4,"bb").start();
        new Thread(testThread4,"cc").start();
    }
}
//运行结果
aa拿到了第10张票
bb拿到了第9张票
cc拿到了第8张票
bb拿到了第7张票
aa拿到了第6张票
cc拿到了第5张票
bb拿到了第4张票
aa拿到了第3张票
cc拿到了第2张票
bb拿到了第0张票
aa拿到了第1张票

Race(多线程模拟龟兔赛跑)

//多线程模拟龟兔赛跑
public class Race implements Runnable{
    private static String winner;
    @Override
    public void run() {
        for (int i = 0; i <= 100; i++) {
            //模拟兔子睡觉
            if (i>0&&i%10==0&&Thread.currentThread().getName()=="兔子"){
                System.out.println("兔子睡了一觉");
                try {
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }
            //如果有胜者,则终止所有比赛
            if (winner!=null){
                break;
            }
            System.out.println(Thread.currentThread().getName()+"跑了第"+i+"步");
            //判断是否有胜者可以结束比赛
            if (gameOver(i)){
                break;
            }
        }
    }
    //判断是否完成比赛
    private Boolean gameOver(int i){
        if (i>=100){
            winner=Thread.currentThread().getName();
            System.out.println("胜者是"+winner);
            return true;
        }
        return false;
    }

    public static void main(String[] args) {
        Race race = new Race();
        new Thread(race,"兔子").start();
        new Thread(race,"乌龟").start();
    }
}
//运行结果
乌龟跑了第39步
兔子跑了第8步
兔子跑了第9步
兔子睡了一觉
乌龟跑了第40步
乌龟跑了第41......
乌龟跑了第97步
乌龟跑了第98步
乌龟跑了第99步
乌龟跑了第100步
胜者是乌龟

实现Callable接口(扩展)

1、创建执行服务
2、提交执行
3、获取结果
4、关闭服务
优点:可以返回值,可以跑出异常
在这里插入图片描述

TestCallable(Callable下载图片)

//实现Callable接口
public class TestCallable implements Callable<Boolean> {
    private String url;
    private String name;
    public TestCallable(String url, String name) {
        this.url = url;
        this.name = name;
    }

    @Override
    public Boolean call() throws Exception {
        WebDownloader webDownloader = new WebDownloader();
        webDownloader.downloader(url,name);
        System.out.println("文件名为:"+name);
        return true;
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        TestCallable t1 = new TestCallable("https://www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png","1.jpg");
        TestCallable t2 = new TestCallable("https://www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png","2.jpg");
        TestCallable t3 = new TestCallable("https://www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png","3.jpg");
        //创建执行服务
        ExecutorService executorService =Executors.newFixedThreadPool(3);
        //提交执行
        Future<Boolean> r1=executorService.submit(t1);
        Future<Boolean> r2=executorService.submit(t2);
        Future<Boolean> r3=executorService.submit(t3);
        //获取结果
        Boolean rs1=r1.get();
        Boolean rs2=r2.get();
        Boolean rs3=r3.get();
        //关闭服务
        executorService.shutdownNow();
    }
}
//下载器
class WebDownloader{
    //下载方法
    public void downloader(String url,String name){
        try {
            FileUtils.copyURLToFile(new URL(url),new File(name));
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("io异常,downloader方法出现问题");
        }
    }
}

Lambda表达式

在这里插入图片描述
在这里插入图片描述

TestLambda1(Lambda表达式与其他方式对比)

public class TestLambda1 {

    //3、静态内部类
    static class Like2 implements ILike{
        @Override
        public void Lambda() {
            System.out.println("i like Lambda2");
        }
    }

    public static void main(String[] args) {
        Like like = new Like();
        like.Lambda();
        Like2 like2 = new Like2();
        like2.Lambda();
        //4、局部内部类
        class Like3 implements ILike{
            @Override
            public void Lambda() {
                System.out.println("i like Lambda3");
            }
        }
        Like3 like3 = new Like3();
        like3.Lambda();
        //5、匿名内部类,没有类名,必须借助接口或父类
        ILike like4 = new ILike() {
            @Override
            public void Lambda() {
                System.out.println("i like Lambda4");
            }
        };
        like4.Lambda();
        ILike like5 = () -> {
            System.out.println("i like Lambda4");
        };
    }
}

//1、定义接口
interface ILike{
	//接口中只包含一个抽象方法,称为函数式接口,只有函数式接口可以使用Lambda表达式
    void Lambda();
}

//2、实现类
class Like implements ILike{
    @Override
    public void Lambda() {
        System.out.println("i like Lambda");
    }
}
//运行结果
i like Lambda
i like Lambda2
i like Lambda3
i like Lambda4

TestLambda2(Lambda表达式各种简化)

public class TestLambda2 {
    public static void main(String[] args) {
        //局部内部类
        class Love1 implements ILove{
            @Override
            public void Lambda(int a) {
                System.out.println("I love Lambda"+a);
            }
        }
        ILove love=new Love1();
        love.Lambda(1);
        //匿名内部类
        ILove love2=new ILove() {
            @Override
            public void Lambda(int a) {
                System.out.println("I love Lambda"+a);
            }
        };
        love2.Lambda(2);


        //Lambda表达式
        ILove love3=(int a)->{
            System.out.println("I love Lambda"+a);
        };
        love3.Lambda(3);

        //Lambda表达式 简化括号和数据类型,多个参数不可省略括号
        ILove love4=a->{
            System.out.println("I love Lambda"+a);
        };
        love4.Lambda(4);

        //Lambda表达式 简化花括号,多个语句不可省略花括号
        ILove love5=a-> System.out.println("I love Lambda"+a);
        love5.Lambda(5);
        
        //使用Lambda表达式创建Runnable对象
        Runnable runnable=()->{
            System.out.println("runnable");
        };
        new Thread(runnable).start();
    }
    
    
}

interface ILove{
    void Lambda(int a);
}
```java
//运行结果
I love Lambda1
I love Lambda2
I love Lambda3
I love Lambda4
I love Lambda5
runnable

静态代理

1、目标对象和代理对象都要实现同一个接口
2、代理对象代理目标对象
优点:
1、代理对象可以做目标对象无法做的事
2、目标对象可以专注自己做的事

StaticProxy(对比Thread对象实现Runnable对象)

//静态代理
public class StaticProxy {
    public static void main(String[] args) { 
        //静态代理是Thread对象实现代理Runnable对象的原理
        new Thread(()-> System.out.println("Thread对象代理Runnable对象")).start();
        
        new WeddingCompany(new You()).HappyMarry();
    }
}

interface Marry{
    void HappyMarry();
}
//目标对象
class You implements Marry{

    @Override
    public void HappyMarry() {
        System.out.println("结婚");
    }
}
//代理对象
class WeddingCompany implements Marry{
    //代理->目标对象
    private Marry target;

    public WeddingCompany(Marry target) {
        this.target = target;
    }

    @Override
    public void HappyMarry() {
        System.out.println("布置");
        this.target.HappyMarry();
        System.out.println("结束");
    }
}
//运行结果
布置
结婚
结束

线程状态

在这里插入图片描述

线程停止(Stop)

在这里插入图片描述

1、建议线程正常停止----利用次数,不建议死循环
2、建议使用标志位----设置一个标志位
3、不要使用stop或者destroy等过时的jdk不建议使用的方法

TestStop(自定义线程停止方法)

public class TestStop implements Runnable{
    //1、设置一个标志位
    private Boolean flag=true;
    @Override
    public void run() {
        int i=0;
        while(flag) {
            System.out.println("run...Thread"+i++);
        }
        if (flag==false){
            System.out.println("Thread stop");
        }
    }
    //2、设置公开方法停止线程,改变标志位
    public void stop(){
        flag=false;
    }
    public static void main(String[] args) {
        TestStop testStop = new TestStop();
        new Thread(testStop).start();
        for (int i = 0; i < 1000; i++) {
            System.out.println("main"+i);
            if(i==900){
                //调用stop方法改变标志位,让线程停止
                testStop.stop();
            }
        }
    }
}
//运行结果
main897
main898
main899
main900
run...Thread650
main901
main902
main903
main904
main905
main906
main907
main908
main909
main910
main911
Thread stop
main912
main913
main914
main915

线程休眠(Sleep)

在这里插入图片描述

TestSleep2(模拟倒计时)

//模拟倒计时
public class TestSleep2 {
    private static int time=10;
    static public void tenDown() throws InterruptedException {
        while(true){
            //获取系统当前时间
            Date date = new Date(System.currentTimeMillis());
            System.out.println(new SimpleDateFormat("HH:mm:ss").format(date));
            //输出后延时一秒
            Thread.sleep(1000);
            //更新当前时间
            date = new Date(System.currentTimeMillis());
            if (time--<=0){
                break;
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        tenDown();
    }
}
//运行结果
19:11:57
19:11:58
19:11:59
19:12:00
19:12:01
19:12:02

线程礼让(Yield)

在这里插入图片描述

TestYield(测试礼让)

//测试礼让
public class TestYield {
    public static void main(String[] args) {
        new Thread(new Yield(),"a").start();
        new Thread(new Yield(),"b").start();
    }
}

class Yield implements Runnable{

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"线程开始执行");
        Thread.yield();
        System.out.println(Thread.currentThread().getName()+"线程停止执行");
    }
}

线程强制执行(Join)

在这里插入图片描述

TestJoin(测试线程强制执行)

public class TestJoin implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("vip线程");
        }
    }

    public static void main(String[] args) throws InterruptedException {
        TestJoin testJoin = new TestJoin();
        Thread thread = new Thread(testJoin);
        thread.start();
        for (int i = 0; i < 1000; i++) {
            System.out.println("main线程"+i);
            if (i==200){
                //主线程的代码块中,如果碰到了thread.join()方法,此时主线程需要等待(阻塞),等待子线程结束了,才能继续执行t.join()之后的代码块。
                thread.join();
            }
        }
    }
}
//运行结果
198
199
200
vip
vip
vip
vip
......
vip
vip
201
202
203
204
205

观察线程状态

在这里插入图片描述

TestState(观察测试线程状态)

//观察测试线程状态
public class TestState{
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(()->{
            for (int i = 0; i < 5; i++) {
                try {
                    //当前子线程休眠
                    Thread.sleep(1000);
                    //线程休眠后输出线程状态:Runnable
                    System.out.println(Thread.currentThread().getState());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("线程结束");
        });
        System.out.println(thread.getState());

        thread.start();
        System.out.println(thread.getState());

        while (thread.getState()!=Thread.State.TERMINATED){
            //当前主线程休眠
            Thread.sleep(500);
            //主线程每500毫秒输出一次子线程的状态
            System.out.println(thread.getState());
        }
    }
}
//运行结果
NEW
RUNNABLE
TIMED_WAITING
RUNNABLE
TIMED_WAITING
TIMED_WAITING
RUNNABLE
TIMED_WAITING
TIMED_WAITING
RUNNABLE
TIMED_WAITING
TIMED_WAITING
RUNNABLE
TIMED_WAITING
TIMED_WAITING
RUNNABLE
线程结束
TERMINATED

线程优先级

在这里插入图片描述

TestPriority(测试线程优先级)

//测试线程优先级
public class TestPriority {
    public static void main(String[] args) {
        //获取当前主线程的优先级
        Thread.currentThread().getPriority();
        Thread t1 = new Thread(() -> {
            //输出当前子线程的优先级
            System.out.println("线程优先级为"+Thread.currentThread().getPriority());
        });
        Thread t2 = new Thread(() -> {
            System.out.println("线程优先级为"+Thread.currentThread().getPriority());
        });
        Thread t3 = new Thread(() -> {
            System.out.println("线程优先级为"+Thread.currentThread().getPriority());
        });
        Thread t4 = new Thread(() -> {
            System.out.println("线程优先级为"+Thread.currentThread().getPriority());
        });
        //不能超过最大优先级10和最小优先级1
        t1.setPriority(7);
        t2.setPriority(9);
        t3.setPriority(2);
        t4.setPriority(10);
        t1.start();
        t2.start();
        t3.start();
        t4.start();
    }
}
//运行结果
线程优先级为7
线程优先级为9
线程优先级为10
线程优先级为2

在这里插入图片描述

守护线程

在这里插入图片描述

TestDaemon(测试守护线程)

public class TestDaemon {
    public static void main(String[] args) {
        Thread God = new Thread(new God());
        Thread You = new Thread(new You());
        God.setDaemon(true);//false为用户线程
        God.start();
        You.start();
    }
}
//守护线程
class God implements Runnable{
    @Override
    public void run() {
        while (true){
            System.out.println("上帝守护着你");
        }
    }
}
//用户线程
class You implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i <= 35600; i++) {
            System.out.println("活着");
            if(i==35600){
                System.out.println("goodbye world");
            }
        }
    }
}
//运行结果
活着
活着
活着
活着
上帝守护着你
上帝守护着你
上帝守护着你
goodbye world
上帝守护着你
上帝守护着你
上帝守护着你
上帝守护着你
上帝守护着你
上帝守护着你
上帝守护着你
上帝守护着你
上帝守护着你

线程同步

在这里插入图片描述

在这里插入图片描述

不安全的例子

UnsafeBuyTicket(买票的线程不安全)

//买票线程不安全
public class UnsafeBuyTicket {
    public static void main(String[] args) {
        BuyTicket buyTicket = new BuyTicket();
        new Thread(buyTicket,"aaa").start();
        new Thread(buyTicket,"bbb").start();
        new Thread(buyTicket,"ccc").start();
    }
}

class BuyTicket implements Runnable{
    private int ticket=10;
    Boolean flag=true;
    @Override
    public void run() {
        while (flag){
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"抢到了票"+ticket--);
            //判断是否有票
            if (ticket<=0){
                flag=false;
            }
        }
    }
}
//运行结果
aaa抢到了票10
ccc抢到了票9
bbb抢到了票8
ccc抢到了票7
bbb抢到了票5
aaa抢到了票6
ccc抢到了票3
aaa抢到了票4
bbb抢到了票2
aaa抢到了票1
ccc抢到了票0
bbb抢到了票-1

UnsafeList(集合的线程不安全)

//测试集合的线程不安全
public class UnsafeList {
    public static void main(String[] args) throws InterruptedException {
        List<String> list = new ArrayList<>();
        for (int i = 0; i < 1000000; i++) {
            new Thread(()->{
                list.add(Thread.currentThread().getName());
            }).start();
        }
        Thread.sleep(3000);
        System.out.println(list.size());
    }
}
//运行结果
//同时add可能会造成覆盖
999867

同步方法

在这里插入图片描述

SafeBuyTicket(买票的线程安全)

//线程安全的买票
public class UnsafeBuyTicket {
    public static void main(String[] args) {
        BuyTicket buyTicket = new BuyTicket();
        new Thread(buyTicket,"aaa").start();
        new Thread(buyTicket,"bbb").start();
        new Thread(buyTicket,"ccc").start();
    }
}

class BuyTicket implements Runnable{
    private int ticket=10;
    Boolean flag=true;
    @Override
    public void run() {
        while (flag){
            if (ticket<=0){
                flag=false;
                break;
            }
            //在开始方法前需要先获取锁
            buy();
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            
        }
    }
    //synchronized同步方法,锁this,即锁该类的实例对象,当前线程改变ticket值后才释放对象的锁,其他线程阻塞排队等待该线程执行完同步方法后才能拿到锁
    synchronized public void buy(){
        System.out.println(Thread.currentThread().getName()+"抢到了票"+ticket--);
    }
}
//运行结果
aaa抢到了票10
bbb抢到了票9
ccc抢到了票8
aaa抢到了票7
ccc抢到了票6
bbb抢到了票5
aaa抢到了票4
bbb抢到了票3
ccc抢到了票2
aaa抢到了票1

同步块

在这里插入图片描述
同步块和同步方法相同,同步块可以是任意对象,同步对象是当前类对象,相当于synchronized(this){},是对指定对象加锁和对this对象加锁的区别

SafeList(集合的线程安全)

//测试集合的线程安全
public class SafeList {
    public static void main(String[] args) throws InterruptedException {
        List<String> list = new ArrayList<>();
        for (int i = 0; i < 1000000; i++) {
            new Thread(()->{
                synchronized (list){
                    list.add(Thread.currentThread().getName());
                }
            }).start();
        }
        Thread.sleep(3000);
        System.out.println(list.size());
    }
}
//运行结果
1000000

TestJUC(JUC安全类型的集合)

//测试JUC安全类型的集合
public class TestJUC {
    public static void main(String[] args) {
        //是个安全的集合类型
        CopyOnWriteArrayList<String> copyOnWriteArrayList = new CopyOnWriteArrayList<>();
        for (int i = 0; i < 100000; i++) {
            new Thread(()->{
                copyOnWriteArrayList.add(Thread.currentThread().getName());
            }).start();
        }
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(copyOnWriteArrayList.size());
    }
}
//运行结果
1000000

死锁

DeadLock(死锁例子)

public class DeadLock {
    public static void main(String[] args) {
        new Thread(new Play(0,"aa")).start();
        new Thread(new Play(1,"bb")).start();
    }
}

class Headphone{

}
class Discman{

}

class Play implements Runnable{
    //需要的资源只有一份,用static来保证只有一份
    static Headphone headphone=new Headphone();
    static Discman discman=new Discman();

    int choice;//选择
    String name;//用户

    public Play(int choice, String name) {
        this.choice = choice;
        this.name = name;
    }

    @Override
    public void run() {
        try {
            play();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    private void play() throws InterruptedException {
        if (choice==0){
            synchronized (headphone){//获得耳机的锁
                System.out.println(name+"拿到了耳机");
                Thread.sleep(1000);
                synchronized (discman){//1秒后想获得前端的锁
                    System.out.println(name+"拿到了前端");
                }
            }
        }
        else{
            synchronized (discman){//获得前端的锁
                System.out.println(name+"拿到了前端");
                Thread.sleep(2000);
                synchronized (headphone){//2秒后想获得耳机的锁
                    System.out.println(name+"拿到了耳机");
                }
            }
        }
    }
}
aa拿到了耳机
bb拿到了前端
(死锁无法结束线程)

Lock锁

在这里插入图片描述
在这里插入图片描述

TestLock(测试Lock锁)

public class TestLock {
    public static void main(String[] args) {
        TestLock2 testLock2 = new TestLock2();
        new Thread(testLock2,"aa").start();
        new Thread(testLock2,"bb").start();
        new Thread(testLock2,"cc").start();
    }
}

class TestLock2 implements Runnable{
    int ticket=10;
    private final ReentrantLock reentrantLock=new ReentrantLock();
    @Override
    public void run() {
        while(true){
            //除了拿到锁的线程,其余锁在该语句前等待解锁拿资源
            reentrantLock.lock();
            try {
                if (ticket>0) {
                    System.out.println(Thread.currentThread().getName() + "---->" + ticket--);
                }else{
                    break;
                }
            }
            finally {
                reentrantLock.unlock();
            }
            //抢完票解锁后延迟200毫秒
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
//运行结果
aa---->10
bb---->9
cc---->8
aa---->7
bb---->6
cc---->5
bb---->4
aa---->3
cc---->2
bb---->1

线程协作

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
synchronizedwait/notify的区别:
synchronized可以阻止并发更新同一个共享资源,当同一个资源被一个线程获得锁时,其他线程需要等待该线程释放锁;

wait/notify可以对同一个对象进行逻辑处理,对对象进行手动的线程阻塞并释放锁 和 唤醒解除线程阻塞。
比如当生产者发现产品已满则需要wait等待消费者消费产品,当消费者消费产品后用notify释放锁继续生产产品,反之亦然。

1、有synchronized的地方不一定有wait,notify

2、有wait,notify的地方必有synchronized.这是因为wait和notify不是属于线程类,而是每一个对象都具有的方法,而且,这两个方法都和对象锁有关,有锁的地方,必有synchronized。

3、调用wait()方法前的判断最好用while,而不用if;while可以实现被wakeup后thread再次作条件判断;而if则只能判断一次。

4、调用这一对方法的对象上的锁必须为当前线程所拥有,这样才有锁可以释放。因此,这一对方法调用必须放置在这样的synchronized 方法或块中,该方法或块的上锁对象就是调用这一对方法的对象。若不满足这一条件,则程序虽然仍能编译,但在运行时会出现IllegalMonitorStateException 异常。

5、调用 notify() 方法导致解除阻塞的线程是从因调用该对象的 wait() 方法而阻塞的线程中随机选取的,我们无法预料哪一个线程将会被选择,所以编程时要特别小心,避免因这种不确定性而产生问题。

6、除了 notify(),还有一个方法 notifyAll() 也可起到类似作用,唯一的区别在于,调用 notifyAll() 方法将把因调用该对象的wait() 方法而阻塞的所有线程一次性全部解除阻塞。当然,只有获得锁的那一个线程才能进入可执行状态。

管程法

在这里插入图片描述

TestPC(生产者与消费者)

//生产者消费者模型-->利用缓冲区解决:管程法
//生产者,消费者,产品,缓冲区
public class TestPC {
    public static void main(String[] args) {
        SynContainer synContainer = new SynContainer();
        Producer producer = new Producer(synContainer);
        Customer customer = new Customer(synContainer);
        new Thread(producer, "a1").start();
        new Thread(producer, "a2").start();
        new Thread(customer, "b1").start();
        new Thread(customer,"b2").start();
    }
}

class Chicken {
    int id;

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

class Producer implements Runnable {
    private int i = 0;
    SynContainer synContainer;

    public Producer(SynContainer synContainer) {
        this.synContainer = synContainer;
    }

    public void run() {
        while (i <= 200) {
            //在调用该方法前需要获得该对象的锁
            push();
        }
    }
    //如果需要对run方法里语句进行加锁,可以把语句中需要加锁的语句定义包装成方法调用
    private synchronized void push(){
        if(i<=200) {
            synContainer.push(new Chicken(i));
            System.out.println(Thread.currentThread().getName() + "生产了" + (i));
            i++;
        }
    }
}

class Customer implements Runnable {
    SynContainer synContainer;

    public Customer(SynContainer synContainer) {
        this.synContainer = synContainer;
    }

    public void run() {
        for (int i = 0; i < 100; i++) {
            pop();
        }
    }
    private synchronized void pop(){
        System.out.println(Thread.currentThread().getName() + "消费了" + synContainer.pop().id);
    }
}


class SynContainer {
    //容器大小为10,最多可以放10个chicken对象
    Chicken[] chickens = new Chicken[10];
    //当前容器计数器
    int count = 0;
	//需要synchronized对对象锁上后才能调用wait/notify方法进行阻塞释放锁或者唤醒解除阻塞
    public synchronized void push(Chicken chicken) {
        //用while不要用if,因为当多个线程同时进入等待的时候,notifyAll唤醒对象上所有wait方法的线程,
        // 而如果用的if方法,进入的判断后wait被唤醒后就不会再一次进行判断,有可能会造成不满足条件的线程
        // 没有进行判断wait直接执行方法,而while会在线程被唤醒后再次判断,如果被其他线程方法抢先执行了,
        // 则其他被唤醒的线程会继续被判断wait等待
        while (count == chickens.length) {
            //等待消费者消费
            System.out.println(Thread.currentThread().getName()+"产品满了");
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }
        chickens[count] = chicken;
        count++;
        this.notifyAll();
    }

    public synchronized Chicken pop() {
        while(count<=0){
            //等待生产者生产
            try {
                //表示线程一直等待直到通知,与sleep的区别是会释放锁
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        Chicken chicken = chickens[count - 1];
        count--;
        //唤醒同一个对象上所有调用wait方法的线程
        this.notifyAll();
        //随机唤醒一个等待状态的线程
        //this.notify();
        return chicken;
    }
}
//运行结果
a1生产了0
a1生产了1
a1生产了2
a1生产了3
a1生产了4
a1生产了5
a1生产了6
a1生产了7
a1生产了8
a1生产了9
a1产品满了
b1消费了9
......
a1生产了197
b1消费了190
a1生产了198
b1消费了198
a1生产了199
b1消费了199
a1生产了200
b1消费了200

信号灯法

在这里插入图片描述
可以理解为缓冲区为1的管程法,生产者生产1然后等待,消费者消费1然后等待,来回重复,只是用flag的true和false标志位代替了缓冲区。

线程池

在这里插入图片描述
在这里插入图片描述

TestPool

//测试线程池
public class TestPool {
    public static void main(String[] args) {
        //1、创建服务,创建线程池
        //newFixedThreadPool 参数是线程池大小
        ExecutorService service= Executors.newFixedThreadPool(10);
        //执行
        service.execute(new MyThread());
        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-3
pool-1-thread-2
pool-1-thread-5
pool-1-thread-4
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值