一、为什么这个是重点?

  1. 以后在开发中,我们的项目都是运行在服务器当中,<br /> 而服务器已经将线程的定义,线程对象的创建,线程<br /> 的启动等,都已经实现完了。这些代码我们都不需要<br /> 编写。
  2. 最重要的是:你要知道,你编写的程序需要放到一个<br /> 多线程的环境下运行,你更需要关注的是这些数据<br /> 在多线程并发的环境下是否是安全的。(重点:*****)<br />

二、什么时候数据在多线程并发的环境下会存在安全问题呢?

  1. **三个条件**:<br /> 条件1:多线程并发。<br /> 条件2:有共享数据。<br /> 条件3:共享数据有修改的行为。
  2. 满足以上3个条件之后,就会存在线程安全问题。<br />

三、怎么解决线程安全问题呢?

  1. 当多线程并发的环境下,有共享数据,并且这个数据还会被修改,此时就存在<br /> 线程安全问题,怎么解决这个问题?<br /> 线程排队执行。(不能并发)。<br /> 用排队执行解决线程安全问题。<br /> 这种机制被称为:**线程同步**机制。
  2. 专业术语叫做:线程同步,实际上就是线程不能并发了,线程必须排队执行。<br /> <br /> 怎么解决线程安全问题呀?<br /> 使用“线程同步机制”。<br /> <br /> 线程同步就是线程排队了,线程排队了就会牺牲一部分效率,没办法,数据安全<br /> 第一位,只有数据安全了,我们才可以谈效率。数据不安全,没有效率的事儿。<br />

四、说到线程同步这块,涉及到这两个专业术语:

  1. 异步编程模型:<br /> 线程t1和线程t2,各自执行各自的,t1不管t2t2不管t1,<br /> 谁也不需要等谁,这种编程模型叫做:异步编程模型。<br /> 其实就是:多线程并发(效率较高。)
  2. 异步就是并发。
  3. 同步编程模型:<br /> 线程t1和线程t2,在线程t1执行的时候,必须等待t2线程执行<br /> 结束,或者说在t2线程执行的时候,必须等待t1线程执行结束,<br /> 两个线程之间发生了等待关系,这就是同步编程模型。<br /> 效率较低。线程排队执行。
  4. 同步就是排队。

五、synchronized

image.png

1.执行原理

image.png

image.png

六、Java中有三大变量?【重要的内容。】

  1. 实例变量:在堆中。
  2. 静态变量:在方法区。
  3. 局部变量:在栈中。
  4. 以上三大变量中:<br /> 局部变量永远都不会存在线程安全问题。<br /> 因为局部变量不共享。(一个线程一个栈。)<br /> 局部变量在栈中。所以局部变量永远都不会共享。<br /> <br /> 实例变量在堆中,堆只有1个。<br /> 静态变量在方法区中,方法区只有1个。<br /> 堆和方法区都是多线程共享的,所以可能存在线程安全问题。
  5. 局部变量+常量:不会有线程安全问题。<br /> 成员变量:可能会有线程安全问题。

七、如果使用局部变量的话:

  1. 建议使用:StringBuilder。<br /> 因为局部变量不存在线程安全问题。选择StringBuilder。<br /> StringBuffer效率比较低。
  2. ArrayList是非线程安全的。<br /> Vector是线程安全的。<br /> HashMap HashSet是非线程安全的。<br /> Hashtable是线程安全的。

八、synchronized有三种写法:

  1. 第一种:同步代码块<br /> 灵活<br /> synchronized(线程共享对象){<br /> 同步代码块;<br /> }
  2. 第二种:在实例方法上使用synchronized<br /> 表示共享对象一定是this<br /> 并且同步代码块是整个方法体。<br /> <br /> 第三种:在静态方法上使用synchronized<br /> 表示找类锁。<br /> 类锁永远只有1把。<br /> 就算创建了100个对象,那类锁也只有一把。<br /> <br /> 对象锁:1个对象1把锁,100个对象100把锁。<br /> 类锁:100个对象,也可能只是1把类锁。

九、synchronized的四种类型(需不需要等)

1.doOther不需要等,因为动态方法doOther没有加synchronized

  1. package synch.Exam;
  2. /*
  3. doOther不需要等,因为动态方法doOther没有加synchronized
  4. */
  5. public class Exam01 {
  6. public static void main(String[] args) throws InterruptedException {
  7. MyClass mc = new MyClass();
  8. Thread t1 = new MyThread(mc);
  9. Thread t2 = new MyThread(mc);
  10. t1.setName("t1");
  11. t2.setName("t2");
  12. t1.start();
  13. Thread.sleep(1000*5);
  14. t2.start();
  15. }
  16. }
  17. class MyThread extends Thread{
  18. private MyClass mc;
  19. public MyThread(MyClass mc) {
  20. this.mc = mc;
  21. }
  22. public void run(){
  23. if(Thread.currentThread().getName().equals("t1")){
  24. mc.doSome();
  25. }
  26. if (Thread.currentThread().getName().equals("t2")){
  27. mc.doOther();
  28. }
  29. }
  30. }
  31. class MyClass{
  32. public synchronized void doSome(){
  33. System.out.println("doSome begin");
  34. try {
  35. Thread.sleep(1000*10);
  36. } catch (InterruptedException e) {
  37. e.printStackTrace();
  38. }
  39. System.out.println("doSome over");
  40. }
  41. public void doOther(){
  42. System.out.println("doOther begin");
  43. System.out.println("doOther over");
  44. }
  45. }

2.doOther需要等,因为动态方法doOther加了synchronized

  1. package synch.Exam02;
  2. /*
  3. doOther需要等,因为动态方法doOther加了synchronized
  4. */
  5. public class Exam02 {
  6. public static void main(String[] args) throws InterruptedException {
  7. MyClass mc = new MyClass();
  8. Thread t1 = new MyThread(mc);
  9. Thread t2 = new MyThread(mc);
  10. t1.setName("t1");
  11. t2.setName("t2");
  12. t1.start();
  13. Thread.sleep(1000*5);
  14. t2.start();
  15. }
  16. }
  17. class MyThread extends Thread{
  18. private MyClass mc;
  19. public MyThread(MyClass mc) {
  20. this.mc = mc;
  21. }
  22. public void run(){
  23. if(Thread.currentThread().getName().equals("t1")){
  24. mc.doSome();
  25. }
  26. if (Thread.currentThread().getName().equals("t2")){
  27. mc.doOther();
  28. }
  29. }
  30. }
  31. class MyClass{
  32. public synchronized void doSome(){
  33. System.out.println("doSome begin");
  34. try {
  35. Thread.sleep(1000*10);
  36. } catch (InterruptedException e) {
  37. e.printStackTrace();
  38. }
  39. System.out.println("doSome over");
  40. }
  41. public synchronized void doOther(){
  42. System.out.println("doOther begin");
  43. System.out.println("doOther over");
  44. }
  45. }

3.doOther不需要等,因为动态方法doOther,new了两个不同的对象

  1. package synch.Exam03;
  2. /*
  3. doOther不需要等,因为动态方法doOther,new了两个不同的对象
  4. */
  5. public class Exam03 {
  6. public static void main(String[] args) throws InterruptedException {
  7. MyClass mc1 = new MyClass();
  8. MyClass mc2 = new MyClass();
  9. Thread t1 = new MyThread(mc1);
  10. Thread t2 = new MyThread(mc2);
  11. t1.setName("t1");
  12. t2.setName("t2");
  13. t1.start();
  14. Thread.sleep(1000);
  15. t2.start();
  16. }
  17. }
  18. class MyThread extends Thread{
  19. private MyClass mc;
  20. public MyThread(MyClass mc) {
  21. this.mc = mc;
  22. }
  23. public void run(){
  24. if(Thread.currentThread().getName().equals("t1")){
  25. mc.doSome();
  26. }
  27. if (Thread.currentThread().getName().equals("t2")){
  28. mc.doOther();
  29. }
  30. }
  31. }
  32. class MyClass{
  33. public synchronized void doSome(){
  34. System.out.println("doSome begin");
  35. try {
  36. Thread.sleep(1000*10);
  37. } catch (InterruptedException e) {
  38. e.printStackTrace();
  39. }
  40. System.out.println("doSome over");
  41. }
  42. public synchronized void doOther(){
  43. System.out.println("doOther begin");
  44. System.out.println("doOther over");
  45. }
  46. }

4.doOther需要等,因为动态方法doOther变成了静态方法
静态方法是类锁,类锁只有一把

  1. package synch.Exam04;
  2. /*
  3. doOther需要等,因为动态方法doOther变成了静态方法
  4. 静态方法是类锁,类锁只有一把
  5. */
  6. public class Exam04 {
  7. public static void main(String[] args) throws InterruptedException {
  8. MyClass mc1 = new MyClass();
  9. MyClass mc2 = new MyClass();
  10. Thread t1 = new MyThread(mc1);
  11. Thread t2 = new MyThread(mc2);
  12. t1.setName("t1");
  13. t2.setName("t2");
  14. t1.start();
  15. Thread.sleep(1000*5);
  16. t2.start();
  17. }
  18. }
  19. class MyThread extends Thread{
  20. private MyClass mc;
  21. public MyThread(MyClass mc) {
  22. this.mc = mc;
  23. }
  24. public void run(){
  25. if(Thread.currentThread().getName().equals("t1")){
  26. mc.doSome();
  27. }
  28. if (Thread.currentThread().getName().equals("t2")){
  29. mc.doOther();
  30. }
  31. }
  32. }
  33. class MyClass{
  34. public synchronized static void doSome(){
  35. System.out.println("doSome begin");
  36. try {
  37. Thread.sleep(1000*10);
  38. } catch (InterruptedException e) {
  39. e.printStackTrace();
  40. }
  41. System.out.println("doSome over");
  42. }
  43. public synchronized static void doOther(){
  44. System.out.println("doOther begin");
  45. System.out.println("doOther over");
  46. }
  47. }

十、死锁

  1. package deadlock;
  2. public class DeadLock {
  3. public static void main(String[] args) {
  4. Object o1 = new Object();
  5. Object o2 = new Object();
  6. Thread t1 = new MyThread1(o1,o2);
  7. Thread t2 = new MyThread2(o1,o2);
  8. t1.start();
  9. t2.start();
  10. }
  11. }
  12. class MyThread1 extends Thread{
  13. Object o1;
  14. Object o2;
  15. public MyThread1(Object o1 , Object o2){
  16. this.o1 = o1;
  17. this.o2 = o2;
  18. }
  19. @Override
  20. public void run() {
  21. synchronized(o1){
  22. try {
  23. Thread.sleep(1000);
  24. } catch (InterruptedException e) {
  25. e.printStackTrace();
  26. }
  27. synchronized (o2){
  28. }
  29. }
  30. }
  31. }
  32. class MyThread2 extends Thread{
  33. Object o1;
  34. Object o2;
  35. public MyThread2(Object o1 , Object o2){
  36. this.o1 = o1;
  37. this.o2 = o2;
  38. }
  39. @Override
  40. public void run() {
  41. synchronized(o2){
  42. try {
  43. Thread.sleep(1000);
  44. } catch (InterruptedException e) {
  45. e.printStackTrace();
  46. }
  47. synchronized (o1){
  48. }
  49. }
  50. }
  51. }

十一、我们以后开发中应该怎么解决线程安全问题?

是一上来就选择线程同步吗?synchronized<br />        不是,synchronized会让程序的执行效率降低,用户体验不好。<br />        系统的用户吞吐量降低。用户体验差。在不得已的情况下再选择<br />        线程同步机制。<br />    <br />    第一种方案:尽量使用局部变量代替“实例变量和静态变量”。

第二种方案:如果必须是实例变量,那么可以考虑创建多个对象,这样<br />    实例变量的内存就不共享了。(一个线程对应1个对象,100个线程对应100个对象,<br />    对象不共享,就没有数据安全问题了。)

第三种方案:如果不能使用局部变量,对象也不能创建多个,这个时候<br />    就只能选择synchronized了。线程同步机制。