对线程做一个简单的分析。对以后的线程相关的都会有帮助,再者说线程也算是java的基本功了。就诸如武林秘籍中的易筋经!一定得学好,今天我就对线程做一个自己理解版的分析:
Q1:首先我们得明白什么是线程?
A1:首先我个人认为线程是一个程序的执行线索,小于一个进程,是内存、CPU占用的基本单位。
Q2:什么叫做线程互斥,他会导致什么后果?
A2:如果是这个问题,我会举个栗子(例子)来分析
ForExample:
--->
- 1、有人往你账号上打了2000RMB:1000RMB+2000RMB;
- 2、你刚好在淘宝上买了一件200RMB的帅气衣服:1000RMB-200RMB
- 3、由于没有线程同步,可怕的事情发生了!在做完被转账的1000+2000,但没有被赋值时、另一个线程做完了1000-200的线程,并把800赋值了到了1000+2000的值上!
- 4、啊啊啊,我的钱哪里去了!我的2800怎么变成了800!次奥啊!所以没有线程同步的你开始抓狂了!
这是一件多么可怕的事情啊!可见有些时候,线程互斥是多么的重要,只要是遇上两个线程会共享一个数据存储的时候,都要记得加上线程互斥!切记。(线程互斥就是指排他性,不允许多个线程同时访问一个共享资源,本质上来说线程互斥是一种特殊的线程同步,两者的概念不能混为一谈)
接下来让我们看看实例,一段java代码没有加互斥:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
| public class TraditionalThreadSynchronized { public static void main(String[] args) { new TraditionalThreadSynchronized().init(); } private void init() { final Outputer outputer = new Outputer(); new Thread(new Runnable() { @Override public void run() { while (true) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } outputer.output("zhushiqing"); } } }).start(); new Thread(new Runnable() { @Override public void run() { while (true) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } outputer.output("qianjiangqi"); } } }).start(); new Thread(new Runnable() { @Override public void run() { while (true) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } outputer.output("wangtianyi"); } } }).start(); } static class Outputer { public void output(String name) { int len = name.length(); for (int i = 0; i < len; i++) { System.out.print(name.charAt(i)); } System.out.println(); } } }
|
结果就是各种名字都不能好好输出!连名字都输不完整!我们能不能做朋友了!当然不能了!
所以,我们得给他们加上一个锁!synchronized关键字来修饰(可以修饰方法,也可以修饰方法里的代码块),而且是有锁当然得有钥匙是吧!这里的钥匙是指,被自己标志的一个上锁的对象(监视器),
比如说:
而且钥匙必须得是同一把,所以我们可以定义一把钥匙,如下
在代码块上加锁(监视器)的情况
1 2 3 4 5 6 7 8 9 10 11 12
| static class Outputer { public void output(String name) { int len = name.length(); String xxx = ""; sychronized("xxx"){ for (int i = 0; i < len; i++) { System.out.print(name.charAt(i)); } System.out.println(); } } }
|
这样就给自己定义了一把钥匙(监视器),起多个这线程都不会再出现名字念不完整的情况了!我们又可以做朋友了!但是呢!我们这样做是多此一举的。一般来说,直接sychronized里面+(this)即可,直接代表了这个线程对象!也是唯一的!其实把到这样就差不多了!
还有就是直接给方法上加上synchronized关键字:
1 2 3 4 5 6 7 8 9
| public synchronized void output2(String name) { int len = name.length(); for (int i = 0; i < len; i++) { System.out.print(name.charAt(i)); } System.out.println(); }
|
这样就不用再考虑对象的问题了,它默认都是用this
其实说到这里就差不多了,但是我觉得呢!还有一种情况:假如你用静态方法还行得通不?
代码如下,你觉得这样两个线程会好好运行吗?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| public synchronized void output(String name) { public void output(String name) { int len = name.length(); sychronized(this){ for (int i = 0; i < len; i++) { System.out.print(name.charAt(i)); } System.out.println(); } } } public static synchronized void output3(String name) { int len = name.length(); for (int i = 0; i < len; i++) { System.out.print(name.charAt(i)); } System.out.println(); }
|
答案是不互斥的!
为什么呢?因为static静态方法是随着类加载而加载的,所以他的对象锁是Outputer.class字节码,而output方法的对象所是this,不过只要把this换成Outputer.class两者就互斥了!
今天又对thread加深了了解!为自己赞一下!