多线程锁——详解

先来讲什么是线程:

即:Thread和Runnable两个类,可以实现线程

class Card extends Thread{

  //第一步,重写父类Thread中的run方法,这样就可以调度线程,调度线程中启动的方法,即run方法:

  @Override

  public void run(){

    System.out.println("线程启动...");

  }

}

为何要重写run方法,因为多线程方法的调用过程就是这么定义的,我们任何线程的

当我们Thread类的引用被创建以后,我们可以调用start方法来启动该线程,很有趣的是,我们应该要思考,我们start启动完线程以后,我们的执行顺序是怎么样的?

首先,我们知道,我们的线程体方法是run,如果我们直接调用run方法,例如

public class Test{

  Thread1 t1=new Thread1();

  Thread2 t2=new Thread2();

  t1.run();

  t2.run();

}

这种情况下,我们的执行顺序,是调用方法的执行顺序,即先执行Thread1中的run方法,再是Thread2中的run方法

然而,这样的调用方式,就会把我们多线程的作用给浪费了,那么该如何是好?

所以,我们才有调用Thread.start()方法,这个方法调用的目的,就是为了将我们的线程,提交至线程栈,或者线程队列,(待考证)

 

当然上面的线程体只是做了打印的操作,对于我们的多个线程的创建并运行并没有什么多大的影响?什么,你告诉我什么是影响?一般都一下几种影响:

1、线程竞争 顾名思义,就是在多个任务(也叫做创建多个线程(多个线程可能都是相同的线程体,也可能是不同的线程体)并启动)同时访问同一个线程体的同时,对执行相同的代码,并且同时执行,例如以下代码

static int ii=0;

 

public Map<Integer,Object> map=new HashMap<Integer,Object>(); 

@Override

public void run(){

  while(ii<100){

    ii++;

    System.out.println("ii:" + ii + "currentThreadNo:" + Thread.currentThread().getName());

  }

}

 

public static void main(String[] args){

  for(int n=0;n<100;n++){

    new Thread(new ThreadSample()).start();

  }

}

 现在我们要启动100个线程,对相同的线程体进行100次的任务调用,然后,我们截取前10个输出打印,我们将发现如下:

ii:1currentThreadNo:Thread-0
ii:2currentThreadNo:Thread-2
ii:3currentThreadNo:Thread-2
ii:4currentThreadNo:Thread-2
ii:5currentThreadNo:Thread-2
ii:8currentThreadNo:Thread-2
ii:9currentThreadNo:Thread-2
ii:10currentThreadNo:Thread-3
ii:12currentThreadNo:Thread-3
ii:13currentThreadNo:Thread-3
ii:14currentThreadNo:Thread-3
ii:7currentThreadNo:Thread-0
ii:16currentThreadNo:Thread-0
ii:17currentThreadNo:Thread-0
ii:6currentThreadNo:Thread-4

 

显然,我们看到,我们的线程输出是从1~5之后,直接跳到8,什么意思?就是说在我们线程2调度完毕,输出为5之后,我们线程2接下去直接调度输出就变成了8,因为6和7已经被其他线程调度到一般,值发生了改变,于是等到我们线程0都调度输出为17之后,这个时候,我们先前的线程4,开始输出6了,大家看,一直等到这一刻,我们的线程4才输出6啊,这里有个问题,就是我们的线程是不安全的,同时有多个线程一起来竞争这个资源,也或者说叫做同时有多个任务一起竞争这个资源,那么竞争完后必然会造成线程体中,各线程对于资源的争抢

所以,我们需要对我们的线程体加锁,怎么加锁?给方法上,加上同步synchronized,这样,就能够保证我们的线程是一个安全的线程,什么是安全线程,就是意味着,我们所有一起参与竞争的任务,或者说线程,不会造成一个任务或线程调用到一半的时候,另外的任务或者线程参与进来开始调度,当我们将run方法改写为如下后,

public synchronized void run(){...}

于是,,我们在运行一下看下我们的日志输出是什么样的情况:

ii:1currentThreadNo:Thread-0
ii:2currentThreadNo:Thread-0
ii:3currentThreadNo:Thread-2
ii:4currentThreadNo:Thread-2
ii:5currentThreadNo:Thread-2
ii:6currentThreadNo:Thread-2
ii:7currentThreadNo:Thread-2
ii:8currentThreadNo:Thread-2

.

.

.

ii:531currentThreadNo:Thread-3
ii:532currentThreadNo:Thread-30
ii:533currentThreadNo:Thread-17
ii:534currentThreadNo:Thread-28
ii:535currentThreadNo:Thread-24
ii:536currentThreadNo:Thread-13
ii:537currentThreadNo:Thread-26
ii:538currentThreadNo:Thread-9
ii:539currentThreadNo:Thread-22
ii:540currentThreadNo:Thread-18
ii:541currentThreadNo:Thread-14
ii:542currentThreadNo:Thread-10
ii:543currentThreadNo:Thread-5
ii:544currentThreadNo:Thread-6

 

大吃一惊,我们最终输出的结果居然到了544,而我们同时参与并发的线程任务才只有500个,这又是为啥呢?

原来,我们没有对run内部调用的所有代码(我们把所有这些代码看成一个方法,)进行加锁操作,所以,我们需要对方法进行如下改进:

 

@Override

public void run(){

  synchronized(this){

    while(ii<500){ 

      ii++;
      queue.add(ii);
      System.out.println("ii:" + ii + "currentThreadNo:" + Thread.currentThread().getName());

    }

  }

}

 但是,当我们运行多个任务调度的时候,发现,在一个线程参与一次迭代的时候,接下去还是会有其他线程参与竞争,这样的话,问题就来了

 

为什么我们加了锁以后,我们的线程还是不安全,还是会有其他线程参与竞争,,,,这个问题很严重啊

 

于是我们将方法修改如下 :

@Override

public void run(){

  synchronized(ThreadSample.class){

    while(ii<500){ 

      ii++;
      queue.add(ii);
      System.out.println("ii:" + ii + "currentThreadNo:" + Thread.currentThread().getName());

    }

  }

}

这个时候,我们高兴的发现,我们的输出,终于是线程安全的了,就是当我们一个线程执行这个线程体的过程中,其他线程是无法参与进来的,输出如下:

ii:1currentThreadNo:Thread-0 Tue Apr 30 15:18:50 CST 2019
ii:2currentThreadNo:Thread-0 Tue Apr 30 15:18:50 CST 2019
ii:3currentThreadNo:Thread-0 Tue Apr 30 15:18:50 CST 2019
ii:4currentThreadNo:Thread-0 Tue Apr 30 15:18:50 CST 2019

.

.

ii:497currentThreadNo:Thread-0 Tue Apr 30 15:18:50 CST 2019
ii:498currentThreadNo:Thread-0 Tue Apr 30 15:18:50 CST 2019
ii:499currentThreadNo:Thread-0 Tue Apr 30 15:18:50 CST 2019
ii:500currentThreadNo:Thread-0 Tue Apr 30 15:18:50 CST 2019

这样,我们的线程终于安全了。

 

锁:synchronized

 

posted @ 2019-04-28 20:24  菊次郎的幻想  阅读(5295)  评论(1编辑  收藏  举报

begin