浅谈下我对JAVA线程的了解。
在讲线程的时候, 我该先讲讲进程 。
那么什么是进程 ?
大家在使用window系统的时候,肯定用过一个功能叫 “ 任务管理器 ”, 在任务管理器里,就有一项叫进程的选项,点进去一探究竟,会发现,
在进程的列表内容里,那里显示着一条又一条运行程序信息, 原来呀,进程就是一个正在执行的程序。
那么什么是线程 ?
其实,线程是进程的一个独立控制单元,线程是控制着进程的执行。一个进程里,至少有一个线程或多个线程。
实例: 进程就好像公司,每家公司一定有个BOSS , 随着公司的日益壮大,BOSS一个人管理不来了,
他就会请外援,帮助管理公司,当然这些是后话了。 我想说的是线程就是那个BOSS ——
不知道大家对于我的举例有没感觉不适合,我个人理解是这样哒,嘿嘿。
既然了解了进程和线程的关系,现在说说JAVA中线程的表现形式:
首先,在java.lang包下,有个名叫 Thread 类, 这就是线程类。 创建线程有2种方式 ——
第一种方式:
class ThreadTest extends Thread { // 1. 定义一个类,继承 Thread 类
public void run() { // 2. 复写 Thread 类中的run方法
System.out.println("It is my create thread .");
}
public static void main(String[] args) {
ThreadTest t = new ThreadTest(); //3. 实例化定义的类
t.start(); //4. 调用父类的start()方法。
}
}
第二种方式:
class ThreadTest implements Runnable { // 1. 定义一个类,实现Runnable接口
public void run() { // 2. 复写 Runnable 接口的run方法
System.out.println("It is my create thread .");
}
public static void main(String[] args) {
ThreadTest t = new ThreadTest(); //3. 实例化定义的类
Thread thread = new Thread(t); //4. 创建线程对象,传入定义的类对象。
thread.start(); //5. 调用线程类的start()方法。
}
}
创建线程2种方式的区别在于:
继承方式:有局限性,JAVA只支持单继承
实现方式:避免了单继承的局限性,同步共享数据及处理。
所以得出的结论是,创建线程使用实现方式,也就是上面的第二种方式。
引进多线程,现在来谈谈它的好处 :
本来一个搬货,现在来了一群人来搬货,答案显而易见, 当然一群人更快搬完货。
这里,就可以得出结论,高效,提高了效率。
俗话说的好, 有利就有弊 —— 多线程也不例外,现在来谈谈它的坏处:
坏处一:共享数据错误
class ThreadTest implements Runnable
{
/* 下面演示卖票的小程序,已知有3个窗口在售票,总共10张票。 */
private int ticket = 10;
public void run()
{
while(ticket>0)
{
try{ Thread.sleep(10); }
catch (Exception e) { e.toString(); }
if(ticket>0)
{
System.out.println(Thread.currentThread().getName()+"......."+ticket--);
}
}
}
public static void main(String[] args)
{
ThreadTest t = new ThreadTest();
Thread t1 = new Thread(t,"NO-1");
Thread t2 = new Thread(t,"NO-2");
Thread t3 = new Thread(t,"NO-3");
t1.start();
t2.start();
t3.start();
/*
print:
NO-2.......10
NO-3.......8
NO-1.......9
NO-2.......7
NO-3.......6
NO-1.......7
NO-2.......5
NO-3.......5
NO-1.......5
NO-2.......4
NO-3.......2
NO-1.......3
NO-2.......1
NO-3.......1
NO-1.......1
从结果发现, 同一样的票,3个线程都有可能同时卖出去了。
这导致了共享数据的错误,其实是不允许这种事情发生的。
*/
}
}
其实,造成这样的问题,原因是数据没有同步,JAVA知道这种问题后提供了一个专业的解决方式,叫同步。
什么是同步呢 ?
假设你要上厕所,你会先敲门看看有没人 , 噢 ~ 有人,我等。
哦 ~ 没人,我进去,并且锁上门,等我上完厕所了,我要解锁才能出门。
假设有一所房子,有个门,你要想进去,你随意 —— ,但是你进去了必须锁门,不能给别人再进来,
只有你出去的时候,把锁解开了,你才可以出去,换别人进来,这种方式在JAVA就叫同步。
同步的关键字是 : synchronized
下面演示同步的使用:
public void run()
{
while(ticket>0)
{
try{ Thread.sleep(10); }
catch (Exception e) { e.toString(); }
synchronized(ThreadTest.class)
{
if(ticket>0)
{
System.out.println(Thread.currentThread().getName()+"......."+ticket--);
}
}
}
}
同步的说明:
1. 被同步所包含的,也就是同步代码块里面的内容,这些内容就是需要被同步的。
2. 同步函数有一个形参,该形参代表一个锁,当线程读到该同步函数时,同步会判断里面是否有其他线程,
如何没有,则拿着锁进去,并且锁上,等执行完后,释放锁,该程序才完成。
3. 同步的形参可以是任意对象,可通过创建Object obj来使用,
4. 必须要有2个或2个以上的线程,才能使用同步函数。 (否则没意义)
同步有2种表现形式,请看以下实例:
//1. 同步在函数上,同步为标记形式
public synchronized void method_1()
{
while(ticket>0)
{
try{ Thread.sleep(10); }
catch (Exception e) { e.toString(); }
if(ticket>0)
System.out.println(Thread.currentThread().getName()+"......."+ticket--);
}
}
public void method_2()
{
while(ticket>0)
{
try{ Thread.sleep(10); }
catch (Exception e) { e.toString(); }
//2. 同步在函数内,同步为函数形式
synchronized(ThreadTest.class)
{
if(ticket>0)
System.out.println(Thread.currentThread().getName()+"......."+ticket--);
}
}
}
这里需要注意的是,同步作为标记形式的时候:
非静态函数的锁是: this , 也就是当前对象。
静态函数的锁是: 类名.class
坏处二:线程中的死锁
同步也有一个小BUG, 那就是引发死锁 ,那什么是死锁呢 ?
其实就是同步嵌套同步,
示例: A和B在吃饭,可是每人只有一支筷子,2个人都想吃饭,于是A跟B要筷子,可是B也跟A要筷子,
2个人都互不让步,结果谁也没吃上饭 。 这种互不让步的状态,就是死锁了。
下面是死锁的示例:
class MyLock
{
<span style="white-space:pre"> </span>static Object locka = new Object();
<span style="white-space:pre"> </span>static Object lockb = new Object();
}
class ThreadTest implements Runnable
{<span style="white-space:pre"> </span>
<span style="white-space:pre"> </span>private boolean flag;
<span style="white-space:pre"> </span>ThreadTest(boolean flag)
<span style="white-space:pre"> </span>{
<span style="white-space:pre"> </span>this.flag = flag;
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>public void run()
<span style="white-space:pre"> </span>{
<span style="white-space:pre"> </span>if(flag)
<span style="white-space:pre"> </span>{
<span style="white-space:pre"> </span>synchronized(MyLock.locka)
<span style="white-space:pre"> </span>{
<span style="white-space:pre"> </span>System.out.println("if --- locka");
<span style="white-space:pre"> </span>synchronized(MyLock.lockb)
<span style="white-space:pre"> </span>{
<span style="white-space:pre"> </span>System.out.println("if --- lockb");
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>}else
<span style="white-space:pre"> </span>{
<span style="white-space:pre"> </span>synchronized(MyLock.lockb)
<span style="white-space:pre"> </span>{
<span style="white-space:pre"> </span>System.out.println("else --- lockb");
<span style="white-space:pre"> </span>synchronized(MyLock.locka)
<span style="white-space:pre"> </span>{
<span style="white-space:pre"> </span>System.out.println("else --- locka");
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>public static void main(String[] args)
<span style="white-space:pre"> </span>{<span style="white-space:pre"> </span>
<span style="white-space:pre"> </span>Thread t1 = new Thread(new ThreadTest(true));
<span style="white-space:pre"> </span>Thread t2 = new Thread(new ThreadTest(false));
<span style="white-space:pre"> </span>t1.start();
<span style="white-space:pre"> </span>t2.start();
<span style="white-space:pre"> </span>}
}
在实际的开发中,我们要尽可能的避免死锁。
----------------------------------------------------------------------------------------------------------------------------------