第一章:线程概述
一:线程相关概念
1:进程概念
进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。在当代面向线程设计的计算机结构中,进程是线程的容器。
简单来讲,进程就是操作系统中正在运行的一个程序,这就是进程
2:线程概念
线程(Thread)就是进程的一个执行单元,一个线程就是进程中一个单一顺序的控制流,线程就是进程的一个执行分支。
进程是线程的容器,一个进程至少有一个线程,一个进程当中也可以有多个线程。在我们的操作系统当中,是以进程为单位分配资源的,比如说我们的虚拟存储空间,文件的描述符等,我们进程当中包含着多个线程,每个线程都有自己的线程栈,都有自己的寄存器环境,都有自己的线程本地存储。
3:线程分类
1):主线程和工作线程
main线程是启动程序的线程,main线程当中往往包含main方法,与之对应的是工作线程,工作线程是执行各种具体的业务逻辑的线程,我们经常称main先成为主线程
2):主线程和子线程
我们Java虚拟机启动时,会创建一个主线程,该主线程负责执行main方法,主线程就是负责运行main方法的线程,Java中的线程不是孤立的,线程之间也存在一些联系。如果在A线程中创建一个B线程,B线程就是A线程的子线程,A线程就是B线程的父线程,父子线程是相对的,谁创建了谁就是一个相对的关系。
3):工作线程和守护线程
JVM当中的线程可以分为工作线程和守护线程,工作线程主要是执行各样的业务逻辑,守护线程 用于服务工作线程
4:串行并发并行
串行:一个一个的完成
并发:交替完成,可以提高事务的处理效率,在一段时间内可以处理或者完成更多的事情。
并行:同时干,更为严格或者理想的并发,可以看做是并发的一种特例,多个CPU一个cpu执行一个任务。
从硬件角度,如果是单核CPU,一个处理器一次只能执行一个线程的情况下,处理器可以使用时间片轮转技术,这是操作系统的概念,我们可以让CPU快速的在各个线程之间进行切换,对于用于来说感觉是三个线程是同时执行,如果是多核CPU,那么我们可以为不同的线程分配不同的CPU内核
二:线程的创建和启动
在我们Java当中创建一个线程就是创建一个Thread的实例,或者子类。Thread类实现了Runnable接口,对于我们创建线程来讲一共有两种形式,第一种就是来创建Thread类的或者子类的实例,或者实现Runnable接口。
Thread类有两个常用的构造方法,第一个是Thread的无参构造,第二个是实现Runnable接口的实例。创建线程的两种方式第一种就是创建Thread类的子类,第二种形式就是创建一个类实现Runnable接口,创建接口实现类的子类,这两种方式没有本质的区别,因为本质上Thread也实现了Runnable接口。
总结:创建一个线程就是创建一个Thread类的对象
1:创建线程的常用方法
1):Thread创建线程的方法
我们调用线程的start()方法来启动线程,这里启动线程的本质,就是请求java虚拟机运行响应的线程,这个线程具体在在什么时候运行由线程调度器(scheduler)决定,这个线程调度器是操作系统的概念,这个start方法执行之后并不意味着,我们子县城开始运行了,只是通知线程调度器这个线程可以进行执行了,我们新开启的线程会执run方法过来执行线程中的代码,如果开启了多个线程,start调用的顺序并不一定就是线程启动的顺序,这个start方法只是告诉java虚拟机和操作系统的调度器我这个线程准备好了。线程开始的前后是由这个线程调度器决定的,子线程有可能在主线程的前边执行完成,有可能在他的后边执行完成,多线程之间的代码和代码的被执行顺序无关,run方法体就是具体线程的需要执行代码,调用start方法就是开启一个线程,开启这个线程之后,线程开始执行之后,过来执行这个run方法
在很多情况下,主线程创建并启动子线程,如果子线程中要进行大量的耗时运算,主线程将可能早于子线程结束。如果主线程需要知道子线程的执行结果时,就需要等待子线程执行结束了。主线程可以sleep(xx),但这样的xx时间不好确定,因为子线程的执行时间不确定,join()方法比较合适这个场景。是主线程等待子线程的终止。也就是说主线程的代码块中,如果碰到了t.join()方法,此时主线程需要等待(阻塞),等待子线程结束了(Waits for this thread to die.),才能继续执行t.join()之后的代码块。Join方法实现是通过wait(小提示:Object 提供的方法)。 当main线程调用t.join时候,main线程会获得线程对象t的锁(wait 意味着拿到该对象的锁),调用该对象的wait(等待时间),直到该对象唤醒main线程 ,比如退出后。这就意味着main 线程调用t.join时,必须能够拿到线程t对象的锁。
public class TestClass { public static void main(String[] args) { System.out.println("java启动主线程main线程....."); Thread thread = new MyThread(); thread.start(); System.out.println("main线程其他的代码........."); //java启动主线程main线程..... //main线程其他的代码......... //这是子线程打印的内容.......... } }
package com.dashu.Test; public class MyThread extends Thread{ @Override public void run() { System.out.println("这是子线程打印的内容.........."); } }
2):多线程结果的随机性
/** * @Auther: DaShu * @Date: 2021/8/26 20:28 * @Description: */ public class TestClass2 { public static void main(String[] args) { Thread thread = new MyThread2(); thread.start(); try { for (int i = 0; i < 10; i++) { System.out.println("Main-:" + i); int time = (int)(Math.random() * 1000); Thread.sleep(time);//线程进入tem_wating装填 } } catch (InterruptedException e) { e.printStackTrace(); } } }
/** * @Auther: DaShu * @Date: 2021/8/26 20:28 * @Description: */ public class MyThread2 extends Thread{ public void run(){ try { for (int i = 0; i < 10; i++) { System.out.println("son-:" + i); int time = (int)(Math.random() * 1000); Thread.sleep(time); } } catch (InterruptedException e) { e.printStackTrace(); } } }
Main-:0 son-:0 son-:1 son-:2 Main-:1 Main-:2 Main-:3 Main-:4 son-:3 Main-:5 son-:4 Main-:6 son-:5 Main-:7 Main-:8 son-:6 son-:7 Main-:9 son-:8 son-:9
3):Runnable接口创建线程
三:线程常用方法
1:currentThread()
currentThread方法是静态方法可以获取当前线程
Java中的任何一段都是执行在某个线程当中,或者主线程,或者子线程,执行当前代码的线程就是当前线程,同一段代码可能被不同的线程执行,因此当前线程是相对的,也就是这个代码返回的是在代码实际运行时候的线程对象,我们用代码演示一下。
public class TestClass3 { public static void main(String[] args) { System.out.println("main方法中线程:"+Thread.currentThread().getName()); //创建子线程 CurrentThread currentThread = new CurrentThread(); currentThread.start(); //main方法中线程:main //构造方法打印main //run方法打印Thread-0 //构造方法当中打印的事main线程,也就是说在main线程当中调用的构造方法,也就是在 //构造方法中的线程就是main线程,子线程启动之后,子线程会调用run方法,所以在run //方法当中输出的线程的名称就是子线程。所以run方法当中输出的线程就是子线程的名称。 //如果我们把把start方法进行注释掉,直接调用run方法,这样就不存在异步的了,这是在 //main方法当中直接执行的是run方法当中的代码,这里打印的事main线程。 //当前线程就是调用着段代码的线程。 } }
/** * @Auther: DaShu * @Date: 2021/8/26 21:42 * @Description: */ public class CurrentThread extends Thread{ public CurrentThread(){ System.out.println("构造方法打印"+Thread.currentThread().getName()); } public void run(){ System.out.println("run方法打印"+Thread.currentThread().getName()); } }
- currentThread复杂方法
/** * @Auther: DaShu * @Date: 2021/8/27 19:49 * @Description: */ public class TestClass4 { public static void main(String[] args) throws InterruptedException { //创建子线程对象 SubThread subThread = new SubThread(); subThread.setName("t2"); //设置线程的名称,getName获取线程名称 subThread.start(); Thread.sleep(500); //main线程睡眠500毫秒,保证T2线程已经启动 //Thread(Runnable)构造方法形参是Runnable接口,调用的时候传递的实参是接口的 //实现类对象。我们T2继承了Thread,Thread实现了Runalbe接口。 Thread t3 = new Thread(subThread); t3.start(); //构造方法中,Thread.currentThread().getName():main //构造方法: :Thread-0 //构造方法中,Thread.currentThread().getName():main //run方法,Thread.currentThread().getName():t2 //run方法: :t2 //run方法,Thread.currentThread().getName():Thread-1 //run方法: :t2 } } class Test4{ public static void main(String[] args) { ThreadTest threadTest = new ThreadTest(); threadTest.start(); //Thread-0 //Thread-0 } } class ThreadTest extends Thread{ @Override public void run() { System.out.println(this.getName()); System.out.println(Thread.currentThread().getName()); } }
2:setName()和getName()
获取线程名称,设置线程名称
设置线程名称,有利于提高程序可读性
3:isAlive()
判断当前线程是否是活动状态,活动状态指的是,线程已经被启动,而且尚未终止。
/** * @Auther: DaShu * @Date: 2021/8/27 20:25 * @Description: */ public class TestClass5 { public static void main(String[] args) throws InterruptedException { CurrentThread5 currentThread5 = new CurrentThread5(); System.out.println("begin = " + currentThread5.isAlive()); currentThread5.start(); Thread.sleep(100); System.out.println("end = " + currentThread5.isAlive());//在main线程当中判断某个子线程是否活着 //begin = false //end = true //当前线程是否存活:true } }
4:sleep()
Thread.sleep(),让当前线程休眠指定的毫秒数,当前现场指的是Thread.currentThread()返回的线程
/** * @Auther: DaShu * @Date: 2021/8/27 20:25 * @Description: */ public class TestClass5 { public static void main(String[] args) throws InterruptedException { CurrentThread5 currentThread5 = new CurrentThread5(); System.out.println("begin = " + currentThread5.isAlive()); currentThread5.start(); Thread.sleep(100); System.out.println("end = " + currentThread5.isAlive());//在main线程当中判断某个子线程是否活着 //begin = false //end = true //当前线程是否存活:true } }
当我们父类方法中没有抛出异常的时候,子类重写父类方法也不能抛出异常只能够捕获处理
5:getId()
获取当前线程的线程号,唯一id,Java当中每一个线程都有自己的线程号,都有自己的ID,main线程的id是1,线程id等线程执行结束之后会被回收,分配给其他的线程,所以可能会出现线程重复的时候。我们某个编号线程运行结束后,该线程id编号,可能被后续线程使用,这样的线程id就是相同的。java虚拟机重启之后,同一个线程编号和上次启动的线程标号有可能是不一样的。
/** * @Auther: DaShu * @Date: 2021/8/27 21:04 * @Description: */ public class TestClass6 { public static void main(String[] args) throws InterruptedException { System.out.println("Thread name = " + Thread.currentThread().getName() + " Thread id = " + Thread.currentThread().getId()); //主线程的ID一定是1,但是子线程id不是从二开始的。 for (int i = 0; i <= 100 ; i++) { Thread.sleep(1000); new CurrentThread6().start(); } //Thread name = main Thread id = 1 //Thread name = Thread-0 Thread id = 11 //Thread name = Thread-1 Thread id = 12 //Thread name = Thread-2 Thread id = 13 //Thread name = Thread-3 Thread id = 14 //Thread name = Thread-4 Thread id = 15 //Thread name = Thread-5 Thread id = 16 //Thread name = Thread-6 Thread id = 17 //Thread name = Thread-7 Thread id = 18 //Thread name = Thread-8 Thread id = 19 //Thread name = Thread-9 Thread id = 20 //Thread name = Thread-10 Thread id = 21 //Thread name = Thread-11 Thread id = 22 //Thread name = Thread-12 Thread id = 23 //Thread name = Thread-13 Thread id = 24 //Thread name = Thread-14 Thread id = 25 //Thread name = Thread-15 Thread id = 26 //Thread name = Thread-16 Thread id = 27 //Thread name = Thread-17 Thread id = 28 //Thread name = Thread-18 Thread id = 29 //Thread name = Thread-19 Thread id = 30 //Thread name = Thread-20 Thread id = 31 //Thread name = Thread-21 Thread id = 32 //Thread name = Thread-22 Thread id = 33 //Thread name = Thread-23 Thread id = 34 //Thread name = Thread-24 Thread id = 35 //Thread name = Thread-25 Thread id = 36 //Thread name = Thread-26 Thread id = 37 //Thread name = Thread-27 Thread id = 38 //Thread name = Thread-28 Thread id = 39 //Thread name = Thread-29 Thread id = 40 //Thread name = Thread-30 Thread id = 41 //Thread name = Thread-31 Thread id = 42 //Thread name = Thread-32 Thread id = 43 //Thread name = Thread-33 Thread id = 44 //Thread name = Thread-34 Thread id = 45 //Thread name = Thread-35 Thread id = 46 //Thread name = Thread-36 Thread id = 47 //Thread name = Thread-37 Thread id = 48 //Thread name = Thread-38 Thread id = 49 //Thread name = Thread-39 Thread id = 50 //Thread name = Thread-40 Thread id = 51 //Thread name = Thread-41 Thread id = 52 //Thread name = Thread-42 Thread id = 53 //Thread name = Thread-43 Thread id = 54 //Thread name = Thread-44 Thread id = 55 //Thread name = Thread-45 Thread id = 56 //Thread name = Thread-46 Thread id = 57 //Thread name = Thread-47 Thread id = 58 //Thread name = Thread-48 Thread id = 59 //Thread name = Thread-49 Thread id = 60 //Thread name = Thread-50 Thread id = 61 //Thread name = Thread-51 Thread id = 62 //Thread name = Thread-52 Thread id = 63 //Thread name = Thread-53 Thread id = 64 //Thread name = Thread-54 Thread id = 65 //Thread name = Thread-55 Thread id = 66 //Thread name = Thread-56 Thread id = 67 //Thread name = Thread-57 Thread id = 68 //Thread name = Thread-58 Thread id = 69 //Thread name = Thread-59 Thread id = 70 //Thread name = Thread-60 Thread id = 71 //Thread name = Thread-61 Thread id = 72 //Thread name = Thread-62 Thread id = 73 //Thread name = Thread-63 Thread id = 74 //Thread name = Thread-64 Thread id = 75 //Thread name = Thread-65 Thread id = 76 //Thread name = Thread-66 Thread id = 77 //Thread name = Thread-67 Thread id = 78 //Thread name = Thread-68 Thread id = 79 //Thread name = Thread-69 Thread id = 80 //Thread name = Thread-70 Thread id = 81 //Thread name = Thread-71 Thread id = 82 //Thread name = Thread-72 Thread id = 83 //Thread name = Thread-73 Thread id = 84 //Thread name = Thread-74 Thread id = 85 //Thread name = Thread-75 Thread id = 86 //Thread name = Thread-76 Thread id = 87 //Thread name = Thread-77 Thread id = 88 //Thread name = Thread-78 Thread id = 89 //Thread name = Thread-79 Thread id = 90 //Thread name = Thread-80 Thread id = 91 //Thread name = Thread-81 Thread id = 92 //Thread name = Thread-82 Thread id = 93 //Thread name = Thread-83 Thread id = 94 //Thread name = Thread-84 Thread id = 95 //Thread name = Thread-85 Thread id = 96 //Thread name = Thread-86 Thread id = 97 //Thread name = Thread-87 Thread id = 98 //Thread name = Thread-88 Thread id = 99 //Thread name = Thread-89 Thread id = 100 //Thread name = Thread-90 Thread id = 101 //Thread name = Thread-91 Thread id = 102 //Thread name = Thread-92 Thread id = 103 //Thread name = Thread-93 Thread id = 104 //Thread name = Thread-94 Thread id = 105 //Thread name = Thread-95 Thread id = 106 //Thread name = Thread-96 Thread id = 107 //Thread name = Thread-97 Thread id = 108 //Thread name = Thread-98 Thread id = 109 //Thread name = Thread-99 Thread id = 110 //Thread name = Thread-100 Thread id = 111 } }
6:yield()
Thread.yield()方法就是告诉线程调度器,放弃当前CPU资源,如果别的线程着急,就先让别的线程用,如果没有别的线程用,就给我用,线程要想执行要先获取CPU资源才能执行,yield就是线程让步,放弃CPU执行权。CPU别的线程不用的时候,才会来搞这个线程的代码。有一点点类似于线程降级。
7:setPriority()
thread.setPriority(num)设置线程优先级,Java中的线程优先级是从1,10超过这个数据会抛出异常的IllegelArgumentException,线程优先级,操作系统中我们是可以给线程划分优先级的,优先级较高的线程获得CPU的资源越多,这样的话,线程优先级本质上只是给线程调度器一个提示信息,以便于线程调度器决定先调度哪些线程,注意不能保证优先级高的线程就能先运行,我们java优先级设置不当或者滥用可能会导致某些线程永远无法得到运行。即产生了线程饥饿,线程饥饿就是因为某一个或者某些线程级别太低获取不到线程资源,永远的不到CPU资源,线程优先级不是越高越好,开发时不必设置线程优先级,都采用默认值就好了,默认的线程等级是5
this.getPriority()获取线程优先级
/** * @Auther: DaShu * @Date: 2021/8/27 21:43 * @Description: */ public class TestClass7 { public static void main(String[] args) { ThreadA threadA = new ThreadA(); threadA.setPriority(1); threadA.start(); ThreadB threadB = new ThreadB(); threadB.setPriority(10); threadB.start(); //ThreadB = 305 //ThreadA = 399 } }
8:interrupt()
中断线程,这个方法可以中断线程,调用interrupt方法仅仅是在当前线程打一个停止标识,并不是真正的停止线程,这个在main方法中执行这个方法只是为了给子线程打上中断标志,这样的话子线程在执行过程中就可以通过获取这个isInterrupt这个标志来获取当前的数据信息。进而可以判断是都要结束当前子线程。subThread.isInterrupt(true)就可以给当前的线程打停止标识。
9:setDaemon()
Java中的线程分为用户线程与守护线程,例如JVM中的Gc线程就是要一个典型的守护线程,守护线程是为了其他线程提供服务的,守护线程不能够单独运行,当JVM虚拟机当中没有其他用户线程,只有守护线程时,Java虚拟机会退出,守护线程会自动销毁,JVM会退出。通过setDaemon()就可以把这个线程设置成守护线程
/** * @Auther: DaShu * @Date: 2021/8/27 21:57 * @Description: */ public class TestClass8 { public static void main(String[] args) throws InterruptedException { CurrentThread8 currentThread8 = new CurrentThread8(); currentThread8.setDaemon(true); currentThread8.start(); for (int i = 0; i < 10; i++) { Thread.sleep(10); System.out.println("main = " + i); } //守护线程在进行打印............ //守护线程在进行打印............ //守护线程在进行打印............ //守护线程在进行打印............ //守护线程在进行打印............ //守护线程在进行打印............ //守护线程在进行打印............ //main = 0 //守护线程在进行打印............ //守护线程在进行打印............ //守护线程在进行打印............ //守护线程在进行打印............ //守护线程在进行打印............ //main = 1 //守护线程在进行打印............ //守护线程在进行打印............ //守护线程在进行打印............ //守护线程在进行打印............ //守护线程在进行打印............ //main = 2 //守护线程在进行打印............ //守护线程在进行打印............ //守护线程在进行打印............ //守护线程在进行打印............ //守护线程在进行打印............ //守护线程在进行打印............ //main = 3 //守护线程在进行打印............ //守护线程在进行打印............ //守护线程在进行打印............ //守护线程在进行打印............ //守护线程在进行打印............ //main = 4 //守护线程在进行打印............ //守护线程在进行打印............ //守护线程在进行打印............ //守护线程在进行打印............ //守护线程在进行打印............ //守护线程在进行打印............ //main = 5 //守护线程在进行打印............ //守护线程在进行打印............ //守护线程在进行打印............ //守护线程在进行打印............ //守护线程在进行打印............ //守护线程在进行打印............ //main = 6 //守护线程在进行打印............ //守护线程在进行打印............ //守护线程在进行打印............ //守护线程在进行打印............ //守护线程在进行打印............ //main = 7 //守护线程在进行打印............ //守护线程在进行打印............ //守护线程在进行打印............ //守护线程在进行打印............ //守护线程在进行打印............ //守护线程在进行打印............ //main = 8 //守护线程在进行打印............ //守护线程在进行打印............ //守护线程在进行打印............ //守护线程在进行打印............ //守护线程在进行打印............ //守护线程在进行打印............ //守护线程在进行打印............ //守护线程在进行打印............ //守护线程在进行打印............ //守护线程在进行打印............ //守护线程在进行打印............ //main = 9 //守护线程在进行打印............ } }
/** * @Auther: DaShu * @Date: 2021/8/27 21:57 * @Description: */ public class CurrentThread8 extends Thread{ @Override public void run() { while(true){ try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("守护线程在进行打印............"); } } }
多线程执行当中,main线程完事之后,守护线程还会执行一段时间,然后再被销毁,这是因为CPU调度修改是需要时间的。
设置线程守护的代码需要设置在线程启动之前,如果不是在线程启动之前,那么就不管用。
四:线程的生命周期
Question:线程的生命周期:
线程的生命周日就是线程对象的生老病死,就是线程的状态。getState获取的是线程的状态,获取的对象类型是枚举类型,Thread.Stare这样的枚举类型,枚举类型有以下集中,
1、new(新建状态)创建了线程对象,在调用Start启动之前就叫做新建状态。
2、Runnable(可运行状态)他是一个符合状态包含Ready和Running状态,Ready状态表示该线程可以随时被调度器进行调度使用使他的装态改为Running状态,Running状态表示该线程正在执行,正在执行现场的Run方法,Thread类的中有一个yield()方法,这个方法可以把线程由Running状态转换为Ready状态。
3、Blocked(阻塞状态)线程发起一个阻塞的I/o操作,或者申请由其他线程独占的资源,也就是这个资源其他线程正在使用着,然后你也想使用,这个线程就陷入阻塞状态,处于阻塞状态的线程不会占用CPU资源,当我们阻塞I/O操作执行完,或者申请的资源获得之后,就可以转换为Runnable状态。
4、Waiting(等待状态),我们线程执行了Object.wait()或者thread.join()方法之后,会把线程转换为waitting状态,执行Object.notify()方,Object.notifyAll()法,或者加入的线程执行完毕,当前线程会转换为Runnable状态,上边的这个加入的线程没有执行完毕,或者这个没有执行notify方法这个线程会已知傻傻的等待。还有一个状态叫做
5、Timed_Wating(暂时等待状态)与上边Wating这个类似,都是等待状态,区别在于状态的线程不会无休止的等待, 区别在于如果线程没有在指定范围内完成期望的操作,该线程自动转换为Runnable状态。
6、Terminated终止状态,线程结束了。
join方法加入一个线程。
五:多线程编程的优势与存在的风险
Question:多线程具有以下优势:
1、提高系统的吞吐路,多线程编程可以使一个进程有多个并发操作,(concurrent并发)有一个线程因为IO阻塞,其他的线程可以继续执行他们的业务
2、可以提高响应性,提高系统响应性,web服务器在同时请求多个http请求,web服务器会采用一些专门的线程来处理用户的请求,减少顾客的等待时间
3、充分利用多核(Multicore)处理器资源,不是多线程的程序,永远只会占用一个CPU,其他的CPU的核在闲着。恰当的多线程可以充分利用CPU的资源。
多线程具有一下风险:
1、线程安全问题:多个线程共享数据如果没有设置正确的并发访问措施就会造成数据一致性的问题,比如说读取脏数据,过期或者中间的数据,丢失数据更新,A线程做了线程更新,B线程给覆盖了
2、线程活性问题:理想的线程状态下我们应该一致处于Runnable状态,由于程序自身的缺陷或者有资源的稀缺性导致线程一值处于非Runnable状态,这就是线程活性问题,常见的活性故障有以下几中
(1)死锁(Deadlock):家门钥匙在车里,车钥匙在家里。
(2)锁死(Lockout):睡美人故事故事中王子挂了。
(3)活锁(Livelock):小猫转圈咬自己尾巴,就是咬不着。
(4)饥饿(Stavation):健壮雏鸟总是从母鸟嘴中抢食,弱鸟总是挨饿。
3、上线文切换:我们的处理器从一个线程专向另一个线程需要消耗系统资源,这个操作叫做上下文切换
4、可靠性:多线程中一个线程引起Java虚拟机的泄露,Java虚拟机以外终止,其他线程也就终止了。
第二章:线程安全问题
非线程安全主要指的是多个线程对同一个对象的实例变量进行操作时,会出现值被更改,值不同步的情况。
线程安全问题表现为三个方面:
一:原子性
1:什么叫原子性:
原子就是不可分割的意思,我们对共享变量进行操作,多个线程访问同一个变量,原子操作的不可分割有两层含义,第一我们访问某一共享变量的时候,从别的线程上看的时候,别的线程可以获取访问共享变量权限的时候,这个线程的操作要么已经结束,要么尚未发生,不能是正在进行,第二,我们访问同一组的共享变量的原子操作是不能够交叉的,ATM转账操作。要么操两个都成功,两个都失败。
2:如何实现原子
Java当中有两种方式实现这个原子性,一种是使用锁,另一种是使用处理器的CAS指令,这个CAS指的是(Compare and Swap),锁具有排他性,共享变量只能在同一时间 被一个线程访问,可以保证共享变量在某一时刻被同一线程访问,CAS指令直接在硬件层次上比如说处理器、内存使用指令,相当于硬件锁。
package com.dashu.Test10; /** * @Auther: DaShu * @Date: 2021/9/1 22:18 * @Description: */ public class TestClass { public static void main(String[] args) { final MyInit myInit = new MyInit(); for (int i = 0; i < 2; i++) { new Thread(new Runnable() { public void run() { while (true){ System.out.println(Thread.currentThread().getName() + "-->>"+ myInit.getNum()); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } } }).start(); } } static class MyInit{ int i ; public int getNum(){ i++; return i; } } } //Thread-0-->>1 //Thread-1-->>2 //Thread-1-->>3 //Thread-0-->>4 //Thread-0-->>5 //Thread-1-->>6 //Thread-1-->>7 //Thread-0-->>8 //Thread-1-->>10 //Thread-0-->>9 //Thread-1-->>11 //Thread-0-->>12 //Thread-1-->>13 //Thread-0-->>13 //Thread-0-->>14 //Thread-1-->>15 //Thread-1-->>16 //Thread-0-->>17 //Thread-0-->>18 //Thread-1-->>19 //Thread-1-->>20 //Thread-0-->>21 //Thread-0-->>22 //Thread-1-->>23 //Thread-1-->>25 //Thread-0-->>24 //Thread-0-->>26 //Thread-1-->>26 //Thread-1-->>28 //Thread-0-->>27 //Thread-0-->>29 //Thread-1-->>30 //Thread-1-->>32 //Thread-0-->>31 //Thread-1-->>33 //Thread-0-->>33 //Thread-0-->>34 //Thread-1-->>35 //Thread-0-->>36 //Thread-1-->>36 //Thread-1-->>38 //Thread-0-->>37 //Thread-0-->>39 //Thread-1-->>40 //Thread-1-->>41 //Thread-0-->>42 //Thread-0-->>43 //Thread-1-->>44 //Thread-1-->>45 //Thread-0-->>45 //Thread-0-->>46 //Thread-1-->>47 //Thread-0-->>48 //Thread-1-->>48 //Thread-1-->>49 //Thread-0-->>50 //Thread-1-->>51 //Thread-0-->>52 //Thread-0-->>53 //Thread-1-->>54 //Thread-0-->>55 //Thread-1-->>55 // //Process finished with exit code -1
Java当中提供了一个线程安全的AtomicInteger就保证了,操作的原子性。修改如下:
/** * @Auther: DaShu * @Date: 2021/9/1 22:18 * @Description: */ public class TestClass2 { public static void main(String[] args) { final MyInit myInit = new MyInit(); for (int i = 0; i < 2; i++) { new Thread(new Runnable() { public void run() { while (true){ System.out.println(Thread.currentThread().getName() + "-->>"+ myInit.getNum()); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } } }).start(); } } static class MyInit{ AtomicInteger atomicInteger = new AtomicInteger(); public int getNum(){ return atomicInteger.getAndIncrement();//先返回在加一。 } } } //Thread-0-->>0 // Thread-1-->>1 // Thread-1-->>3 // Thread-0-->>2 // Thread-0-->>4 // Thread-1-->>5 // Thread-0-->>6 // Thread-1-->>7 // Thread-1-->>8 // Thread-0-->>9 // Thread-1-->>10 // Thread-0-->>11 // Thread-0-->>13 // Thread-1-->>12 // Thread-0-->>14 // Thread-1-->>15 // Thread-1-->>16 // Thread-0-->>17 // Thread-1-->>18 // Thread-0-->>19 // Thread-0-->>20 // Thread-1-->>21 // Thread-0-->>22 // Thread-1-->>23 // Thread-1-->>24 // Thread-0-->>25 // Thread-0-->>26 // Thread-1-->>27 // Thread-1-->>28 // Thread-0-->>29 // Thread-0-->>31 // Thread-1-->>30 // Thread-1-->>32 // Thread-0-->>33 // // Process finished with exit code -1
二:可见性
1:什么是可见性
可见性是指一个线程对于共享变量的修改是否对于另外一个线程是可见的,在多线程环境中,我们一个线程对某个共享变量进行修改,修改更新之后后续的其他线程可能无法立即读取到这个更新之后的结果,这就是线程的可见性。一个线程对于共享变量更新后,后续访问该变量的线程可以读到更新的结果,我们就称这个线程对共享变量的更新对其他线程可见。否则这个线程对共享变量的更新不可见。
多线程由于可见性的问题可能会导致其他线程读取到旧数据或者脏数据,我们通过代码演示一下。
/** * @Auther: DaShu * @Date: 2021/9/1 22:18 * @Description: */ public class TestClass3 { public static void main(String[] args) { MyTask myTask = new MyTask(); new Thread(myTask).start(); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } //主线程取消子线程 myTask.cancel(); } //执行某个任务....... //执行某个任务....... //收到取消线程消息...... //任务被取消......... /** * 可能出先在main线程中调用myTask方法把task方法修改为true,可能存在 * 把子线程看不到,main线程对子线程作出的修改,子线程中的toCancle变量一值为 * false,这个多线程的执行结果是不确定的,导致子线程看不到main线程对他修改更新 * 的原因为: * 可能原因1:JTI即时编译器可能会对run方法中的while循环进行优化为:if(!toCancel){ * while(true){ * if(doSomething() ){ * * } * } * } * 编译器这样做的目的是为了减少变量的读取,但是这样可能会导致死循环的发生 * 原因二:可能与CPU的存储系统相关,假设有两个CPU内核运行main线程和子线程, * 一个内核无法立即读取另一个内核CPU中的数据,两个线程分别在不同的CPU内核上 * 运行,这两个线程的共享边量在主内存上存储,但是我们的数据是在CPU的内核的寄存器上或者 * 缓存上,这就导致了一个线程修改了数据,另外一个线程无法立即读到另外一个线程的数据。 * 这就导致了,一个线程对于共享数据的更新对于另外一个线程不可见的问题。 * */ static class MyTask implements Runnable{ private boolean toCancel = false; public void run() { while(!toCancel){ if(doSomething() ){ } } if(toCancel){ System.out.println("任务被取消........."); }else{ System.out.println("任务正常结束......."); } } private boolean doSomething() { System.out.println("执行某个任务......."); try { Thread.sleep(new Random().nextInt(1000)); } catch (InterruptedException e) { e.printStackTrace(); } return true; } public void cancel(){ toCancel = true; System.out.println("收到取消线程消息......"); } } }
三:有序性
有序性是指在还说呢么情况下一个处理器的运行的一个线程所执行的内存访问操作,咋另外一个处理器上运行的其他线程看来是乱序的。乱序是指什么呢?乱序是指内存访问操作顺序看起来仿佛发生了变化,
涉及这个乱序的时候涉及到了一个概念叫做重排序,重排序的概念是指我们在多核处理器的环境下,我们编写的程序顺序结构这这种操作他执行的顺序可能是没有保障的,就是说他执行的顺序和我们写的顺序是不一样的,编译器有可能改变两个操作的顺序,处理器上有可能不会按照目标代码的顺序执行。这种一个处理器上执行的多个操作在其他处理器来看,他的顺序与目标代码指定的顺序可能不一样,这种现象称为重排序,重排序是对内存访问操作的一种优化,在可以不影响单线程程序结果正确结果的情况下,他能够提升程序的运行性能,如果他是单线程可以起到提高程序性能的作用,但是可能会对多线程程序的正确性产生影响,可能导致线程安全问题。
重排序与可见性问题类似,不是必然出现的,重排序有可能是编译器出现了重排序,有可能是处理器进行了重排序,对于这个重排序有这么几个概念,对于内存操作顺序有挂拿的几个概念,源代码顺序就是源码中指定的内存访问顺序,程序顺序,处理器上运行的目标代码所指定的内存访问顺序,执行顺序,内存访问操作在处理器上的实际内存访问顺序,感知顺序,给定处理器所感知到的该处理器即其他处理器的内存访问操作。
我们可以把重排序分为指令重排序和存储子系统重排序两种。
指令重排序:指的是代码顺序、指令顺序、执行顺序不一致,他是由于JIT编译器或者处理器引起的。
存储子系统重排序是由高速缓存,写缓冲器引起的,感知顺序与执行顺序不一致。
1:指令重排序
在源码顺序和程序顺序不一致,或者程序顺序和执行顺序不一致的情况下,我们就说发生了执行重排序,前者是由于JIT编译器引起的,后者是由于处理器内核引起的,指令重排是一种动作,确实对指令的顺序做了调整,重排序的对象是我们代码的指令,JavaC编译器一般不会执行指令重排序,JIT编译器可能执行指令重排序,Javac编译器把Java文件编译成字节码文件,我们通过Java虚拟机运行字节码文件,Java虚拟机当中有这个JIT编译器,java虚拟机当中的JIT编译器有可能会发生这个指令重排。处理器也是可能执行这个指令重排,导致执行顺序程序顺序不一致,我们处理器对这个指令重排,我们也称为这个处理器的乱序执行,只是我们当前的处理器为了提高他的运行效率,他往往不是按照这个程序执行来执行,他的会动态提调整他的指令顺序,这就是处理器的乱序执行,我们在乱序执行的处理器当中,我们的指令按照程序顺序被处理器读取,但是我们那条指令先就绪了,就先执行那一条。指令重排不会对单线程程序结果正确性造成影响,但是会对多线程程序造成出现非预期的结果。
2:存储子系统重排序
我们的CPU处理速度很快,但是我们的主内存相对CPU来讲他的处理速度很慢,在处理器当中他有一个高速缓存的东西(一级缓存,二级缓存),他为了处理CPU运算速度和内存运算速度差异比较大的情况,在有的处理器当中还引入了这个写缓冲器的存储空间,用来写告诉缓存的一个操作,我们就把这个写缓冲器和告诉缓存合并称为存储子系统。
高速缓存是CPU中为了匹配主主内存处理速度不匹配的而设计的一种高速缓存,哪些一级缓存二级缓存说的就是这玩意。
写缓冲器用来提高写高速缓存效率的这个么一个部分,假设处理器严格按照程序顺序执行,两个内存访问操作,在存储子系统的作用下,其他处理器对这个操作的感知顺序也可能与程序顺序不一致。即这两个操作的执行顺序看起来像是发起了变化,这种现象称为存储子系统重排序,这个存储子重排序并没有重新排代码顺序,而是一种顺序的感知顺序发生了改变。没有改变代码顺序,改变的知识一种感知顺序。
存储子系统重排序并没有真正的对指令执行顺序进行调整,而是造成了一种指令顺序执行顺序发生调整的假象。存储子系统重排序对象是内存操作的结果。
从处理器角度来看,读内存就是从指定的RAM地址中加载数据,把数据加载到处理器的寄存器当中我们成为Load操作,写内存就是把数据存储到执行地址表示的RAM存储单元当,内存重排序有以下四种可能,
存储子系统重排序并没有真正的对指令执行顺序进行调整,而是造成一种指令执行顺序被调整的现象,存储子系统重排序对象是内存操作的结果,从处理器的角度来看,读内存就是从指定的RAM地址中加载数据到寄存器,称为Load(下面简称为L)操作;写内存就说把数据存储到指定的地址表示的RAM存储单元中,称为Store(下面简称为S)操作,内存重排序有以下四种可能:
LL重排序:一个处理器先后执行两个读操作L1和L2,其他处理器对两个内存操作的感知顺序可能是L2->L1
SS重排序:一个处理器先后执行两个写操作W1和W2,其他处理器对两个内存操作的感知顺序可能是W2->W1
LS重排序:一个处理器先执行读内存操作L1,再执行写内存操作W1,其他处理器对两个内存操作的感知顺序可能是W1->L1
SL重排序:一个处理器先执行写内存操作,再执行读内存操作L1,其他处理器对两个内存操作的感知顺序可能是L1->W1
内存重排序与具体的处理器微架构有关,不同的架构处理器与所允许的内存重排序不同。内存重排序可能会导致线程安全问题。加油有两个共享变量 int data = 0
boolean ready = false;现在又两个处理器,
现在有这个高速缓冲器,CPU把数据写入到这个高速缓冲区之后,另一个处理器没有读取到,结果就造成了一个死循环就永远停不下来,所以永远都不会停下来。这就是个存储子系统造成了线程安全问题。
3:貌似串行语义
我们的编译器或者处理器或者存储子系统是按照一定的规则给代码进行重排序,给我们一个假象是按照我们的代码逻辑运行的,这是对于单线程来讲,这种假象是貌似串行语义。这种假象对于单线程来讲, 是可以保证代码的运行顺序的,但是对于多线程来讲是不能保证多线程环境下程序的正确性。那么我们保证这个貌似穿行语义,有数据依赖关系的语句不会进行重排序。只有不存在数据依赖关系的语句才会被重排序。
一个操作依赖上一个操作的结果就是有数据依赖关系。例如:x = 1,y = x + 1;就是如此。如果不存在数据依赖关系则可能发生重排序。如果if语句汇总允许重排,可能存在处理器先执行if代码块,在判断if条件是否成立,处于性能的考虑。
4:保证内存访问的顺序
硬件和软件都可能导致执行顺序和源码顺序不一致,怎么避免重排序导致的感知顺序和源码顺序一致呢?我们从底层的角度来讲,在Java来讲,我们可以使用一些关键字Volative关键字,synchronize关键字实现有序性。
四:Java内存模型
Java虚拟机包含栈区和堆区,每个线程还能够都有自己独立的线程栈,方法区和本地方法区,每一个此案城都有自己的线程栈,在线程自己的线程栈当中,有每个方法的独立的空间,然后方法的内部还有局部变量,在堆区当中创建一个对象,每个线程中的栈空间的方法都可以访问对象,这是Java虚拟机简易的内存模型,最终这些命令都会交给计算机来执行,右边的图代表计算机,计算机包含CPU、CPU有一块自己的区域叫做寄存器,CPU的这个寄存器,然后CPU还有一块区域叫做Cache缓存,这个缓存包含一级缓存,二级缓存,CPU直接从寄存器中取数据,寄存器可以读写数据从缓存中,缓存中的数据是从主内存中读取,换存中的数据来自于主内存,主内存中的数据来自于硬盘。当然我们可能有多个CPU,每个CPU都有自己的寄存器和换粗,Java虚拟机中的内存数据可能直接分配到某核CPU的寄存器中也可能读取到寄存器的缓存中,也可能读取到内存当中,这么一个图。一个CPU不能读取到另一个CPU的寄存器和缓存中的内容。如果两个线程运行在不同的CPU上,那么共享变量被分配到某个寄存器上只有,就会出现可见性的问题,即使我们把Java虚拟机中的共享变量分配到主内存当中也不能保证数据的可见性,这是因为处理器不直接对主内存访问,而是通过Cache高速缓存进行的,这个地方涉及到一个知识点,如果一个处理器上运行的线程对数据的更新可能只是更新到处理器的写缓冲器中,也就是我们的CPU运行的线程对共享数据进行操作,把更新的数据放到写缓冲器当中还没有到达cache换存,更不是说主内存中,一个处理器不能读取另外一个处理器的写缓存,这就出现了一个问题,运行在另外一个处理器上的线程无法看到该处理器对共享数据的更新,这也会出现不可见性问题。一个处理器的高速缓存不能直接读取另一个处理器的Cache但是,但是一个处理器可以通过缓存一致性协议来读取其他处理器缓存中的数据,并将督导的数据更新到该处理器的cache当中。基于这个协议的数据同步我们成为缓存同步,这个缓存同步使得一个处理器或者一个处理器上运行的线程可以读取到另一个处理器上的运行的线程的对共享数据所做的更新,可以保障可见性,为了保障可见性,我们必须使得一个数据对共享变量所做的修改,最终必须写入到这个处理器的高速缓存当中,而不是一值停留在这个写缓冲器当中,这个过程成为冲刷处理器缓存
第三章:线程同步
一:线程同步机制
线程同步机制就是用于协调多线程程序线程与线程之间的数据同步访问的这样的一个机制,该机制可以保证线程安全,Java平台提供的线程同步机制包括:锁、volatile关键字,final关键字、static关键字、以及相关的Api如Object.wait()和Object.notiry()方法等。
二:锁概述
线程安全问题产生的愿意在于多个线程并发访问共享数据,我们的策略就是把多个线程并发访问共享数据的方法改为串行操作,即共享数据一次只能被一个线程拥有访问和修改,这个线程访问完毕之后其他的线程才可以进行访问和修改,这样的话才不会产生线程安全问题,锁就是利用这个思路保障线程安全。
锁(Lock)可以理解为对共享数据进行保护的一个许可证,对于同一个许可证保护的共享数据来说,任何线程巷一号访问这些共享数据必须先持有该许可证,一个线程只有在持有许可证的前提下才能够对共享数据进行访问,并且一个许可证一次只能被一个线程持有。许可证持有的线程在结束对共享数据的访问后必须释放其持有的许可证。
一线程共享数据前必须获取锁,获得锁的线程称为锁的持有线程,一个锁一次只能被一个线程持有,锁的持有线程在获取锁之后释放锁之前执行的代码称为临界区,锁因为只能被一个线程持有所以锁具有排他性,即锁一次只能被一个线程持有,这样的锁我们成为排它锁,或者叫互斥锁。
Java虚拟机把锁分为内部锁和显示锁两种,这个内部锁通过synchronized关键字,显示锁通过Java.concurrent.locks.Lock接口的实现类实现。
锁的理念就是一种并行转串行的思想。
三:内部锁synchronize关键字
内部锁是通过这个synchronize关键字实现的,Java中的每个对象都有一个与他关联的内部锁,这种锁也称为监视器,有人也称为monitor,每个对象都有,每个对象都有,这个内部锁他是一个排它锁,可以保障原子性可见性与有序性。内部锁是通过synchronize关键字实现的,这个synChronize可以修饰代码块,可以修饰方法,
1:synchronize同步代码块
/** * @Auther: DaShu * @Date: 2021/9/10 19:28 * @Description: */ public class TestClass { public static void main(String[] args) { final TestClass testClass = new TestClass(); new Thread(new Runnable() { public void run() { testClass.mm();//这里的锁对象指的是testclass对象,指的是this当前对象。 } }).start(); new Thread(new Runnable() { public void run() { testClass.mm();//这里的锁对象指的是testclass对象,指的是this当前对象。 } }).start(); } public void mm(){ synchronized (this){//我们经常使用当前对象使用锁对象。进行同步,必须使用同一个锁对象。 for (int i= 0; i < 100; i++) { System.out.println(Thread.currentThread().getName()+"--->>>"+i); } } } }
这样就实现了同步,一个线程执行完之后释放了锁对象,其他线程才可以过来继续执行这个线程。
2:锁对象不一致不能实现同步
/** * @Auther: DaShu * @Date: 2021/9/10 19:28 * @Description: */ public class TestClass2 { public static void main(String[] args) { final TestClass2 testClass = new TestClass2(); final TestClass2 testClass2 = new TestClass2(); new Thread(new Runnable() { public void run() { testClass.mm();//这里的锁对象指的是testclass对象,指的是this当前对象。 } }).start(); new Thread(new Runnable() { public void run() { testClass2.mm();//这里的锁对象指的是testclass2对象,指的是this当前对象。 } }).start(); } public void mm(){ synchronized (this){//锁对象不一致,不能实现同步,打印肯定有交叉 for (int i= 0; i < 100; i++) { System.out.println(Thread.currentThread().getName()+"--->>>"+i); } } } //Thread-0--->>>0 //Thread-0--->>>1 //Thread-0--->>>2 //Thread-0--->>>3 //Thread-0--->>>4 //Thread-0--->>>5 //Thread-0--->>>6 //Thread-0--->>>7 //Thread-0--->>>8 //Thread-0--->>>9 //Thread-0--->>>10 //Thread-0--->>>11 //Thread-0--->>>12 //Thread-0--->>>13 //Thread-0--->>>14 //Thread-0--->>>15 //Thread-0--->>>16 //Thread-0--->>>17 //Thread-0--->>>18 //Thread-0--->>>19 //Thread-0--->>>20 //Thread-0--->>>21 //Thread-0--->>>22 //Thread-0--->>>23 //Thread-0--->>>24 //Thread-0--->>>25 //Thread-0--->>>26 //Thread-0--->>>27 //Thread-0--->>>28 //Thread-0--->>>29 //Thread-0--->>>30 //Thread-0--->>>31 //Thread-0--->>>32 //Thread-0--->>>33 //Thread-0--->>>34 //Thread-0--->>>35 //Thread-0--->>>36 //Thread-0--->>>37 //Thread-0--->>>38 //Thread-1--->>>0 //Thread-1--->>>1 //Thread-1--->>>2 //Thread-0--->>>39 //Thread-1--->>>3 //Thread-1--->>>4 //Thread-1--->>>5 //Thread-1--->>>6 //Thread-1--->>>7 //Thread-1--->>>8 //Thread-1--->>>9 //Thread-1--->>>10 //Thread-1--->>>11 //Thread-1--->>>12 //Thread-1--->>>13 //Thread-1--->>>14 //Thread-1--->>>15 //Thread-1--->>>16 //Thread-1--->>>17 //Thread-1--->>>18 //Thread-1--->>>19 //Thread-1--->>>20 //Thread-1--->>>21 //Thread-1--->>>22 //Thread-1--->>>23 //Thread-1--->>>24 //Thread-1--->>>25 //Thread-1--->>>26 //Thread-1--->>>27 //Thread-1--->>>28 //Thread-1--->>>29 //Thread-1--->>>30 //Thread-1--->>>31 //Thread-1--->>>32 //Thread-1--->>>33 //Thread-1--->>>34 //Thread-1--->>>35 //Thread-1--->>>36 //Thread-1--->>>37 //Thread-1--->>>38 //Thread-1--->>>39 //Thread-1--->>>40 //Thread-1--->>>41 //Thread-1--->>>42 //Thread-1--->>>43 //Thread-1--->>>44 //Thread-1--->>>45 //Thread-1--->>>46 //Thread-1--->>>47 //Thread-1--->>>48 //Thread-1--->>>49 //Thread-1--->>>50 //Thread-1--->>>51 //Thread-1--->>>52 //Thread-1--->>>53 //Thread-1--->>>54 //Thread-1--->>>55 //Thread-1--->>>56 //Thread-1--->>>57 //Thread-1--->>>58 //Thread-1--->>>59 //Thread-1--->>>60 //Thread-1--->>>61 //Thread-1--->>>62 //Thread-1--->>>63 //Thread-1--->>>64 //Thread-1--->>>65 //Thread-1--->>>66 //Thread-1--->>>67 //Thread-1--->>>68 //Thread-1--->>>69 //Thread-1--->>>70 //Thread-1--->>>71 //Thread-1--->>>72 //Thread-1--->>>73 //Thread-1--->>>74 //Thread-1--->>>75 //Thread-1--->>>76 //Thread-1--->>>77 //Thread-1--->>>78 //Thread-1--->>>79 //Thread-1--->>>80 //Thread-1--->>>81 //Thread-1--->>>82 //Thread-1--->>>83 //Thread-1--->>>84 //Thread-1--->>>85 //Thread-1--->>>86 //Thread-1--->>>87 //Thread-1--->>>88 //Thread-1--->>>89 //Thread-1--->>>90 //Thread-1--->>>91 //Thread-1--->>>92 //Thread-1--->>>93 //Thread-1--->>>94 //Thread-1--->>>95 //Thread-1--->>>96 //Thread-1--->>>97 //Thread-1--->>>98 //Thread-1--->>>99 //Thread-0--->>>40 //Thread-0--->>>41 //Thread-0--->>>42 //Thread-0--->>>43 //Thread-0--->>>44 //Thread-0--->>>45 //Thread-0--->>>46 //Thread-0--->>>47 //Thread-0--->>>48 //Thread-0--->>>49 //Thread-0--->>>50 //Thread-0--->>>51 //Thread-0--->>>52 //Thread-0--->>>53 //Thread-0--->>>54 //Thread-0--->>>55 //Thread-0--->>>56 //Thread-0--->>>57 //Thread-0--->>>58 //Thread-0--->>>59 //Thread-0--->>>60 //Thread-0--->>>61 //Thread-0--->>>62 //Thread-0--->>>63 //Thread-0--->>>64 //Thread-0--->>>65 //Thread-0--->>>66 //Thread-0--->>>67 //Thread-0--->>>68 //Thread-0--->>>69 //Thread-0--->>>70 //Thread-0--->>>71 //Thread-0--->>>72 //Thread-0--->>>73 //Thread-0--->>>74 //Thread-0--->>>75 //Thread-0--->>>76 //Thread-0--->>>77 //Thread-0--->>>78 //Thread-0--->>>79 //Thread-0--->>>80 //Thread-0--->>>81 //Thread-0--->>>82 //Thread-0--->>>83 //Thread-0--->>>84 //Thread-0--->>>85 //Thread-0--->>>86 //Thread-0--->>>87 //Thread-0--->>>88 //Thread-0--->>>89 //Thread-0--->>>90 //Thread-0--->>>91 //Thread-0--->>>92 //Thread-0--->>>93 //Thread-0--->>>94 //Thread-0--->>>95 //Thread-0--->>>96 //Thread-0--->>>97 //Thread-0--->>>98 //Thread-0--->>>99 }
如果线程的锁不同不能实现同步,如果想实现同步,必须得是同一把锁。因为这个是实例方法,这个锁对象this是调用该方法的对象。
3:使用常量作为锁对象
public class TestClass3 { public static void main(String[] args) { final TestClass3 testClass = new TestClass3(); final TestClass3 testClass2 = new TestClass3(); new Thread(new Runnable() { public void run() { testClass.mm();//这里的锁对象是常量 } }).start(); new Thread(new Runnable() { public void run() { testClass2.mm();//这里的锁对象是常量 } }).start(); } public static final Object OBJ = new Object(); public void mm() { synchronized (OBJ) { for (int i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName() + "--->>>" + i); } } } } //Thread-0--->>>0 //Thread-0--->>>1 //Thread-0--->>>2 //Thread-0--->>>3 //Thread-0--->>>4 //Thread-0--->>>5 //Thread-0--->>>6 //Thread-0--->>>7 //Thread-0--->>>8 //Thread-0--->>>9 //Thread-0--->>>10 //Thread-0--->>>11 //Thread-0--->>>12 //Thread-0--->>>13 //Thread-0--->>>14 //Thread-0--->>>15 //Thread-0--->>>16 //Thread-0--->>>17 //Thread-0--->>>18 //Thread-0--->>>19 //Thread-0--->>>20 //Thread-0--->>>21 //Thread-0--->>>22 //Thread-0--->>>23 //Thread-0--->>>24 //Thread-0--->>>25 //Thread-0--->>>26 //Thread-0--->>>27 //Thread-0--->>>28 //Thread-0--->>>29 //Thread-0--->>>30 //Thread-0--->>>31 //Thread-0--->>>32 //Thread-0--->>>33 //Thread-0--->>>34 //Thread-0--->>>35 //Thread-0--->>>36 //Thread-0--->>>37 //Thread-0--->>>38 //Thread-0--->>>39 //Thread-0--->>>40 //Thread-0--->>>41 //Thread-0--->>>42 //Thread-0--->>>43 //Thread-0--->>>44 //Thread-0--->>>45 //Thread-0--->>>46 //Thread-0--->>>47 //Thread-0--->>>48 //Thread-0--->>>49 //Thread-0--->>>50 //Thread-0--->>>51 //Thread-0--->>>52 //Thread-0--->>>53 //Thread-0--->>>54 //Thread-0--->>>55 //Thread-0--->>>56 //Thread-0--->>>57 //Thread-0--->>>58 //Thread-0--->>>59 //Thread-0--->>>60 //Thread-0--->>>61 //Thread-0--->>>62 //Thread-0--->>>63 //Thread-0--->>>64 //Thread-0--->>>65 //Thread-0--->>>66 //Thread-0--->>>67 //Thread-0--->>>68 //Thread-0--->>>69 //Thread-0--->>>70 //Thread-0--->>>71 //Thread-0--->>>72 //Thread-0--->>>73 //Thread-0--->>>74 //Thread-0--->>>75 //Thread-0--->>>76 //Thread-0--->>>77 //Thread-0--->>>78 //Thread-0--->>>79 //Thread-0--->>>80 //Thread-0--->>>81 //Thread-0--->>>82 //Thread-0--->>>83 //Thread-0--->>>84 //Thread-0--->>>85 //Thread-0--->>>86 //Thread-0--->>>87 //Thread-0--->>>88 //Thread-0--->>>89 //Thread-0--->>>90 //Thread-0--->>>91 //Thread-0--->>>92 //Thread-0--->>>93 //Thread-0--->>>94 //Thread-0--->>>95 //Thread-0--->>>96 //Thread-0--->>>97 //Thread-0--->>>98 //Thread-0--->>>99 //Thread-1--->>>0 //Thread-1--->>>1 //Thread-1--->>>2 //Thread-1--->>>3 //Thread-1--->>>4 //Thread-1--->>>5 //Thread-1--->>>6 //Thread-1--->>>7 //Thread-1--->>>8 //Thread-1--->>>9 //Thread-1--->>>10 //Thread-1--->>>11 //Thread-1--->>>12 //Thread-1--->>>13 //Thread-1--->>>14 //Thread-1--->>>15 //Thread-1--->>>16 //Thread-1--->>>17 //Thread-1--->>>18 //Thread-1--->>>19 //Thread-1--->>>20 //Thread-1--->>>21 //Thread-1--->>>22 //Thread-1--->>>23 //Thread-1--->>>24 //Thread-1--->>>25 //Thread-1--->>>26 //Thread-1--->>>27 //Thread-1--->>>28 //Thread-1--->>>29 //Thread-1--->>>30 //Thread-1--->>>31 //Thread-1--->>>32 //Thread-1--->>>33 //Thread-1--->>>34 //Thread-1--->>>35 //Thread-1--->>>36 //Thread-1--->>>37 //Thread-1--->>>38 //Thread-1--->>>39 //Thread-1--->>>40 //Thread-1--->>>41 //Thread-1--->>>42 //Thread-1--->>>43 //Thread-1--->>>44 //Thread-1--->>>45 //Thread-1--->>>46 //Thread-1--->>>47 //Thread-1--->>>48 //Thread-1--->>>49 //Thread-1--->>>50 //Thread-1--->>>51 //Thread-1--->>>52 //Thread-1--->>>53 //Thread-1--->>>54 //Thread-1--->>>55 //Thread-1--->>>56 //Thread-1--->>>57 //Thread-1--->>>58 //Thread-1--->>>59 //Thread-1--->>>60 //Thread-1--->>>61 //Thread-1--->>>62 //Thread-1--->>>63 //Thread-1--->>>64 //Thread-1--->>>65 //Thread-1--->>>66 //Thread-1--->>>67 //Thread-1--->>>68 //Thread-1--->>>69 //Thread-1--->>>70 //Thread-1--->>>71 //Thread-1--->>>72 //Thread-1--->>>73 //Thread-1--->>>74 //Thread-1--->>>75 //Thread-1--->>>76 //Thread-1--->>>77 //Thread-1--->>>78 //Thread-1--->>>79 //Thread-1--->>>80 //Thread-1--->>>81 //Thread-1--->>>82 //Thread-1--->>>83 //Thread-1--->>>84 //Thread-1--->>>85 //Thread-1--->>>86 //Thread-1--->>>87 //Thread-1--->>>88 //Thread-1--->>>89 //Thread-1--->>>90 //Thread-1--->>>91 //Thread-1--->>>92 //Thread-1--->>>93 //Thread-1--->>>94 //Thread-1--->>>95 //Thread-1--->>>96 //Thread-1--->>>97 //Thread-1--->>>98 //Thread-1--->>>99
使用常亮作为锁对象的话,所有的线程都能获取到这个常量,所以是同步的。不同的线程使用常量(同一个锁对象)锁住不同的共享资源,这样也能实现线程之前的同步,也就是说,只要是一个线程拿着锁锁住了某个共享资源,另一个线程即使想访问这个锁锁住的其他的共享资源也必须等待,
/** * @Auther: DaShu * @Date: 2021/9/10 19:28 * @Description: */ public class TestClass4 { public static void main(String[] args) { final TestClass4 testClass = new TestClass4(); final TestClass4 testClass2 = new TestClass4(); new Thread(new Runnable() { public void run() { testClass.mm();//这里的锁对象是常量obj } }).start(); new Thread(new Runnable() { public void run() { testClass2.mm();//这里的锁对象是常量obj } }).start(); new Thread(new Runnable() { public void run() { TestClass4.sm();//这里的锁对象是常量obj } }).start(); } public static final Object OBJ = new Object(); public void mm() { synchronized (OBJ) { for (int i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName() + "--->>>" + i); } } } public static void sm() { synchronized (OBJ) { for (int i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName() + "--->>>" + i); } } } } //Thread-0--->>>0 //Thread-0--->>>1 //Thread-0--->>>2 //Thread-0--->>>3 //Thread-0--->>>4 //Thread-0--->>>5 //Thread-0--->>>6 //Thread-0--->>>7 //Thread-0--->>>8 //Thread-0--->>>9 //Thread-0--->>>10 //Thread-0--->>>11 //Thread-0--->>>12 //Thread-0--->>>13 //Thread-0--->>>14 //Thread-0--->>>15 //Thread-0--->>>16 //Thread-0--->>>17 //Thread-0--->>>18 //Thread-0--->>>19 //Thread-0--->>>20 //Thread-0--->>>21 //Thread-0--->>>22 //Thread-0--->>>23 //Thread-0--->>>24 //Thread-0--->>>25 //Thread-0--->>>26 //Thread-0--->>>27 //Thread-0--->>>28 //Thread-0--->>>29 //Thread-0--->>>30 //Thread-0--->>>31 //Thread-0--->>>32 //Thread-0--->>>33 //Thread-0--->>>34 //Thread-0--->>>35 //Thread-0--->>>36 //Thread-0--->>>37 //Thread-0--->>>38 //Thread-0--->>>39 //Thread-0--->>>40 //Thread-0--->>>41 //Thread-0--->>>42 //Thread-0--->>>43 //Thread-0--->>>44 //Thread-0--->>>45 //Thread-0--->>>46 //Thread-0--->>>47 //Thread-0--->>>48 //Thread-0--->>>49 //Thread-0--->>>50 //Thread-0--->>>51 //Thread-0--->>>52 //Thread-0--->>>53 //Thread-0--->>>54 //Thread-0--->>>55 //Thread-0--->>>56 //Thread-0--->>>57 //Thread-0--->>>58 //Thread-0--->>>59 //Thread-0--->>>60 //Thread-0--->>>61 //Thread-0--->>>62 //Thread-0--->>>63 //Thread-0--->>>64 //Thread-0--->>>65 //Thread-0--->>>66 //Thread-0--->>>67 //Thread-0--->>>68 //Thread-0--->>>69 //Thread-0--->>>70 //Thread-0--->>>71 //Thread-0--->>>72 //Thread-0--->>>73 //Thread-0--->>>74 //Thread-0--->>>75 //Thread-0--->>>76 //Thread-0--->>>77 //Thread-0--->>>78 //Thread-0--->>>79 //Thread-0--->>>80 //Thread-0--->>>81 //Thread-0--->>>82 //Thread-0--->>>83 //Thread-0--->>>84 //Thread-0--->>>85 //Thread-0--->>>86 //Thread-0--->>>87 //Thread-0--->>>88 //Thread-0--->>>89 //Thread-0--->>>90 //Thread-0--->>>91 //Thread-0--->>>92 //Thread-0--->>>93 //Thread-0--->>>94 //Thread-0--->>>95 //Thread-0--->>>96 //Thread-0--->>>97 //Thread-0--->>>98 //Thread-0--->>>99 //Thread-2--->>>0 //Thread-2--->>>1 //Thread-2--->>>2 //Thread-2--->>>3 //Thread-2--->>>4 //Thread-2--->>>5 //Thread-2--->>>6 //Thread-2--->>>7 //Thread-2--->>>8 //Thread-2--->>>9 //Thread-2--->>>10 //Thread-2--->>>11 //Thread-2--->>>12 //Thread-2--->>>13 //Thread-2--->>>14 //Thread-2--->>>15 //Thread-2--->>>16 //Thread-2--->>>17 //Thread-2--->>>18 //Thread-2--->>>19 //Thread-2--->>>20 //Thread-2--->>>21 //Thread-2--->>>22 //Thread-2--->>>23 //Thread-2--->>>24 //Thread-2--->>>25 //Thread-2--->>>26 //Thread-2--->>>27 //Thread-2--->>>28 //Thread-2--->>>29 //Thread-2--->>>30 //Thread-2--->>>31 //Thread-2--->>>32 //Thread-2--->>>33 //Thread-2--->>>34 //Thread-2--->>>35 //Thread-2--->>>36 //Thread-2--->>>37 //Thread-2--->>>38 //Thread-2--->>>39 //Thread-2--->>>40 //Thread-2--->>>41 //Thread-2--->>>42 //Thread-2--->>>43 //Thread-2--->>>44 //Thread-2--->>>45 //Thread-2--->>>46 //Thread-2--->>>47 //Thread-2--->>>48 //Thread-2--->>>49 //Thread-2--->>>50 //Thread-2--->>>51 //Thread-2--->>>52 //Thread-2--->>>53 //Thread-2--->>>54 //Thread-2--->>>55 //Thread-2--->>>56 //Thread-2--->>>57 //Thread-2--->>>58 //Thread-2--->>>59 //Thread-2--->>>60 //Thread-2--->>>61 //Thread-2--->>>62 //Thread-2--->>>63 //Thread-2--->>>64 //Thread-2--->>>65 //Thread-2--->>>66 //Thread-2--->>>67 //Thread-2--->>>68 //Thread-2--->>>69 //Thread-2--->>>70 //Thread-2--->>>71 //Thread-2--->>>72 //Thread-2--->>>73 //Thread-2--->>>74 //Thread-2--->>>75 //Thread-2--->>>76 //Thread-2--->>>77 //Thread-2--->>>78 //Thread-2--->>>79 //Thread-2--->>>80 //Thread-2--->>>81 //Thread-2--->>>82 //Thread-2--->>>83 //Thread-2--->>>84 //Thread-2--->>>85 //Thread-2--->>>86 //Thread-2--->>>87 //Thread-2--->>>88 //Thread-2--->>>89 //Thread-2--->>>90 //Thread-2--->>>91 //Thread-2--->>>92 //Thread-2--->>>93 //Thread-2--->>>94 //Thread-2--->>>95 //Thread-2--->>>96 //Thread-2--->>>97 //Thread-2--->>>98 //Thread-2--->>>99 //Thread-1--->>>0 //Thread-1--->>>1 //Thread-1--->>>2 //Thread-1--->>>3 //Thread-1--->>>4 //Thread-1--->>>5 //Thread-1--->>>6 //Thread-1--->>>7 //Thread-1--->>>8 //Thread-1--->>>9 //Thread-1--->>>10 //Thread-1--->>>11 //Thread-1--->>>12 //Thread-1--->>>13 //Thread-1--->>>14 //Thread-1--->>>15 //Thread-1--->>>16 //Thread-1--->>>17 //Thread-1--->>>18 //Thread-1--->>>19 //Thread-1--->>>20 //Thread-1--->>>21 //Thread-1--->>>22 //Thread-1--->>>23 //Thread-1--->>>24 //Thread-1--->>>25 //Thread-1--->>>26 //Thread-1--->>>27 //Thread-1--->>>28 //Thread-1--->>>29 //Thread-1--->>>30 //Thread-1--->>>31 //Thread-1--->>>32 //Thread-1--->>>33 //Thread-1--->>>34 //Thread-1--->>>35 //Thread-1--->>>36 //Thread-1--->>>37 //Thread-1--->>>38 //Thread-1--->>>39 //Thread-1--->>>40 //Thread-1--->>>41 //Thread-1--->>>42 //Thread-1--->>>43 //Thread-1--->>>44 //Thread-1--->>>45 //Thread-1--->>>46 //Thread-1--->>>47 //Thread-1--->>>48 //Thread-1--->>>49 //Thread-1--->>>50 //Thread-1--->>>51 //Thread-1--->>>52 //Thread-1--->>>53 //Thread-1--->>>54 //Thread-1--->>>55 //Thread-1--->>>56 //Thread-1--->>>57 //Thread-1--->>>58 //Thread-1--->>>59 //Thread-1--->>>60 //Thread-1--->>>61 //Thread-1--->>>62 //Thread-1--->>>63 //Thread-1--->>>64 //Thread-1--->>>65 //Thread-1--->>>66 //Thread-1--->>>67 //Thread-1--->>>68 //Thread-1--->>>69 //Thread-1--->>>70 //Thread-1--->>>71 //Thread-1--->>>72 //Thread-1--->>>73 //Thread-1--->>>74 //Thread-1--->>>75 //Thread-1--->>>76 //Thread-1--->>>77 //Thread-1--->>>78 //Thread-1--->>>79 //Thread-1--->>>80 //Thread-1--->>>81 //Thread-1--->>>82 //Thread-1--->>>83 //Thread-1--->>>84 //Thread-1--->>>85 //Thread-1--->>>86 //Thread-1--->>>87 //Thread-1--->>>88 //Thread-1--->>>89 //Thread-1--->>>90 //Thread-1--->>>91 //Thread-1--->>>92 //Thread-1--->>>93 //Thread-1--->>>94 //Thread-1--->>>95 //Thread-1--->>>96 //Thread-1--->>>97 //Thread-1--->>>98 //Thread-1--->>>99
4:使用常量作为锁对象
public class TestClass5 { public static void main(String[] args) { final TestClass5 testClass = new TestClass5(); final TestClass5 testClass2 = new TestClass5(); //创建线程一个调用mm,一个调用mm22 new Thread(new Runnable() { public void run() { testClass.mm();//这里的锁对象是常量obj } }).start(); new Thread(new Runnable() { public void run() { testClass.mm();//这里的锁对象是常量obj } }).start(); } public void mm() { synchronized (this) { for (int i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName() + "--->>>" + i); } } } //这个就叫同步实例方法。使用关键字修饰实例方法,就是同步实例方法,他默认的锁对象是this。 //我现在有一个mm方法,有一个mm22方法, public synchronized void mm22() { for (int i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName() + "--->>>" + i); } } } //Thread-1--->>>0 //Thread-1--->>>1 //Thread-1--->>>2 //Thread-1--->>>3 //Thread-1--->>>4 //Thread-1--->>>5 //Thread-1--->>>6 //Thread-1--->>>7 //Thread-1--->>>8 //Thread-1--->>>9 //Thread-1--->>>10 //Thread-1--->>>11 //Thread-1--->>>12 //Thread-1--->>>13 //Thread-1--->>>14 //Thread-1--->>>15 //Thread-1--->>>16 //Thread-1--->>>17 //Thread-1--->>>18 //Thread-1--->>>19 //Thread-1--->>>20 //Thread-1--->>>21 //Thread-1--->>>22 //Thread-1--->>>23 //Thread-1--->>>24 //Thread-1--->>>25 //Thread-1--->>>26 //Thread-1--->>>27 //Thread-1--->>>28 //Thread-1--->>>29 //Thread-1--->>>30 //Thread-1--->>>31 //Thread-1--->>>32 //Thread-1--->>>33 //Thread-1--->>>34 //Thread-1--->>>35 //Thread-1--->>>36 //Thread-1--->>>37 //Thread-1--->>>38 //Thread-1--->>>39 //Thread-1--->>>40 //Thread-1--->>>41 //Thread-1--->>>42 //Thread-1--->>>43 //Thread-1--->>>44 //Thread-1--->>>45 //Thread-1--->>>46 //Thread-1--->>>47 //Thread-1--->>>48 //Thread-1--->>>49 //Thread-1--->>>50 //Thread-1--->>>51 //Thread-1--->>>52 //Thread-1--->>>53 //Thread-1--->>>54 //Thread-1--->>>55 //Thread-1--->>>56 //Thread-1--->>>57 //Thread-1--->>>58 //Thread-1--->>>59 //Thread-1--->>>60 //Thread-1--->>>61 //Thread-1--->>>62 //Thread-1--->>>63 //Thread-1--->>>64 //Thread-1--->>>65 //Thread-1--->>>66 //Thread-1--->>>67 //Thread-1--->>>68 //Thread-1--->>>69 //Thread-1--->>>70 //Thread-1--->>>71 //Thread-1--->>>72 //Thread-1--->>>73 //Thread-1--->>>74 //Thread-1--->>>75 //Thread-1--->>>76 //Thread-1--->>>77 //Thread-1--->>>78 //Thread-1--->>>79 //Thread-1--->>>80 //Thread-1--->>>81 //Thread-1--->>>82 //Thread-1--->>>83 //Thread-1--->>>84 //Thread-1--->>>85 //Thread-1--->>>86 //Thread-1--->>>87 //Thread-1--->>>88 //Thread-1--->>>89 //Thread-1--->>>90 //Thread-1--->>>91 //Thread-1--->>>92 //Thread-1--->>>93 //Thread-1--->>>94 //Thread-1--->>>95 //Thread-1--->>>96 //Thread-1--->>>97 //Thread-1--->>>98 //Thread-1--->>>99 //Thread-0--->>>0 //Thread-0--->>>1 //Thread-0--->>>2 //Thread-0--->>>3 //Thread-0--->>>4 //Thread-0--->>>5 //Thread-0--->>>6 //Thread-0--->>>7 //Thread-0--->>>8 //Thread-0--->>>9 //Thread-0--->>>10 //Thread-0--->>>11 //Thread-0--->>>12 //Thread-0--->>>13 //Thread-0--->>>14 //Thread-0--->>>15 //Thread-0--->>>16 //Thread-0--->>>17 //Thread-0--->>>18 //Thread-0--->>>19 //Thread-0--->>>20 //Thread-0--->>>21 //Thread-0--->>>22 //Thread-0--->>>23 //Thread-0--->>>24 //Thread-0--->>>25 //Thread-0--->>>26 //Thread-0--->>>27 //Thread-0--->>>28 //Thread-0--->>>29 //Thread-0--->>>30 //Thread-0--->>>31 //Thread-0--->>>32 //Thread-0--->>>33 //Thread-0--->>>34 //Thread-0--->>>35 //Thread-0--->>>36 //Thread-0--->>>37 //Thread-0--->>>38 //Thread-0--->>>39 //Thread-0--->>>40 //Thread-0--->>>41 //Thread-0--->>>42 //Thread-0--->>>43 //Thread-0--->>>44 //Thread-0--->>>45 //Thread-0--->>>46 //Thread-0--->>>47 //Thread-0--->>>48 //Thread-0--->>>49 //Thread-0--->>>50 //Thread-0--->>>51 //Thread-0--->>>52 //Thread-0--->>>53 //Thread-0--->>>54 //Thread-0--->>>55 //Thread-0--->>>56 //Thread-0--->>>57 //Thread-0--->>>58 //Thread-0--->>>59 //Thread-0--->>>60 //Thread-0--->>>61 //Thread-0--->>>62 //Thread-0--->>>63 //Thread-0--->>>64 //Thread-0--->>>65 //Thread-0--->>>66 //Thread-0--->>>67 //Thread-0--->>>68 //Thread-0--->>>69 //Thread-0--->>>70 //Thread-0--->>>71 //Thread-0--->>>72 //Thread-0--->>>73 //Thread-0--->>>74 //Thread-0--->>>75 //Thread-0--->>>76 //Thread-0--->>>77 //Thread-0--->>>78 //Thread-0--->>>79 //Thread-0--->>>80 //Thread-0--->>>81 //Thread-0--->>>82 //Thread-0--->>>83 //Thread-0--->>>84 //Thread-0--->>>85 //Thread-0--->>>86 //Thread-0--->>>87 //Thread-0--->>>88 //Thread-0--->>>89 //Thread-0--->>>90 //Thread-0--->>>91 //Thread-0--->>>92 //Thread-0--->>>93 //Thread-0--->>>94 //Thread-0--->>>95 //Thread-0--->>>96 //Thread-0--->>>97 //Thread-0--->>>98 //Thread-0--->>>99
5:使用synchronized修饰实例方法
实例方法中,不同的方法都是使用this对象作为锁对象,这样当执行的时候,可以实现线程同步,在同一个实例对象当中的两个实例方法,一个实例方法被synchronized修饰方法,一个方法内部部分方法体被synchronized锁住,这样的话因为是同一个锁对象也能实现线程的同步。
/** * @Auther: DaShu * @Date: 2021/9/10 19:28 * @Description: * 使用synchronize实例方法的是将整个方法作为一个共享资源。整个方法体作为同步代码快。默认的锁对象是this对象 */ public class TestClass5 { public static void main(String[] args) { final TestClass5 testClass = new TestClass5(); final TestClass5 testClass2 = new TestClass5(); //创建线程一个调用mm,一个调用mm22 new Thread(new Runnable() { public void run() { testClass.mm();//这里的锁对象是常量obj } }).start(); new Thread(new Runnable() { public void run() { TestClass6.mm22();//这里的锁对象是常量obj } }).start(); } public void mm() { synchronized (this) { for (int i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName() + "--->>>" + i); } } } //这个就叫同步实例方法。使用关键字修饰实例方法,就是同步实例方法,他默认的锁对象是this。 //我现在有一个mm方法,有一个mm22方法, public synchronized void mm22() { for (int i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName() + "--->>>" + i); } } } //Thread-1--->>>0 //Thread-1--->>>1 //Thread-1--->>>2 //Thread-1--->>>3 //Thread-1--->>>4 //Thread-1--->>>5 //Thread-1--->>>6 //Thread-1--->>>7 //Thread-1--->>>8 //Thread-1--->>>9 //Thread-1--->>>10 //Thread-1--->>>11 //Thread-1--->>>12 //Thread-1--->>>13 //Thread-1--->>>14 //Thread-1--->>>15 //Thread-1--->>>16 //Thread-1--->>>17 //Thread-1--->>>18 //Thread-1--->>>19 //Thread-1--->>>20 //Thread-1--->>>21 //Thread-1--->>>22 //Thread-1--->>>23 //Thread-1--->>>24 //Thread-1--->>>25 //Thread-1--->>>26 //Thread-1--->>>27 //Thread-1--->>>28 //Thread-1--->>>29 //Thread-1--->>>30 //Thread-1--->>>31 //Thread-1--->>>32 //Thread-1--->>>33 //Thread-1--->>>34 //Thread-1--->>>35 //Thread-1--->>>36 //Thread-1--->>>37 //Thread-1--->>>38 //Thread-1--->>>39 //Thread-1--->>>40 //Thread-1--->>>41 //Thread-1--->>>42 //Thread-1--->>>43 //Thread-1--->>>44 //Thread-1--->>>45 //Thread-1--->>>46 //Thread-1--->>>47 //Thread-1--->>>48 //Thread-1--->>>49 //Thread-1--->>>50 //Thread-1--->>>51 //Thread-1--->>>52 //Thread-1--->>>53 //Thread-1--->>>54 //Thread-1--->>>55 //Thread-1--->>>56 //Thread-1--->>>57 //Thread-1--->>>58 //Thread-1--->>>59 //Thread-1--->>>60 //Thread-1--->>>61 //Thread-1--->>>62 //Thread-1--->>>63 //Thread-1--->>>64 //Thread-1--->>>65 //Thread-1--->>>66 //Thread-1--->>>67 //Thread-1--->>>68 //Thread-1--->>>69 //Thread-1--->>>70 //Thread-1--->>>71 //Thread-1--->>>72 //Thread-1--->>>73 //Thread-1--->>>74 //Thread-1--->>>75 //Thread-1--->>>76 //Thread-1--->>>77 //Thread-1--->>>78 //Thread-1--->>>79 //Thread-1--->>>80 //Thread-1--->>>81 //Thread-1--->>>82 //Thread-1--->>>83 //Thread-1--->>>84 //Thread-1--->>>85 //Thread-1--->>>86 //Thread-1--->>>87 //Thread-1--->>>88 //Thread-1--->>>89 //Thread-1--->>>90 //Thread-1--->>>91 //Thread-1--->>>92 //Thread-1--->>>93 //Thread-1--->>>94 //Thread-1--->>>95 //Thread-1--->>>96 //Thread-1--->>>97 //Thread-1--->>>98 //Thread-1--->>>99 //Thread-0--->>>0 //Thread-0--->>>1 //Thread-0--->>>2 //Thread-0--->>>3 //Thread-0--->>>4 //Thread-0--->>>5 //Thread-0--->>>6 //Thread-0--->>>7 //Thread-0--->>>8 //Thread-0--->>>9 //Thread-0--->>>10 //Thread-0--->>>11 //Thread-0--->>>12 //Thread-0--->>>13 //Thread-0--->>>14 //Thread-0--->>>15 //Thread-0--->>>16 //Thread-0--->>>17 //Thread-0--->>>18 //Thread-0--->>>19 //Thread-0--->>>20 //Thread-0--->>>21 //Thread-0--->>>22 //Thread-0--->>>23 //Thread-0--->>>24 //Thread-0--->>>25 //Thread-0--->>>26 //Thread-0--->>>27 //Thread-0--->>>28 //Thread-0--->>>29 //Thread-0--->>>30 //Thread-0--->>>31 //Thread-0--->>>32 //Thread-0--->>>33 //Thread-0--->>>34 //Thread-0--->>>35 //Thread-0--->>>36 //Thread-0--->>>37 //Thread-0--->>>38 //Thread-0--->>>39 //Thread-0--->>>40 //Thread-0--->>>41 //Thread-0--->>>42 //Thread-0--->>>43 //Thread-0--->>>44 //Thread-0--->>>45 //Thread-0--->>>46 //Thread-0--->>>47 //Thread-0--->>>48 //Thread-0--->>>49 //Thread-0--->>>50 //Thread-0--->>>51 //Thread-0--->>>52 //Thread-0--->>>53 //Thread-0--->>>54 //Thread-0--->>>55 //Thread-0--->>>56 //Thread-0--->>>57 //Thread-0--->>>58 //Thread-0--->>>59 //Thread-0--->>>60 //Thread-0--->>>61 //Thread-0--->>>62 //Thread-0--->>>63 //Thread-0--->>>64 //Thread-0--->>>65 //Thread-0--->>>66 //Thread-0--->>>67 //Thread-0--->>>68 //Thread-0--->>>69 //Thread-0--->>>70 //Thread-0--->>>71 //Thread-0--->>>72 //Thread-0--->>>73 //Thread-0--->>>74 //Thread-0--->>>75 //Thread-0--->>>76 //Thread-0--->>>77 //Thread-0--->>>78 //Thread-0--->>>79 //Thread-0--->>>80 //Thread-0--->>>81 //Thread-0--->>>82 //Thread-0--->>>83 //Thread-0--->>>84 //Thread-0--->>>85 //Thread-0--->>>86 //Thread-0--->>>87 //Thread-0--->>>88 //Thread-0--->>>89 //Thread-0--->>>90 //Thread-0--->>>91 //Thread-0--->>>92 //Thread-0--->>>93 //Thread-0--->>>94 //Thread-0--->>>95 //Thread-0--->>>96 //Thread-0--->>>97 //Thread-0--->>>98 //Thread-0--->>>99
6:同步静态方法
这里是使用TestClass6的运行时Class对象作为锁对象,这个对象也称为类对象,只要对象相同就能实现同步。
/** * @Auther: DaShu * @Date: 2021/9/10 19:28 * @Description: * 使用synchronize修饰静态方法, */ public class TestClass6 { public static void main(String[] args) { final TestClass6 testClass = new TestClass6(); final TestClass6 testClass2 = new TestClass6(); //创建线程一个调用mm,一个调用mm22 new Thread(new Runnable() { public void run() { testClass.mm();//这里的锁对象是常量obj } }).start(); new Thread(new Runnable() { public void run() { TestClass6.mm22();//这里的锁对象是常量obj } }).start(); } public void mm() { synchronized (TestClass6.class) {//使用当前类的运行时类对象(Class对象)作为锁对象。可以理解为把当前类的字节码文件作为实例对象。 for (int i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName() + "--->>>" + i); } } } //这个就叫同步静态方法,同步静态方法也是把整个代码快进行锁定,锁对象不是this对象,而是当前类的运行类对象。 public synchronized static void mm22() { for (int i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName() + "--->>>" + i); } } //Thread-0--->>>0 //Thread-0--->>>1 //Thread-0--->>>2 //Thread-0--->>>3 //Thread-0--->>>4 //Thread-0--->>>5 //Thread-0--->>>6 //Thread-0--->>>7 //Thread-0--->>>8 //Thread-0--->>>9 //Thread-0--->>>10 //Thread-0--->>>11 //Thread-0--->>>12 //Thread-0--->>>13 //Thread-0--->>>14 //Thread-0--->>>15 //Thread-0--->>>16 //Thread-0--->>>17 //Thread-0--->>>18 //Thread-0--->>>19 //Thread-0--->>>20 //Thread-0--->>>21 //Thread-0--->>>22 //Thread-0--->>>23 //Thread-0--->>>24 //Thread-0--->>>25 //Thread-0--->>>26 //Thread-0--->>>27 //Thread-0--->>>28 //Thread-0--->>>29 //Thread-0--->>>30 //Thread-0--->>>31 //Thread-0--->>>32 //Thread-0--->>>33 //Thread-0--->>>34 //Thread-0--->>>35 //Thread-0--->>>36 //Thread-0--->>>37 //Thread-0--->>>38 //Thread-0--->>>39 //Thread-0--->>>40 //Thread-0--->>>41 //Thread-0--->>>42 //Thread-0--->>>43 //Thread-0--->>>44 //Thread-0--->>>45 //Thread-0--->>>46 //Thread-0--->>>47 //Thread-0--->>>48 //Thread-0--->>>49 //Thread-0--->>>50 //Thread-0--->>>51 //Thread-0--->>>52 //Thread-0--->>>53 //Thread-0--->>>54 //Thread-0--->>>55 //Thread-0--->>>56 //Thread-0--->>>57 //Thread-0--->>>58 //Thread-0--->>>59 //Thread-0--->>>60 //Thread-0--->>>61 //Thread-0--->>>62 //Thread-0--->>>63 //Thread-0--->>>64 //Thread-0--->>>65 //Thread-0--->>>66 //Thread-0--->>>67 //Thread-0--->>>68 //Thread-0--->>>69 //Thread-0--->>>70 //Thread-0--->>>71 //Thread-0--->>>72 //Thread-0--->>>73 //Thread-0--->>>74 //Thread-0--->>>75 //Thread-0--->>>76 //Thread-0--->>>77 //Thread-0--->>>78 //Thread-0--->>>79 //Thread-0--->>>80 //Thread-0--->>>81 //Thread-0--->>>82 //Thread-0--->>>83 //Thread-0--->>>84 //Thread-0--->>>85 //Thread-0--->>>86 //Thread-0--->>>87 //Thread-0--->>>88 //Thread-0--->>>89 //Thread-0--->>>90 //Thread-0--->>>91 //Thread-0--->>>92 //Thread-0--->>>93 //Thread-0--->>>94 //Thread-0--->>>95 //Thread-0--->>>96 //Thread-0--->>>97 //Thread-0--->>>98 //Thread-0--->>>99 //Thread-1--->>>0 //Thread-1--->>>1 //Thread-1--->>>2 //Thread-1--->>>3 //Thread-1--->>>4 //Thread-1--->>>5 //Thread-1--->>>6 //Thread-1--->>>7 //Thread-1--->>>8 //Thread-1--->>>9 //Thread-1--->>>10 //Thread-1--->>>11 //Thread-1--->>>12 //Thread-1--->>>13 //Thread-1--->>>14 //Thread-1--->>>15 //Thread-1--->>>16 //Thread-1--->>>17 //Thread-1--->>>18 //Thread-1--->>>19 //Thread-1--->>>20 //Thread-1--->>>21 //Thread-1--->>>22 //Thread-1--->>>23 //Thread-1--->>>24 //Thread-1--->>>25 //Thread-1--->>>26 //Thread-1--->>>27 //Thread-1--->>>28 //Thread-1--->>>29 //Thread-1--->>>30 //Thread-1--->>>31 //Thread-1--->>>32 //Thread-1--->>>33 //Thread-1--->>>34 //Thread-1--->>>35 //Thread-1--->>>36 //Thread-1--->>>37 //Thread-1--->>>38 //Thread-1--->>>39 //Thread-1--->>>40 //Thread-1--->>>41 //Thread-1--->>>42 //Thread-1--->>>43 //Thread-1--->>>44 //Thread-1--->>>45 //Thread-1--->>>46 //Thread-1--->>>47 //Thread-1--->>>48 //Thread-1--->>>49 //Thread-1--->>>50 //Thread-1--->>>51 //Thread-1--->>>52 //Thread-1--->>>53 //Thread-1--->>>54 //Thread-1--->>>55 //Thread-1--->>>56 //Thread-1--->>>57 //Thread-1--->>>58 //Thread-1--->>>59 //Thread-1--->>>60 //Thread-1--->>>61 //Thread-1--->>>62 //Thread-1--->>>63 //Thread-1--->>>64 //Thread-1--->>>65 //Thread-1--->>>66 //Thread-1--->>>67 //Thread-1--->>>68 //Thread-1--->>>69 //Thread-1--->>>70 //Thread-1--->>>71 //Thread-1--->>>72 //Thread-1--->>>73 //Thread-1--->>>74 //Thread-1--->>>75 //Thread-1--->>>76 //Thread-1--->>>77 //Thread-1--->>>78 //Thread-1--->>>79 //Thread-1--->>>80 //Thread-1--->>>81 //Thread-1--->>>82 //Thread-1--->>>83 //Thread-1--->>>84 //Thread-1--->>>85 //Thread-1--->>>86 //Thread-1--->>>87 //Thread-1--->>>88 //Thread-1--->>>89 //Thread-1--->>>90 //Thread-1--->>>91 //Thread-1--->>>92 //Thread-1--->>>93 //Thread-1--->>>94 //Thread-1--->>>95 //Thread-1--->>>96 //Thread-1--->>>97 //Thread-1--->>>98 //Thread-1--->>>99 }
7:同步方法和同步代码块如何选择
说明:这样可以实现同步:共享资源使用this加锁,两个线程需要获取的锁对象一致,可以实现同步。
/** * @Auther: DaShu * @Date: 2021/9/14 20:33 * @Description: */ public class TestClass { public static void main(String[] args) { final TestClass testClass = new TestClass(); new Thread(new Runnable() { public void run() { testClass.doDealCount(); } }).start(); new Thread(new Runnable() { public void run() { testClass.doDealCount(); } }).start(); } public void doDealCount(){ try { System.out.println("----Method Begin----"); Thread.sleep(3000); synchronized (this){ for (int i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName()+"--->>>"+i); } } System.out.println("----Method End----"); } catch (InterruptedException e) { e.printStackTrace(); } } } //----Method Begin---- //----Method Begin---- //Thread-0--->>>0 //Thread-0--->>>1 //Thread-0--->>>2 //Thread-0--->>>3 //Thread-0--->>>4 //Thread-0--->>>5 //Thread-0--->>>6 //Thread-0--->>>7 //Thread-0--->>>8 //Thread-0--->>>9 //Thread-0--->>>10 //Thread-0--->>>11 //Thread-0--->>>12 //Thread-0--->>>13 //Thread-0--->>>14 //Thread-0--->>>15 //Thread-0--->>>16 //Thread-0--->>>17 //Thread-0--->>>18 //Thread-0--->>>19 //Thread-0--->>>20 //Thread-0--->>>21 //Thread-0--->>>22 //Thread-0--->>>23 //Thread-0--->>>24 //Thread-0--->>>25 //Thread-0--->>>26 //Thread-0--->>>27 //Thread-0--->>>28 //Thread-0--->>>29 //Thread-0--->>>30 //Thread-0--->>>31 //Thread-0--->>>32 //Thread-0--->>>33 //Thread-0--->>>34 //Thread-0--->>>35 //Thread-0--->>>36 //Thread-0--->>>37 //Thread-0--->>>38 //Thread-0--->>>39 //Thread-0--->>>40 //Thread-0--->>>41 //Thread-0--->>>42 //Thread-0--->>>43 //Thread-0--->>>44 //Thread-0--->>>45 //Thread-0--->>>46 //Thread-0--->>>47 //Thread-0--->>>48 //Thread-0--->>>49 //Thread-0--->>>50 //Thread-0--->>>51 //Thread-0--->>>52 //Thread-0--->>>53 //Thread-0--->>>54 //Thread-0--->>>55 //Thread-0--->>>56 //Thread-0--->>>57 //Thread-0--->>>58 //Thread-0--->>>59 //Thread-0--->>>60 //Thread-0--->>>61 //Thread-0--->>>62 //Thread-0--->>>63 //Thread-0--->>>64 //Thread-0--->>>65 //Thread-0--->>>66 //Thread-0--->>>67 //Thread-0--->>>68 //Thread-0--->>>69 //Thread-0--->>>70 //Thread-0--->>>71 //Thread-0--->>>72 //Thread-0--->>>73 //Thread-0--->>>74 //Thread-0--->>>75 //Thread-0--->>>76 //Thread-0--->>>77 //Thread-0--->>>78 //Thread-0--->>>79 //Thread-0--->>>80 //Thread-0--->>>81 //Thread-0--->>>82 //Thread-0--->>>83 //Thread-0--->>>84 //Thread-0--->>>85 //Thread-0--->>>86 //Thread-0--->>>87 //Thread-0--->>>88 //Thread-0--->>>89 //Thread-0--->>>90 //Thread-0--->>>91 //Thread-0--->>>92 //Thread-0--->>>93 //Thread-0--->>>94 //Thread-0--->>>95 //Thread-0--->>>96 //Thread-0--->>>97 //Thread-0--->>>98 //Thread-0--->>>99 //----Method End---- //Thread-1--->>>0 //Thread-1--->>>1 //Thread-1--->>>2 //Thread-1--->>>3 //Thread-1--->>>4 //Thread-1--->>>5 //Thread-1--->>>6 //Thread-1--->>>7 //Thread-1--->>>8 //Thread-1--->>>9 //Thread-1--->>>10 //Thread-1--->>>11 //Thread-1--->>>12 //Thread-1--->>>13 //Thread-1--->>>14 //Thread-1--->>>15 //Thread-1--->>>16 //Thread-1--->>>17 //Thread-1--->>>18 //Thread-1--->>>19 //Thread-1--->>>20 //Thread-1--->>>21 //Thread-1--->>>22 //Thread-1--->>>23 //Thread-1--->>>24 //Thread-1--->>>25 //Thread-1--->>>26 //Thread-1--->>>27 //Thread-1--->>>28 //Thread-1--->>>29 //Thread-1--->>>30 //Thread-1--->>>31 //Thread-1--->>>32 //Thread-1--->>>33 //Thread-1--->>>34 //Thread-1--->>>35 //Thread-1--->>>36 //Thread-1--->>>37 //Thread-1--->>>38 //Thread-1--->>>39 //Thread-1--->>>40 //Thread-1--->>>41 //Thread-1--->>>42 //Thread-1--->>>43 //Thread-1--->>>44 //Thread-1--->>>45 //Thread-1--->>>46 //Thread-1--->>>47 //Thread-1--->>>48 //Thread-1--->>>49 //Thread-1--->>>50 //Thread-1--->>>51 //Thread-1--->>>52 //Thread-1--->>>53 //Thread-1--->>>54 //Thread-1--->>>55 //Thread-1--->>>56 //Thread-1--->>>57 //Thread-1--->>>58 //Thread-1--->>>59 //Thread-1--->>>60 //Thread-1--->>>61 //Thread-1--->>>62 //Thread-1--->>>63 //Thread-1--->>>64 //Thread-1--->>>65 //Thread-1--->>>66 //Thread-1--->>>67 //Thread-1--->>>68 //Thread-1--->>>69 //Thread-1--->>>70 //Thread-1--->>>71 //Thread-1--->>>72 //Thread-1--->>>73 //Thread-1--->>>74 //Thread-1--->>>75 //Thread-1--->>>76 //Thread-1--->>>77 //Thread-1--->>>78 //Thread-1--->>>79 //Thread-1--->>>80 //Thread-1--->>>81 //Thread-1--->>>82 //Thread-1--->>>83 //Thread-1--->>>84 //Thread-1--->>>85 //Thread-1--->>>86 //Thread-1--->>>87 //Thread-1--->>>88 //Thread-1--->>>89 //Thread-1--->>>90 //Thread-1--->>>91 //Thread-1--->>>92 //Thread-1--->>>93 //Thread-1--->>>94 //Thread-1--->>>95 //Thread-1--->>>96 //Thread-1--->>>97 //Thread-1--->>>98 //Thread-1--->>>99 //----Method End----
同步方法锁的粒度粗,并发效率低,执行效率没有同步代码块好,这个肯定是粒度越低效率越高,这个关键点在于参考这个代码中的准备工作。概括一下就是粒度越细,效率越高。
/** * @Auther: DaShu * @Date: 2021/9/14 20:33 * @Description: */ public class TestClass1 { public static void main(String[] args) { final TestClass1 testClass = new TestClass1(); new Thread(new Runnable() { public void run() { testClass.doDealCount(); } }).start(); new Thread(new Runnable() { public void run() { testClass.doDealCount(); } }).start(); } public void doDealCount(){ try { System.out.println("----Method Begin----"); Thread.sleep(3000); synchronized (this){ for (int i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName()+"--->>>"+i); } } System.out.println("----Method End----"); } catch (InterruptedException e) { e.printStackTrace(); } } public synchronized void doDealCountCopy(){ try { System.out.println("----Method Begin----"); Thread.sleep(3000); for (int i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName()+"--->>>"+i); } System.out.println("----Method End----"); } catch (InterruptedException e) { e.printStackTrace(); } } }
8: 脏读
出现了一个线程读取到另一个线程修改过程中的值,要么读还没改的值, 要么读取已经修改完毕的值,不能读取正在修改当中的值。究其原因,是因为没有实现同步的原因,我们不仅对修改的代码块进行同步,还要对读取的代码块进行同步,才能保证同步性,就能保证不出现脏读,不出现脏读,读取和修改的时候都需要通过加锁实现同步,单单实现一个同步的话是没有办法不出现脏读的。只要保证修改数据的线程和读取数据的线程能够实现同步,就可以保证不出现脏读,也就是说,修改完了,才能读取。
9:线程异常会自动释放锁
如果同步过程中出现了异常,我们的锁今天Thread线程获取到CPU执行权,拿到了锁对象之后,如果他出现了异常,如果没有处理的话,这个线程就崩了,锁对象就会自动释放,这样的话,另外一个线程就可以拿到锁对象。
如果异步当中的锁住的共享数据中的代码发生了异常,如果进行了捕获,异常没有发生向上抛出穿过锁代码块这样的情况,就不会释放锁对象,如果穿过了锁住的代码块区域,就会释放锁对象,同步方法同理。
/** * @Auther: DaShu * @Date: 2021/9/17 21:18 * @Description: */ public class TestClass { public static void main(String[] args) { final TestClass testClass = new TestClass(); new Thread(new Runnable() { public void run() { testClass.doDealCount(); } }).start(); new Thread(new Runnable() { public void run() { TestClass.doDealCountCopy(); } }).start(); } public void doDealCount(){ synchronized (TestClass.class){ System.out.println("----Method Begin----aaaaa"); Thread.currentThread().setName("aaaaa"); for (int i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName()+"--->>>"+i); if(i == 50){ throw new RuntimeException("异常信息"); } } } System.out.println("----Method End----"); } public static synchronized void doDealCountCopy(){ System.out.println("----Method Begin----bbbbb"); Thread.currentThread().setName("bbbbb"); for (int i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName()+"--->>>"+i); } System.out.println("----Method End----"); } //----Method Begin----aaaaa //aaaaa--->>>0 //aaaaa--->>>1 //aaaaa--->>>2 //aaaaa--->>>3 //aaaaa--->>>4 //aaaaa--->>>5 //aaaaa--->>>6 //aaaaa--->>>7 //aaaaa--->>>8 //aaaaa--->>>9 //aaaaa--->>>10 //aaaaa--->>>11 //aaaaa--->>>12 //aaaaa--->>>13 //aaaaa--->>>14 //aaaaa--->>>15 //aaaaa--->>>16 //aaaaa--->>>17 //aaaaa--->>>18 //aaaaa--->>>19 //aaaaa--->>>20 //aaaaa--->>>21 //aaaaa--->>>22 //aaaaa--->>>23 //aaaaa--->>>24 //aaaaa--->>>25 //aaaaa--->>>26 //aaaaa--->>>27 //aaaaa--->>>28 //aaaaa--->>>29 //aaaaa--->>>30 //aaaaa--->>>31 //aaaaa--->>>32 //aaaaa--->>>33 //aaaaa--->>>34 //aaaaa--->>>35 //aaaaa--->>>36 //aaaaa--->>>37 //aaaaa--->>>38 //aaaaa--->>>39 //aaaaa--->>>40 //aaaaa--->>>41 //aaaaa--->>>42 //aaaaa--->>>43 //aaaaa--->>>44 //aaaaa--->>>45 //aaaaa--->>>46 //aaaaa--->>>47 //aaaaa--->>>48 //aaaaa--->>>49 //aaaaa--->>>50 //----Method Begin----bbbbb //bbbbb--->>>0 //bbbbb--->>>1 //bbbbb--->>>2 //bbbbb--->>>3 //bbbbb--->>>4 //bbbbb--->>>5 //bbbbb--->>>6 //bbbbb--->>>7 //bbbbb--->>>8 //bbbbb--->>>9 //bbbbb--->>>10 //bbbbb--->>>11 //bbbbb--->>>12 //bbbbb--->>>13 //bbbbb--->>>14 //bbbbb--->>>15 //bbbbb--->>>16 //bbbbb--->>>17 //bbbbb--->>>18 //bbbbb--->>>19 //bbbbb--->>>20 //bbbbb--->>>21 //bbbbb--->>>22 //bbbbb--->>>23 //bbbbb--->>>24 //bbbbb--->>>25 //bbbbb--->>>26 //bbbbb--->>>27 //bbbbb--->>>28 //bbbbb--->>>29 //bbbbb--->>>30 //bbbbb--->>>31 //bbbbb--->>>32 //bbbbb--->>>33 //bbbbb--->>>34 //bbbbb--->>>35 //bbbbb--->>>36 //bbbbb--->>>37 //bbbbb--->>>38 //bbbbb--->>>39 //bbbbb--->>>40 //bbbbb--->>>41 //bbbbb--->>>42 //bbbbb--->>>43 //bbbbb--->>>44 //bbbbb--->>>45 //bbbbb--->>>46 //bbbbb--->>>47 //bbbbb--->>>48 //bbbbb--->>>49 //bbbbb--->>>50 //bbbbb--->>>51 //bbbbb--->>>52 //bbbbb--->>>53 //bbbbb--->>>54 //bbbbb--->>>55 //bbbbb--->>>56 //bbbbb--->>>57 //bbbbb--->>>58 //bbbbb--->>>59 //bbbbb--->>>60 //bbbbb--->>>61 //bbbbb--->>>62 //bbbbb--->>>63 //bbbbb--->>>64 //bbbbb--->>>65 //bbbbb--->>>66 //bbbbb--->>>67 //bbbbb--->>>68 //bbbbb--->>>69 //bbbbb--->>>70 //bbbbb--->>>71 //bbbbb--->>>72 //bbbbb--->>>73 //bbbbb--->>>74 //bbbbb--->>>75 //bbbbb--->>>76 //bbbbb--->>>77 //bbbbb--->>>78 //bbbbb--->>>79 //bbbbb--->>>80 //bbbbb--->>>81 //bbbbb--->>>82 //bbbbb--->>>83 //bbbbb--->>>84 //bbbbb--->>>85 //bbbbb--->>>86 //bbbbb--->>>87 //bbbbb--->>>88 //bbbbb--->>>89 //bbbbb--->>>90 //bbbbb--->>>91 //bbbbb--->>>92 //bbbbb--->>>93 //bbbbb--->>>94 //bbbbb--->>>95 //bbbbb--->>>96 //bbbbb--->>>97 //bbbbb--->>>98 //bbbbb--->>>99 //----Method End---- //Exception in thread "aaaaa" java.lang.RuntimeException: 异常信息 // at com.dashu.Test13.TestClass.doDealCount(TestClass.java:33) // at com.dashu.Test13.TestClass$1.run(TestClass.java:15) // at java.lang.Thread.run(Thread.java:748) }
10:死锁
死锁也是多线程中经常出现的一个问题,当我们的线程获取多个锁的时候, 获取锁的顺序不一致就会出现死锁的问题,在多线程程序中,同步时可能需要使用多个锁,如果获得锁的顺序不一致可能会导致死锁。
本质就是:一共有两个锁,一个线程获取到一个锁之后,获取另一个锁,另一个线程获取到另一个锁之后还要获取第一个锁,两个最先获取到的锁都没有得到释放的前提下就形成了死锁。
public class TestClass { public static void main(String[] args) { SubThread t1 = new SubThread(); t1.setName("a"); t1.start(); SubThread t2 = new SubThread(); t2.setName("b"); t2.start(); } static class SubThread extends Thread{ private static final Object lock1 = new Object(); private static final Object lock2 = new Object(); @Override public void run() { if("a".equals(Thread.currentThread().getName())){ synchronized (lock1){ System.out.println("a线程获取了Lock1锁"); synchronized (lock2){ System.out.println("a,线程获取lock1之后又获取了lock2,可以想干任何任务。"); } } } if("b".equals(Thread.currentThread().getName())){ synchronized (lock2){ System.out.println("b线程获取了Lock2锁"); synchronized (lock1){ System.out.println("b线程获取lock2之后又获取了lock1,可以想干任何任务。"); } } } } } } //b线程获取了Lock2锁 //a线程获取了Lock1锁
四:轻量级同步机制:Volative关键字
1:volatile的作用
1、volatile这个关键字的作用使变量在多个线程之间可见,解决变量的可见性。比如会发现,main线程中修改了这个一个属性的值会后,工作线程中读取不到,但是我们可以加上volatile关键字,他会强制线程从公共内存中读取这个字的值,产生我们修改了标志但是线程中没有感应到是因为这个线程不是从公共内存中取去,从而感应不到这个字段的变化,这样的话就是当main线程(主线程)修改了堆内存当中的数据之后,子线程读取不到,因为他是从线程当中来取值的,所以需要使用使用Volatile关键字进行修饰,强制线程去公共内存中进行读取volatile关键字的作用就是通知线程每次从主内存当中取值,而不是线程的工作内存中取值。
2、volatile和Synchronized比较
1)volatile关键字是线程同步的轻量级实现,volatile的新能肯定比synchronized好,volatile只能修饰变量,后者方法、代码块都是可以修饰的,随着JDK新版本的发布,synchronized的执行效率也得到了提升,开发中,我们使用synchronized比率还是很高的。
2)、多线程访问volatile变量不会发生阻塞,而synchd可能会发生阻塞,volatile能保证数据的可见性,不能保证数据的原子性,就是可能会读取到线程过程中的数据,而后者可以保证线程的原子性和可见性。因为他可以将工作内存和主内存进行同步。
3)关键字volatile解决的是变量在多个线程之间的可见性,而synchronized解决的是多个线程之间访问公共资源的同步性,synchronized解决的原子性和可见性。
2:votatile非原子特性
/** * @Auther: DaShu * @Date: 2021/9/26 18:44 * @Description: */ public class TestClass02 { public static void main(String[] args) throws InterruptedException { final PrintString printString = new PrintString(); new Thread(new Runnable() { public void run() { printString.printString(); } }).start(); throw new RuntimeException("测试异常"); } //定义一个类 static class PrintString{ private boolean continuePrint = true; public PrintString setContinuePrint(boolean continuePrint){ this.continuePrint = continuePrint; return this; } public void printString(){ while (continuePrint){ System.out.println(Thread.currentThread().getName()); // try { // Thread.sleep(500); // } catch (InterruptedException e) { // e.printStackTrace(); // } } } } }