java基础--笔记14


在使用多线程时,必须先明确的是线程的任务。
线程任务中往往都有循环。
线程不可以多次启动。
线程任务执行结束,线程就结束了。即run()结束,线程就结束。

线程安全问题产生原因:线程任务中有多条操作共享数据的代码
要素:
          1, 共享数据
          2, 线程任务代码中有多条操作共享数据的代码

解决的思路:只要让一个线程在执行任务时,操作多条共享数据代码时,其他线程不要参与运算。

怎么在代码上体现出来?
     首先将多条操作共享数据的代码封装起来。用指定的关键字进行标识。java中提供了synchronized(同步)关键字。
解决方案:同步代码块
synchronized(对象) //是任意对象(相当于锁)
{

}
同步好处:解决多线程的安全问题。
同步弊端:降低了执行效率。
同步前提:多个线程必须使用同一个锁。
可以解决什么问题呢?当多线程安全问题发生时,需要通过同步解决,可是假如同步后,安全问题依旧,这时可以通过同步前提解决

同步关键字还有种用法,修饰函数,称为同步函数。
同步的另一种体现:同步函数。
同步函数使用的所是this。

静态同步函数用的锁是哪个呢?
首先肯定用的不是this锁,因为,static函数不所属于对象,而是所属于类。
结论:静态同步函数使用的锁是当前类的字节码文件对象。
表示方式:  类名.class

同步函数和同步代码块的特点?
相同点:实现同步
不同点:同步函数使用的锁是固定的,静态的用的 类名.class
             非静态的用的是 this
             同步代码块使用的锁是任意对象

如果你需要使用一个锁,两个方式都行。简单写法就是同步函数。
如果你需要使用的锁不止一个,使用同步代码块。
一般建议使用同步代码块,通用!

多线程访问单例时的安全问题:
出现在延迟加载,即懒汉式单例模式。首先要明确的是,单例的出现是为了控制对象只有一个,即一个类对应一个实例。但在多线程中,会造成创建实例不唯一。
分析原因:
public static Single getInstance()
{
     if(instance == null);
     {
          -->Thread-0    
          instance = new Single();
     }
     return instance;
}
当一个线程0执行到图中位置时,要是执行权被另一个线程拿到,运行完代码,创建好对象,然后线程0再次拿到执行权,又创建了一个对象。
怎么解决咧?
将getInstance()用synchronized修饰。
可又产生了新的问题,获取实例的效率很低,都要去判断锁。
解决思想:少判断锁,所以必须要将同步函数改成同步代码块。
可以对同步代码块可控。通过if(实例是否为null)来进行控制,这个写法也可以称之为双重判断实例。
if(instance == null)
          {
               synchronized(Single.class)
               {
                    if(instance == null)
                        
                         instance = new Single();
               }
          }

          return instance;


同步的弊端:一个是降低了执行效率,另一个,程序有可能出现运行停住,但没有结束,这种情况称之为:死锁。
情况一:
     程序中至少有两个以上的锁。锁出现了嵌套。

     synchronized(objA)
     {
          --->0 objA
          synchronized(objB)
          {
              
          }
     }

     synchronized(objB)
     {
          -->1 objB
          synchronized(objA)
          {
              
          }
     }


*
书写一个锁嵌套情况的死锁代码。
1,线程任务。
2,任务中要有同步,而且同步还需要嵌套。
3,让线程分别去不同的同步中去执行,需要定义一个标记。
4,需要两个以上的锁。
*/
//1,定义线程任务
class Demo implements Runnable
{
     //定义标记。
     private boolean flag;
     Demo(boolean flag)
     {
          this.flag = flag;
     }
     public void run()
     {
          if(flag)
          {
               while(true)
               {
                    synchronized(MyLock.LOCKA)
                    {
                         System.out.println("if......locka");
                         synchronized(MyLock.LOCKB)
                         {
                              System.out.println("if......lockb");
                         }
                    }
               }
          }
          else
          {
               while(true)
               {
                    synchronized(MyLock.LOCKB)
                    {
                         System.out.println("else......lockb");
                         synchronized(MyLock.LOCKA)
                         {
                              System.out.println("else......locka");
                         }
                    }
               }
          }
     }
}
class MyLock
{
     public static final Object LOCKA = new Object();
     public static final Object LOCKB = new Object();
}

class ThreadDemo13
{
     public static void main(String[] args)
     {
          //虽然是两个任务,任务内容是相同的,仅仅是标记值不一样。
          Demo d1 = new Demo(true);
          Demo d2 = new Demo(false);
          Thread t1 = new Thread(d1);
          Thread t2 = new Thread(d2);
          t1.start();
          t2.start();
     }
}

wait notify方法到底是怎么做的。
     wait:可以将该锁上的线程置为等待(冻结)状态,释放锁的所有权(放锁)。
     notify:可以将该锁上等待的线程,进行唤醒,当该线程具备执行资格。一旦该线程也具备了锁的所有权就可以执行。
               只能唤醒一个等待线程,而且是任意的一个。
     notifyAll:唤醒该锁上的所有的线程。

     简单理解:wait方法可以将线程等待,相当于存储到了线程池中,这个线程必须标识一下,用锁标识。

     这些方法必须定义在同步中,并且为了区别不同的锁,
     这些三个方法在使用时必须标识所属的锁。需要有锁对象来进行调用。
     锁对象.wait();
     锁对象.notify();
     锁对象.notifyAll();

等待唤醒机制:
     涉及的内容: wait  notify  notifyAll  判断条件,什么时候需要等待。这些方法一定要定义同步中,并被锁对象调用。
多线程间的通信:多个线程处理的资源是相同的,但是处理的任务不同。

sleep和wait有什么异同点?
相同点:
     都可以让线程处于冻结状态。
不同点:     
     sleep必须指定时间
     wait可以指定时间,可以不指定时间

     sleep可以不用定义在同步中,被Thread类名调用
     wait必须定义在同步中,必须被锁调用。

如果都定义在同步中,
sleep方法,让线程休眠,但是该线程不丢失任何监视器的所有权(不放锁)
wait方法,让线程等待,该线程发布对此监视器的所有权并等待(放锁)

问题:同步中如果有多个线程,会不会出现安全问题?
虽然同步中会出现多个线程,但是线程想要执行,除了具备执行资格,还需要有锁。所以不会出现安全问题。


中断:将线程的冻结状态恢复运行状态(让其具备执行资格)。

和其他的正常唤醒有什么区别呢?
notify属于正常唤醒情况,这都属于线程状态的正常切换。

interrupt中断:强制将线程从冻结状态恢复回来,正因为强制,不该醒来而醒来,就会发生中断异常InterruptedException。

中断的目的就是让线程恢复回来去判断标记,让循环结束,让run方法结束。让线程停止。


答疑:
1,单生产单消费没问题时,加入多生产,多消费造成多次生产和多次消费,为什么?
解决办法:每次生产,消费后要判断标记,唤醒后要判断,使用while循环。

2,多生产,多消费造成死锁是为什么?
解决办法:死锁是因为全部进入等待状态,唤醒的是自己这边的,不是对方的线程。使用notifyAll

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值