3

JavaSE-09 Thread 多线程(完整版)

 2 years ago
source link: https://blog.51cto.com/u_15316078/5342134
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

JavaSE-09 Thread 多线程

1. 线程简介

1.1 普通方法调用和多线程

JavaSE-09 Thread 多线程(完整版)_java

1.2 程序、进程、线程

  • 程序跑起来编程进程,进程里面分为若干个线程 :例如main函数就是主线程(是系统入口,用于执行多个程序),gc垃圾回收机制也是一个线程
  • 多线程是模拟出来的,真正的多线程是指很多cpu,即多核,但是因为cpu执行代码切换的很快,所以有同时执行的感觉
  • 多个线程是由调度器安排调度与操作系统相关的,控制到cpu先后顺序
  • 对同一个资源操作时,会发生资源抢夺的问题,需要加入并发控制

2. 线程创建:com.fenfen.Thread.Demo1

2.1继承Thread类

三步走:
1.自定义线程类继承Thread类
2.重写run()方法,编写线程执行力
3.创建线程对象,调用start()方法启动线程
        public class TestTread1 extends Thread{

            @Override
            public void run() {
                //run 方法线程体
                for (int i = 0; i < 20; i++) {
                    System.out.println("我在通宵肝代码---"+i);

                }
            }
            public static void main(String[] args) {
                //main方法,主线程

                //创建一个线程对象,并调用start方法
                TestTread1 testTread1 = new TestTread1();
                testTread1.start();
                //如果是run方法就是正常的先跑run方法上面的
                testTread1.run();


                for (int i = 0; i < 200; i++) {
                    System.out.println("我在学习多线程---"+i);

                }
                /*
                输出结果是交替执行的,由cpu调度执行
                 */
            }

2.2 用继承thread实现网图下载的多线程

学完io流后记得补代码

2.3 实现runnable接口

三步走:
1.定义MyRunnable类实现Runnable接口
2.实现run()方法,编写线程执行体
3.创建线程对象,传入目标对象+调用start()方法启动线程
        public class TestThread3 implements Runnable{
            @Override
            public void run() {
                //run 方法线程体
                for (int i = 0; i < 20; i++) {
                    System.out.println("我在通宵肝代码---"+i);

                }
            }
            public static void main(String[] args) {
                //main方法,主线程

                //创建Runnable接口实现类对象,并调用start方法
                TestThread3 testTread3 = new TestThread3();

                //创建线程对象,通过线程对象来开启我们的线程,代理
                Thread thread = new Thread(testTread3);
                thread.start();
                //new Thread(testTread3).start();或者直接一句这个


                for (int i = 0; i < 200; i++) {
                    System.out.println("我在学习多线程---"+i);

                }
                /*
                1、去看源码发现,本质是因为:Thread也实现了Runnable接口,Runnable就一个run方法在里面
                2、继承是单继承,推荐使用Runnable方法
                 */
            }
        }

2.4 初始并发问题

多个线程操作同一个资源的情况下,并发出现问题,线程不安全了,数据紊乱

        public class TestThread4 implements Runnable{

            //票数
            private int ticketnums = 10;


            @Override
            public void run() {
                while (true){
                    if (ticketnums<=0){
                        break;
                    }

                    //模拟延迟sleep
                    try {
                        Thread.sleep(200);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                    System.out.println(Thread.currentThread().getName()+"-->拿到了第"+ticketnums--+"票");
                }
            }

            public static void main(String[] args) {
                TestThread4 ticket = new TestThread4();

                new Thread(ticket,"小明").start();
                new Thread(ticket,"小芬").start();
                new Thread(ticket,"黄牛党").start();
            }
        }

2.5 利用多线程实现龟兔赛跑

思路:
1.fori循环
2.方法用boolean写一个判断是否完成比赛,传递i过去
3.比赛结束跳出循环
4.新建两个线程调用
5.让兔子线程休息,记得try和catch一下
        public class Race implements Runnable{


            //胜利者
            private static String winner;

            @Override
            public void run() {
                for (int i = 0; i <= 100; i++) {
                    //模拟兔子休息sleep
                    if(Thread.currentThread().getName().equals("兔子")&&i%10==0){

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

                    //判断比赛是否结束
                    boolean flag = gameover(i);

                    //如果比赛结束了,就停止
                    if(flag){
                        break;
                    }

                    System.out.println(Thread.currentThread().getName()+"跑了"+i+"步");
                }
            }

            //判断完成比赛
            private boolean gameover(int steps){
                //判断是否有胜利者
                if (winner!=null){
                    return true;
                }{
                    if (steps >=100){
                        winner = Thread.currentThread().getName();
                        System.out.println("winner is "+ winner);
                        return true;
                    }
                }
                    return false;
            }


            public static void main(String[] args) {
                Race race = new Race();

                new Thread(race,"兔子").start();
                new Thread(race,"乌龟").start();
            }
        }

2.6 实现Callable接口

好几步走:
1.实现Callable接口,需要返回值类型
2.重写call方法,需要抛出异常
3.创建目标对象
4.创建执行服务
5.提交执行
6.获取结果
7.关闭服务

了解就好,如果以后用到再学,再来补,先鸽一下(狗头)

2.7 静态代理模式:com.fenfen.Thread.Demo2

思路:
1.两个类都改写接口的方法
2.将真实对象通过参数传进去(构造器),代理对象从而代理真实角色
好处:
代理对象可以做很多真实对象做不了的事情,真实对象专注做自己的事情
就是线程的底部原理
  			public class StaticProxy {       
			public static void main(String[] args) {

                You you = new You();

                //这边用lambda表达式表示:Thread代理一个真实的Runnable接口,并且调用了start方法
                new Thread(()-> System.out.println("我爱你")).start();


                //或者精简成new WeddingCompany(new You()).HappyMarry();
                WeddingCompany weddingCompany = new WeddingCompany(new You());
                weddingCompany.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() {
                before();
                this.target.HappyMarry();//这就是真实对象
                after();

            }

            private void after() {
                System.out.println("结婚之后,收尾款");
            }

            private void before() {
                System.out.println("结婚之前,布置现场");
            }
        }

2.8 Lambda表达式:com.fenfen.Thread.lamdba

2.8.1 基本内容

  1. Lambda表达式属于函数式编程
  2. 例如:a->System.out.println(“我在学习多线程->”+i);
  3. 函数式接口的定义:任何接口只包含了一个抽象方法,那就是函数式接口,就可以通过lambda表达式来创建该接口的对象

2.8.2 简略代码的方法

  1. 静态内部类
        public class TestLambda2 {

            //利用静态内部类的方式:加上static
            static class Like11 implements ILike1{
                @Override
                public void lambda() {
                    System.out.println("i like lambda1");
                }
            }


            public static void main(String[] args) {


                Like11 like11 = new Like11();
                like11.lambda();
            }

        }
                //1、定义一个函数式接口
                interface ILike1 {
                    void lambda();

                }
  1. 局部内部类
        public class TestLambda3 {


            public static void main(String[] args) {

                class Like3 implements ILike3{
                    @Override
                    public void lambda() {
                        System.out.println("i like lambda3");
                    }
                }
                Like3 like3 = new Like3();
                like3.lambda();
            }

        }
        //1、定义一个函数式接口
        interface ILike3 {
            void lambda();

        }
  1. 匿名内部类:没有类的名称
        public class TestLambda4 {

            public static void main(String[] args) {

                ILike4 like4 = new ILike4(){
                @Override
                public void lambda() {
                    System.out.println("i like lambda4");
                }
            };
                like4.lambda();
            }

        }
        //1、定义一个函数式接口
        interface ILike4 {
            void lambda();

        }
  1. 用lambda简化
        public class TestLambda5 {
            public static void main(String[] args) {
                ILike5 like5= ()->{
                    System.out.println("i like lambda5");
                };

                like5.lambda();
            }

        }
        //1、定义一个函数式接口
        interface ILike5 {
            void lambda();

        }
  1. 正常接口代码2
        public class TestLambda6 {

            public static void main(String[] args) {
                Love love = new Love();
                love.love(666);
            }

        }

        interface Ilove{
            void love(int a );
        }

        class Love implements Ilove{
            @Override
            public void love(int a) {
                System.out.println("i love life-->"+a);
            }
        }
  1. 匿名内部类2
        public class TestLambda7 {

            public static void main(String[] args) {

                Ilove1 ilove1 = new Ilove1(){//记得改成接口的类
                    @Override
                    public void love(int a) {
                        System.out.println("i love life-->"+a);
                    }
                };
                ilove1.love(888);
            }

        }

        interface Ilove1{
            void love(int a );
        }

7.用lambda简化2

        public static void main(String[] args) {

            Ilove2 ilove2 = (int a)-> {
                    System.out.println("i love life-->"+a);
                };

            //再简化:①去掉参数类型
            ilove2 = (a)-> {
                System.out.println("i love life-->"+a);
            };

            //再简化:把括号都简化没了
            ilove2 = a->
                System.out.println("i love life-->"+a);

            ilove2.love(888);
        }

    }
    interface Ilove2{
        void love(int a );
    }
  1. 用lambda简化多个参数
        public class TestLambda9 {

            public static void main(String[] args) {

                Ilove3 ilove3 = null;

                ilove3 = (a,b)-> {
                    System.out.println("i love you-->"+a+" "+b);
                };
                ilove3.love(520,1314);
                /*
                多个参数也可以去掉参数类型,要去掉就全部去掉,并且带上括号
                 */

            }

        }
        interface Ilove3{
            void love(int a,int b );
        }

3. 线程状态

3.1线程五个状态

JavaSE-09 Thread 多线程(完整版)_ide_02

3.2 线程方法

有优先级,休眠,加入,暂停,停止,是否存活

JavaSE-09 Thread 多线程(完整版)_优先级_03

3.2.1 线程停止stop:com.fenfen.Thread.ThreadStop

JDK一般不建议使用它自己本身的方法停止线程,一般会建立一个标志位进行终止变量

        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++);
                }
            }

            //2、设置一个公开的方法停止线程,转换标志位,方便调用的

            public void stop(){
                 this.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();
                        System.out.println("线程该停止了");
                    }

                }

            }

        }

3.2.2 线程休眠sleep:com.fenfen.Thread.ThreadSleep

  1. sleep作用:模拟网络超时:没有sleep会出现只有一个线程拿了票,有了延迟是为了方法问题的发生性:发生线程安全,即多个线程操作了通过同一个对象
  2. 用sleep模拟当前倒计时
        public class TestSleep2 {

            public static void tenDown() throws InterruptedException{
                int num = 10;

                while (true){
                    Thread.sleep(1000);
                    System.out.println(num--);

                    if (num<=0){
                        break;
                    }
                }
            }

            public static void main(String[] args) {
                try {
                    tenDown(); 
                } catch (Exception e) {
                    e.printStackTrace();
                }

            }
        }
  1. 用sleep打印系统当前时间
        public class TestSleep3 {
            public static void main(String[] args) {

                Date startTime = new Date(System.currentTimeMillis());//获取系统当前时间

                while (true){

                    try {
                        System.out.println(new SimpleDateFormat("HH:mm:ss").format(startTime));//时间格式化工厂
                        startTime = new Date(System.currentTimeMillis());//更新当前时间
                        Thread.sleep(1000);

                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        }

3.2.3 线程礼让yield

  1. 概念:让当前执行的线程暂停,但不阻塞,将线程从运行状态转为就绪状态,然后cpu会重新调度,礼让不一定成功,可能调度的还是它
  2. com.fenfen.Thread.ThreadYield
        public class TestYield1 {
            public static void main(String[] args) {
                MyYield myYield = new MyYield();

                new Thread(myYield,"a").start();
                new Thread(myYield,"b").start();
            }


        }


        class MyYield implements Runnable{//alt+回车导入run方法
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+ "线程开始执行");
                Thread.yield();//礼让
                System.out.println(Thread.currentThread().getName()+ "线程停止执行");
            }

        }

3.2.4 线程强制执行join

  1. 概念:join合并线程,待此线程执行完成后,再执行其他线程,其他线程阻塞

  2. com.fenfen.Thread.ThreadJoin

        public class TestJoin1 implements Runnable{
            //main到199的时候,让给thread执行了
            @Override
            public void run() {
                for (int i = 0; i < 1000; i++) {
                    System.out.println("线程vip已经出现!" + i);
                }

            }

            public static void main(String[] args) throws InterruptedException {
                TestJoin1 testJoin1 = new TestJoin1();
                Thread thread = new Thread(testJoin1);
                thread.start();

                //主线程方法

                for (int i = 0; i < 500; i++) {
                    if (i == 200) {
                        thread.join();//插队
                    }
                    System.out.println("main" + i);

                }
            }
        }

3.2.5 线程状态观测getState

  1. 具体实现如下:
  • 使用thread.getState()观察测试线程的状态:主线程观测子线程状态,当子线程中重写的run方法执行后,子线程就停止了,主线程跳出循环并停止
        public class TestState1 {

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

                //用lambda重写run方法
                Thread thread = new Thread(()->{
                    for (int i = 0; i < 5; i++) {
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println(i);
                    }
                    System.out.println("========");

                });


                //观察状态
                Thread.State state = thread.getState();
                System.out.println(state);


                //观察启动后并再次观察状态
                thread.start();
                state = thread.getState();//可以不用每次都创建一个对象了,节约空间来的
                System.out.println(state);

                //
                while (state != Thread.State.TERMINATED){//只要线程不终止,就一直输出状态
                    Thread.sleep(100);
                    state = thread.getState();//再次更新线程状态
                    System.out.println(state);

                }

            }

观察测试线程的状态:主线程观测子线程状态,当子线程中重写的run方法执行后,子线程就停止了,主线程跳出循环

  1. 再回顾下线程的五个状态
JavaSE-09 Thread 多线程(完整版)_优先级_04

3.2.6 线程优先级getPriority().setPriority(int x)

在Thread类中有几个常量,设置了最小优先值为1,最大优先级是10

JavaSE-09 Thread 多线程(完整版)_java_05

顺便看下setPriority方法

        public final void setPriority(int newPriority) {//此方法需要一个int值
            ThreadGroup g;//线程组
            checkAccess();
            if (newPriority > MAX_PRIORITY || newPriority < MIN_PRIORITY) {
                throw new IllegalArgumentException();//如果线程的优先级超出上面的常量最大值,小于最小值抛一个异常出来
            }
            if((g = getThreadGroup()) != null) {//为空的话,直接默认或者最大都ok
                if (newPriority > g.getMaxPriority()) {
                    newPriority = g.getMaxPriority();
                }
                setPriority0(priority = newPriority);
            }
        }

那就继续顺便咯,在下看getPriority方法,就比较简单们直接返回priority就可以

        public final int getPriority() {
            return priority;
        }
  1. 获取与设置线程的优先级
        public class TestPriority {
            public static void main(String[] args) {
                //获取主线程名字以及优先级:默认是5
                System.out.println(Thread.currentThread().getName()+"-->"+Thread.currentThread().getPriority());

                MyPriority myPriority = new MyPriority();

                Thread t1 = new Thread(myPriority);
                Thread t2 = new Thread(myPriority);
                Thread t3 = new Thread(myPriority);
                Thread t4 = new Thread(myPriority);
                Thread t5 = new Thread(myPriority);
                Thread t6 = new Thread(myPriority);

                //先设置优先级,再启动,不然启动了再设置没用的
                t1.start();

                t2.setPriority(1);
                t2.start();

                t3.setPriority(4);
                t3.start();

                t4.setPriority(Thread.MAX_PRIORITY);//10
                t4.start();

                //t5.setPriority(-1);
                //t5.start();会报错抛出异常

                //t6.setPriority(11);
                //t6.start();会报错抛出异常
            }
        }

        class MyPriority implements Runnable{
            @Override
            public void run() {
                //获取子线程名字以及优先级
                System.out.println(Thread.currentThread().getName()+"-->"+Thread.currentThread().getPriority());
            }
        }

只是意味着获取调度的概率低,并不是优先级高的就一定被最先调用,全看cpu的调度

4 线程同步

4.1基础概念

  1. 并发:同一个对象被多个线程同时操作
  2. 线程同步:就是一种等待机制,多个需要同时访问此对象的线程进行这个对象的等待池形成对列,等待前面线程使用完毕,下一个线程再使用,一般会通过对列加锁的形式保证线程同步,解决安全性
  3. 锁:为了保证线程安全,在访问时加入锁机制synchronized,当一个线程获得对象的排它锁,其他线程必须等待,使用后释放锁即可
  4. 用锁后仍然存在以下问题:
  • 一个线程有锁导致其他线程需要此锁的所有线程会被挂起
  • 多线程竞争下,加锁,释放锁会导致比较多的上下文切换以及调度延迟,引起性能问题
  • 优先级高的线程等待一个优先级低的线程释放锁,会导致优先级导致,引发性能倒置问题

4.2 线程不安全的举例:com.fenfen.Thread.Synchronized

4.2.1 火车票超卖的例子:

不安全的买票:票会出现-1的情况

        public class UnsafeBuyTicket {
            public static void main(String[] args) {
                BuyTicket station = new BuyTicket();

                new Thread(station,"你").start();
                new Thread(station,"我").start();
                new Thread(station,"他").start();

            }
        }

        class BuyTicket implements Runnable{

            //票
            private int ticketNums = 10;

            //标志位
            boolean flag = true;//外部停止方式
            @Override
            public void run() {
                //买票
              while(flag){
                  try {
                      buy();
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
              }
            }

            private void buy() throws InterruptedException {
                //判断是否邮票
                if(ticketNums<=0){
                    flag = false;
                    return;
                }

                //延迟一下
                Thread.sleep(100);


                System.out.println(Thread.currentThread().getName()+"拿到----->"+ticketNums--);
            }

        }

4.2.2 银行取钱的例子

多人去银行取钱可取到超出余额的钱

        public class UnsafeBank {
            public static void main(String[] args) {
                Account account = new Account(100,"积蓄");

                Drawing you = new Drawing(account,50,"你" );
                Drawing boyfriend = new Drawing(account,100,"boyfriend" );

                you.start();
                boyfriend.start();
            }
        }

        //账户
        class Account{
            int money;//余额
            String name;//卡名

            public Account(int money, String name) {
                this.money = money;
                this.name = name;
            }
        }


        //银行:模拟取款
        class Drawing extends Thread{

            Account account;//账户
            //要去多少钱
            int drawingMoney;
            //现在手里有多少钱
            int nowMoney;

            //构造器
            public Drawing(Account account,int drawingMoney,String name){
                super(name);//调用父类的有参构造,是线程的名字
                this.account = account;
                this.drawingMoney = drawingMoney;

            }

            //重写方法
            @Override
            public void run() {
                //判断下有没有钱呢
                if(account.money-drawingMoney<0){
                    System.out.println(Thread.currentThread().getName()+"钱不够你取了");
                    return;
                }

                //延迟下呀,放大线程不安全的发生性
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                //余额=上次余额-取的钱
                account.money = account.money-drawingMoney;
                //你手里的钱
                nowMoney = nowMoney +drawingMoney;

                System.out.println(account.name+"余额为:"+account.money);
                //Thread.currentThread().getName() = this.getName()原因是:继承了Thread的全部方法,因此可以调用this
                System.out.println(this.getName()+"手里钱为:"+nowMoney);
            }
        }

4.2.3 线程不安全的集合

        public class UnsafeList {
            public static void main(String[] args) throws InterruptedException {
                List<String> list = new ArrayList<>();

                for (int i = 0; i < 18000; i++) {//实际输出就17000多
                    new Thread(()->{
                        list.add(Thread.currentThread().getName());//把线程名字添加到集合里面了
                    }).start();
                }

                Thread.sleep(3000);
                System.out.println(list.size());
            }
        }

可能出现两个线程同一瞬间操作了同一个位置,把两个数组添加了同一个位置,覆盖的就是少的元素

4.3 同步方法及同步块

4.3.1 同步方法

火车票超卖修改:将synchronized放在方法前:默认锁的是this类

        public class SafeBuyTicket {
            public static void main(String[] args) {
                BuyTicket1 station = new BuyTicket1();

                new Thread(station,"你").start();
                new Thread(station,"我").start();
                new Thread(station,"他").start();

            }
        }

        class BuyTicket1 implements Runnable{

            //票
            private int ticketNums = 10;

            //标志位
            boolean flag = true;//外部停止方式
            @Override
            public void run() {
                //买票
                while(flag){

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

                    buy();

                    }

            }

            //
            private synchronized void buy(){
                //判断是否邮票
                if(ticketNums<=0){
                    flag = false;
                    return;
                }

                //延迟一下
                //Thread.sleep(1000);//注意这边,sleep不会释放锁,一个线程拿到锁就会一直执行,于是把sleep放在run方法里


                System.out.println(Thread.currentThread().getName()+"拿到----->"+ticketNums--);
            }

        }

4.3.2 同步块

  1. 基本概念:同步块synchronized(Obj){},Obj就是称之为同步监视器,但是推荐使用共享资源作为同步监视器 ,例子中就是锁的是account这个共享资源

  2. 银行取钱修改:

        public class SafeBank {
            public static void main(String[] args) {
                Account1 account1 = new Account1(500,"积蓄");

                Drawing1 you = new Drawing1(account1,50,"你" );
                Drawing1 boyfriend = new Drawing1(account1,100,"boyfriend" );

                you.start();
                boyfriend.start();
            }
        }

        //账户
        class Account1{
            int money;//余额
            String name;//卡名

            public Account1(int money, String name) {
                this.money = money;
                this.name = name;
            }
        }


        //银行:模拟取款
        class Drawing1 extends Thread{

            Account1 account1;//账户
            //要去多少钱
            int drawingMoney;
            //现在手里有多少钱
            int nowMoney;

            //构造器
            public Drawing1(Account1 account1,int drawingMoney,String name){
                super(name);//调用父类的有参构造,是线程的名字
                this.account1 = account1;
                this.drawingMoney = drawingMoney;

            }

            //重写方法
            @Override
            public void run() {
                synchronized (account1) {//锁这个对象,是根据代码中针对哪一些进行了增删改查
                    //判断下有没有钱呢
                    if (account1.money - drawingMoney < 0) {
                        System.out.println(Thread.currentThread().getName() + "钱不够你取了");
                        return;
                    }

                    //延迟下呀,放大线程不安全的发生性
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                    //余额=上次余额-取的钱
                    account1.money = account1.money - drawingMoney;
                    //你手里的钱
                    nowMoney = nowMoney + drawingMoney;

                    System.out.println(account1.name + "余额为:" + account1.money);
                    //Thread.currentThread().getName() = this.getName()原因是:继承了Thread的全部方法,因此可以调用this
                    System.out.println(this.getName() + "手里钱为:" + nowMoney);
                }
            }
        }
  • 只锁run方法的时候:只会锁住所在的Drawing类,不会锁住Account类线程

  • 线程“you”执行run方法时发现加锁了,便找到加锁对象(you),发现没有其他线程执行run方法,就持锁执行run方法

  • 线程“boyfriend”执行run方法时发现加锁了,便找到加锁对象,也发现没有其他线程执行run方法,就只锁执行run方法,导致实际上两个线程还是同步运行

  • 真正被两个线程并发访问引起冲突的是账户,因为两个Drawing都是使用同一个account来new的,所以应该锁Account而不是Bank,因此哪个类的属性会发生变化,就锁哪个类的哪个类的对象

  1. 线程不安全的集合的修改
        public class SafeList {
            public static void main(String[] args) throws InterruptedException {
                List<String> list = new ArrayList<>();

                for (int i = 0; i < 18000; i++) {//实际输出就17000多
                    new Thread(()->{
                        synchronized (list) {
                            list.add(Thread.currentThread().getName());//把线程名字添加到集合里面了
                        }
                    }).start();
                }

                Thread.sleep(3000);
                System.out.println(list.size());
            }
        }

把lambda中的list用同步块锁住就可以了

4.3.3 JUC包初始

JUC安全类型的集合:人家写好的类本来就是安全的,我们不需要再去同步了

        public class TestJUC {
            public static void main(String[] args) throws InterruptedException {
                CopyOnWriteArrayList<String > list = new CopyOnWriteArrayList<String>();

                for (int i = 0; i < 10000; i++) {
                    new Thread(()->{
                        list.add(Thread.currentThread().getName());
                            }).start();
                }
                Thread.sleep(3000);

                System.out.println(list.size());
            }
        }

去看源码:

        public CopyOnWriteArrayList() {
            setArray(new Object[0]);
        }
        final void setArray(Object[] a) {
            array = a;
        }
        private transient volatile Object[] array;
        //array用两个关键词修饰了, volatile 是唯一的意思,transient保证是序列化的
		//序列化 (Serialization)是将对象的状态信息转换为可以存储或传输的形式的过程,Java只是以某种形式实现了序列化

4.3.4 死锁

  1. 概念:多个线程都在等待对方释放资源,都停止执行,某一个同步块同时拥有”两个以上对象的锁“,就可能发生”死锁“

        public class DeadLock {
            public static void main(String[] args) {
                Makeup g1 = new Makeup(0,"芬芬");
                Makeup g2 = new Makeup(1,"静静");

                g1.start();
                g2.start();
            }
        }
        //口红
        class LipStick{ }

        //镜子
        class Mirror{ }

        class Makeup extends Thread{

            //static是为了限制只有一份资源
            static LipStick lipStick =  new LipStick();
            static Mirror mirror =  new Mirror();

            //定义选择以及人名
            int choice ;
            String girlName;

            //整一个构造器
            Makeup(int choice,String girlName){
                this.choice = choice;
                this.girlName = girlName;

            }

            @Override
            public void run() {
                try {
                    makeup();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

            //写一个化妆的方法:互相持有对方的锁,即拿到对方的资源
            private void makeup() throws InterruptedException {
                if(choice==0){
                    synchronized (lipStick){//把口红锁住
                        System.out.println(this.girlName+"获得口红的锁");

                        Thread.sleep(1000);

                        synchronized (mirror){////锁中锁,自己没释放,还想去锁拿另一个的资源
                            System.out.println(this.girlName+"获得镜子的锁");
                        }
                    }
                }else{
                    synchronized (mirror){//把镜子锁住
                        System.out.println(this.girlName+"获得镜子的锁");

                        Thread.sleep(2000);

                        synchronized (lipStick){////锁中锁,自己没释放,还想去锁拿另一个的资源
                            System.out.println(this.girlName+"获得口红的锁");
                        }
                    }
                }
            }
        }
        public class DeadLock1 {
            public static void main(String[] args) {
                Makeup1 g1 = new Makeup1(0,"芬芬");
                Makeup1 g2 = new Makeup1(1,"静静");

                g1.start();
                g2.start();
            }
        }
        //口红
        class LipStick1{ }

        //镜子
        class Mirror1{ }

        class Makeup1 extends Thread{

            //static是为了限制只有一份资源
            static LipStick1 lipStick1 =  new LipStick1();
            static Mirror1 mirror1 =  new Mirror1();

            //定义选择以及人名
            int choice ;
            String girlName;

            //整一个构造器
            Makeup1(int choice,String girlName){
                this.choice = choice;
                this.girlName = girlName;

            }

            @Override
            public void run() {
                try {
                    makeup();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }


            //写一个化妆的方法:互相持有对方的锁,即拿到对方的资源
            private void makeup() throws InterruptedException {
                if(choice==0){

                    synchronized (lipStick1){//把口红锁住
                        System.out.println(this.girlName+"获得口红的锁");
                        Thread.sleep(1000);
                        }
                    synchronized (mirror1){//放到外面来就可以了
                        System.out.println(this.girlName+"获得镜子的锁");
                    }

                }else{

                    synchronized (mirror1){//把镜子锁住
                        System.out.println(this.girlName+"获得镜子的锁");

                        Thread.sleep(2000);
                        }
                    synchronized (lipStick1){//放到外面来就可以了
                        System.out.println(this.girlName+"获得口红的锁");

                    }
                }
            }
        }

4.3.4 Lock锁

  1. 概念:通过显示定义同步锁对象来实现同步,同步锁使用Lock对象,只有一个线程对Lock对象加锁,类似synchronized并发性和内存语义,常用ReentrantLock可重入锁

    /** The lock protecting all mutators */
    final transient ReentrantLock lock = new ReentrantLock();
JavaSE-09 Thread 多线程(完整版)_ide_06
        public class TestLock {
            public static void main(String[] args) {
                TestLock2 testLock2 = new TestLock2();

                new Thread(testLock2).start();
                new Thread(testLock2).start();
                new Thread(testLock2).start();
            }

        }

        class TestLock2 implements Runnable{

            int ticketNums = 10;

            //定义lock锁
            private final ReentrantLock lock = new ReentrantLock();

            @Override
            public void run() {
                while (true){
                    //加锁解锁,在try和finally中
                    try {
                        lock.lock();
                        if(ticketNums>0){
                            try {
                                Thread.sleep(1000);
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                            System.out.println(ticketNums--);
                        }else {
                            break;
                        }
                    }finally {
                        lock.unlock();//解锁
                    }
                }

            }
        }

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK