疯狂java


您现在的位置: 疯狂软件 >> 新闻资讯 >> 正文

多线程和synchronized


 

 
多线程
  多线程是我们开发人员经常提到的一个名词。为什么会有多线程的概念呢?我们的电脑有可能会有多个cpu(或者CPU有多个内核)这就产生了多个线程。对于单个CPU来说,由于CPU运算很快,我们在电脑上运行多个软件时,每个软件在CPU上运行很短的时间就会切换成其他软件。由于来回切换的时间很短,我们感觉好像所有的程序都在同时运行,这也是多线程。多线程可以解决较多的用户访问同一个服务时压力过大的问题,可以更充分的利用计算机的性能。
 
多线程的问题
  多线程的好处很多,可是相应的也出现了一些问题。其中最常见的就是脏读了。我们的程序,在多个线程访问同一个共享的数据,并且出现了对数据进行修改的时候,就很有可能出现脏读的情况。比如说:我有一个用户名为:yonghu,密码为:123的数据。我有两个线程,第一个线程将用户名密码修改为:yonghu1 : 456 第二个线程负责将修改后的数据读取。并且打印。这个时候,就有可能出现这样的输出:(用户名:yonghu 密码:456)的情况,这就是多线程并发带来的问题。代码如下:
 
1 package jgs_11;
 2
 3 public class DirtyRead {
 4     private String username = "yonghu";
 5     private String password = "123";
 6
      // public synchronized void setValue(String username, String password) {
 7     public void setValue(String username, String password) {
 8         this.username = username;
 9         try {
10             Thread.sleep(2000);
11         } catch (Exception e) {
12             e.printStackTrace();
13         }
14         this.password = password;
15         System.out.println("setValue 最终结果:username =" + username + ", password = " + password);
16     }
17
18     public synchronized void getValue() {
19         System.out.println("getValue 最终结果:username =" + this.username + ", password = " + this.password);
20     }
21
22     public static void main(String[] args) throws Exception {
23         final DirtyRead dr = new DirtyRead();
24         Thread t1 = new Thread(new Runnable() {
25             @Override
26             public void run() {
27                 dr.setValue("yonghu1", "1654");
28             }
29         });
30         t1.start();
31         Thread.sleep(1000);
32         dr.getValue();
33     }
34
35 }
             
   出现这种情况的原因就是两个线程的方法,修改了同一个数据导致了资源在被修改的时候产生了我们不想要的结果。这就是脏读。
 
synchronized关键字
  那么,如何解决这个问题呢?我们可以给这个资源加一个锁,或者给调用这个资源的方法加一个锁。在一个线程对它进行修改的时候,将资源锁住,禁止其他线程访问。等这个线程操作完成后,才允许其他线程进行操作。这样做可以避免多个线程同时操作一个资源的时候出现问题的情况,但是会出现一个线程等待另外一个线程的情况。性能肯定会降低。这个锁的代码很简单,就是想锁住谁就给它前面加个synchronized关键字。然后就可以了,但是原理却是很复杂。
 
  大概是这样子的:其实synchronized的锁锁的都是对象。每个对象都有个类似于标记的东西,当我们执行一段代码,发现有synchronized关键字的时候,线程就去尝试获取这个关键字的锁(将这个标记修改下内容)。如果获取成功了(如果内容为标记值为0那么将值修改为1,表示获取锁成功,如果不为0表示当前锁处于被占用状态则获取锁失败),那么就执行代码。