- 浏览: 50517 次
- 性别:
- 来自: 合肥
文章分类
最新评论
java学习日记(线程)
一、线程的概念:
线程与进程相似,是一段完成某个特定功能的代码,是程序中单个顺序的流控制;但与进程不同的是,同类的多个线程是共享一块内存空间和一组系统资源,而线程本身的数据通常只有微处理器的寄存器数据,以及一个供程序执行时使用的堆栈。所以系统在产生一个线程,或者在各个线程之间切换时,负担要比进程小的多,正因如此,线程被称为轻负荷进程(light-weight process)。一个进程中可以包含多个线程。
一个线程是一个程序内部的顺序控制流。
1. 进程:每个进程都有独立的代码和数据空间(进程上下文) ,进程切换的开销大。
2. 线程:轻量的进程,同一类线程共享代码和数据空间,每个线程有独立的运行栈和程序计数器(PC),线程切换的开销小。
3. 多进程:在操作系统中,能同时运行多个任务程序。
4. 多线程:在同一应用程序中,有多个顺序流同时执行。
Java内在支持多线程,它的所有类都是在多线程下定义的,Java利用多线程使整个系统成为异步系统。
1. 虚拟的CPU,封装在java.lang.Thread类中。
2. CPU所执行的代码,传递给Thread类。
3. CPU所处理的数据,传递给Thread类。
二、线程的构造
线程实例表示Java解释器中的真正的线程,通过它可以启动线程、终止线程、线程挂起等,每个线程都是通过类Thread在Java的软件包Java.lang中定义,它的构造方法为:
public Thread (ThreadGroup group,Runnable target,String name);
其中,group 指明该线程所属的线程组;target实际执行线程体的目标对象,它必须实现接口Runnable; name为线程名。Java中的每个线程都有自己的名称,Java提供了不同Thread类构造器,允许给线程指定名称。如果name为null时,则 Java自动提供唯一的名称。
当上述构造方法的某个参数为null时,我们可得到下面的几个构造方法:
public Thread ();
public Thread (Runnable target);
public Thread (Runnable target,String name);
public Thread (String name);
public Thread (ThreadGroup group,Runnable target);
public Thread (ThreadGroup group,String name);
一个类声明实现Runnable接口就可以充当线程体,在接口Runnable中只定义了一个方法 run():
public void run();
任何实现接口Runnable的对象都可以作为一个线程的目标对象,类Thread本身也实现了接口Runnable,因此我们可以通过两种方法实现线程体。
(一)定义一个线程类,它继承线程类Thread并重写其中的方法 run(),这时在初始化这个类的实例时,目标target可为null,表示由这个实例对来执行线程体。由于Java只支持单重继承,用这种方法定义的类不能再继承其它父类。
(二)提供一个实现接口Runnable的类作为一个线程的目标对象,在初始化一个Thread类或者Thread子类的线程对象时,把目标对象传递给这个线程实例,由该目标对象提供线程体 run()。这时,实现接口Runnable的类仍然可以继承其它父类。
三、线程的状态
每个线程都是通过某个特定Thread对象的方法run( )来完成其操作的,方法run( )称为线程体。下图表示了java线程的不同状态以及状态之间转换所调用的方法。
1. 创建状态(new Thread)
执行下列语句时,线程就处于创建状态:
Thread myThread = new Thread( );
当一个线程处于创建状态时,它仅仅是一个空的线程对象,系统不为它分配资源。
2. 可运行状态( Runnable )
Thread myThread = new Thread( );
myThread.start( );
当一个线程处于可运行状态时,系统为这个线程分配了它需的系统资源,安排其运行并调用线程运行方法,这样就使得该线程处于可运行( Runnable )状态。需要注意的是这一状态并不是运行中状态(Running ),因为线程也许实际上并未真正运行。由于很多计算机都是单处理器的,所以要在同一时刻运行所有的处于可运行状态的线程是不可能的,Java的运行系统必须实现调度来保证这些线程共享处理器。
3. 不可运行状态(Not Runnable)
进入不可运行状态的原因有如下几条:
1) 调用了sleep()方法;
2) 调用了suspend()方法;
3) 为等候一个条件变量,线程调用wait()方法;
4) 输入输出流中发生线程阻塞;
不可运行状态也称为阻塞状态(Blocked)。因为某种原因(输入/输出、等待消息或其它阻塞情况),系统不能执行线程的状态。这时即使处理器空闲,也不能执行该线程。
4. 死亡状态(Dead)
线程的终止一般可通过两种方法实现:自然撤消(线程执行完)或是被停止(调用stop()方法)。目前不推荐通过调用stop()来终止线程的执行,而是让线程执行完。
四、有关线程的一些长用的方法
1. sleep(long millis)
这个方法是一个静态的方法,也就是说我们可以直接调用它,如Thread.sleep(5000)就是指让目前正在运行的线程先停下工作等待5000毫秒。有一点需要注意的是:不能肯定这个线程在过5000毫秒肯定会立刻被执行。
2.interrupt()
这个方法用来打断一个线程(感觉说睡眠中的进程更加合适)。这个方法的作用可以举个例子来看一下:
public class TestInterrupt extends Thread
{
/** Creates a new instance of TestInterrupt */
public TestInterrupt()
{
}
public void run()
{
try
{
for ( int i=0; i<5; i++)
{
System.out.println("running the first loop " + i);
}
Thread.sleep(10000);
for ( int i=6; i<10; i++)
{
System.out.println("running the second loop" + i);
}
}catch (InterruptedException ie)
{
System.out.println("Sleep interrupted in run()");
for ( int i=11; i<15; i++)
{
System.out.println("running the third loop" + i);
}
}
}
public static void main(String[] args)
{
TestInterrupt ti = new TestInterrupt();
Thread t = new Thread(ti);
t.start();
//Delay for a few seconds to let the other thread get going
try
{
Thread.sleep(2500);
}catch (InterruptedException ie)
{
System.out.println("Sleep interrupted in main()");
}
System.out.println("About to wake up the other thread");
t.interrupt();
System.out.println("Exiting from Main");
}
}
上面的例子中假如没有t.interropt()的话,程序运行做的就是
for ( int i=6; i<10; i++)
{
System.out.println("running the second loop" + i);
}
加上以后做的就是catch中的内容了.
3.join()和join(long millis)
join()这个函数的作用是使得目前正在运行的线程假如为a停下来,一直到调用join()方法的这个线程b被执行完毕,再继续一开始的线程a;
看个例子好了:
public class TestJoin1 extends Thread
{
/** Creates a new instance of TestJoin1 */
public TestJoin1()
{
}
public void run()
{
try
{
for ( int i=0; i<5; i++)
{
System.out.println("running the first loop " + i);
}
Thread.sleep(1000);
for ( int i=6; i<10; i++)
{
System.out.println("running the second loop" + i);
}
}catch (InterruptedException ie)
{
System.out.println("Sleep interrupted in run()");
}
}
public static void main(String[] args)
{
try
{
TestJoin1 ti = new TestJoin1();
Thread t = new Thread(ti);
t.start();
t.join();
for ( int i=11; i<15; i++)
{
System.out.println("running the third loop" + i);
}
}catch (InterruptedException ie)
{
System.out.println("Join interrupted in run()");
}
System.out.println("Exiting from Main");
}
}
这个程序的结果是先让t.join()时刻正在运行的线程(其实就是main)被搁置,做完了t这个线程的所有内容,再回到main线程继续做的 t.join();语句后剩下的内容.假如把t.join()这行去掉的话,在一般的计算机上跑出来的结果应该是先做了main所有的内容再去做t线程的内容.
join(long millis)这个方法和join()方法差不多,都是正在执行的线程a被搁置,去做调用join(long millis)这个方法的线程b的run里面的内容。但后面的millis这个参数决定了b这个线程能被优先运行多少时间(millis代表多少毫秒),millis豪秒过后b线程即使没有运行完毕,也会回到线程a.
底下的一个程序能很好的说明这个问题:
public class TestJoin2 extends Thread
{
/** Creates a new instance of TestJoin2 */
public TestJoin2()
{
}
public void run()
{
try
{
for ( int i=0; i<5; i++)
{
System.out.println("running the first loop " + i);
}
Thread.sleep(3500);
for ( int i=6; i<10; i++)
{
System.out.println("running the second loop" + i);
}
}catch (InterruptedException ie)
{
System.out.println("Sleep interrupted in run()");
}
}
public static void main(String[] args)
{
try
{
TestJoin2 t2 = new TestJoin2();
Thread t = new Thread(t2);
t.start();
t.join(3000);
for ( int i=11; i<15; i++)
{
System.out.println("running the third loop" + i);
}
}catch (InterruptedException ie)
{
System.out.println("Join interrupted in run()");
}
System.out.println("Exiting from Main");
}
}
看了这么多以后,好象很容易产生一种误解join()这个函数就是让调用这个方法的线程b优先(第一个)被执行.其实事实并不是这样的,join()的作用如上面所说的,它只能让目前运行的线程a搁置等,等b执行完毕再开始执行a.
底下的程序可以让人消除这中误解:
public class Test extends Thread
{
public Test(String a)
{
super(a);
}
public void run()
{
System.out.println(this.getName());
}
public static void main(String [] args)
{
Test a = new Test("a");
Test b = new Test("b");
Test c = new Test("c");
a.start();
b.start();
c.start();
try
{
c.join();
}
catch(Exception e){
}
System.out.println("This is Main!");
}
}
看了运行结果是a,b先被执行了,然后才是c,最后是main^^;
4.关于synchronized
这个关键字出现的目的是为了让几个线程能同步,举一个最简单的例子。一个电影院有20张票要卖,它有3个售票员。
写个程序来证实一下不用synchronized的结果好了,需要使用sleep()函数来制造出这种可能(线程的执行时机谁也不能预料)出现的情况:
public class Sell
{
public static void main(String [] args)
{
SellThread sell = new SellThread();
Thread sell1 = new Thread(sell,"sellman1");
Thread sell2 = new Thread(sell,"sellman2");
Thread sell3 = new Thread(sell,"sellman3");
sell1.start();
sell2.start();
sell3.start();
}
}
class SellThread implements Runnable
{
private int i = 20;
public void run()
{
while(true)
{
if( i > 0)
{
try
{
Thread.sleep(100);
} catch(Exception e)
{
}
System.out.println(Thread.currentThread().getName() + " sell " + i--);
}
}
}
}
结果一共卖掉了22张票(估计电影院以为无缘无故多收了门票钱会很高兴,不过一会也许就要面对愤怒的顾客了....)
这个时候我们的synchronized应该发挥作用了^^修改程序如下:
public class Sell2
{
public static void main(String [] args)
{
SellThread sell = new SellThread();
Thread sell1 = new Thread(sell,"sellman1");
Thread sell2 = new Thread(sell,"sellman2");
Thread sell3 = new Thread(sell,"sellman3");
sell1.start();
sell2.start();
sell3.start();
}
}
class SellThread implements Runnable
{
private int i = 20;
String a = "now ok!";
public void run()
{
while(true)
{
synchronized(a)
{
if( i > 0)
{
try
{
Thread.sleep(100);
} catch(Exception e)
{
}
System.out.println(Thread.currentThread().getName() + " sell " + i--);
}
}
}
}
}
这样就好了只会卖20张票了, synchronized()中的括号中需要的是一个class的对象所以我们不能直接在括号中写上i,就定义了一个String的对象a,a的标志旗 (不知道说什么更合适)本来为1代表大家都能使用,这样一个售票员selln的卖票线程拿到了a以后他就可以开始卖票,同时他把a这个对象标志旗置为0, 然后其他售票员卖票的线程发现他们拿不到a这个对象了就只先搁置了.一直到selln的卖票线程释放了a,a的标志旗就又变成了1,这个时候其他售票员的卖票的线程就可以竞争了,看谁先拿到a这个对象.不过String a和卖票没什么关系,所以我们可以用this来代替synchronized()中的a,它和a的效果一样表示谁拿到了this对象才能执行.
这里有两个容易误解的地方:
(1).一个线程拿到synchronized的括号中的对象之后,其他也要需要拿到这个对象才能运行的线程不能被执行了.其实是其他线程也是可以执行的,但他们执行到了需要synchronized中对象的时候,他们发现对象的标志旗为0,所以只能又被搁置了。(看来幸运女神只能同时光顾一个人^^)所以我们用synchronized来使得线程同步的时候是以牺牲效率为代价的,所以不需要使用的地方就别用好了.
(2),一个线程拿到synchronized的括号中的对象之后,其他任何线程都不能执行了,其实假如其他不需要synchronized的对象才能继续执行的线程还是可以和拿到synchronized的括号中的对象的线程一起运行的。
有的方法前面被加上了synchronized.其实这个时候就是把这个方法的调用者,也就是this的标志旗置0了,他不能和其他需要this才能运行的线程一起执行,但可以和其他不需要这个this对象的线程一起运行。
5.wait()和notify()或者notifyAll()
这个几个函数是为了使得几个同步的线程按照一定的先后顺序执行。
都是和synchronized()一起使用,设()中对象为Obj吧。
有一点需要注意的是,我们应该让需要Obj.wait的线程先启动。因为执行顺序是需要Obj.wait()的线程a先启动,然后它运行到 Obj.wait()的时候,进入搁置状态,让其他线程先执行。于是带用Obj.notify()的线程b开始执行了,一直到b执行到了 Obj.notify()以后(Obj.notify()实际上就是通知因为Obj.wait()被搁置的线程:"轮到你了"),b被搁置,然后继续做a 的Obj.wait()以后的内容.
所以我们假如让带有Obj.notify()的线程b先运行的话,那么b执行完毕以后,b执行的Obj.notify()没有找到任何在因 Obj.wait()而进入搁置状态的线程.然后开始做带有Obj.wait()的线程a的话.a运行到了Obj.wait()就进入搁置状态,等待另外一个线程中的Obj.notify()来唤醒它,不过可惜它永远也等不到了,因为带有Obj.notify()的线程已经到搁置的线程中来找过它一次,很可惜的是没找到.于是线程a就一直搁置了...(感觉有点像爱情剧...);
举个例子好了:
public class ThreadTest
{
public static void main(String [] args)
{
Storage stor = new Storage();
Counter a = new Counter(stor);
Printer b = new Printer(stor);
a.start();
b.start();
}
}
class Storage
{
public int i = 0;
}
class Counter extends Thread
{
private Storage a;
public Counter(Storage stor)
{
a = stor;
}
public void run()
{
System.out.println("Hi");
try
{
sleep(100);
}
catch(Exception e) {}
int i = 0;
while(i < 5)
{
synchronized(a)
{
System.out.println("Counter");
a.i = (int)(Math.random()*50);
System.out.println(a.i);
a.notify();
}
System.out.println("Counter2");
++i;
}
}
}
class Printer extends Thread
{
private Storage a;
public Printer(Storage stor)
{
a = stor;
}
public void run()
{
int i = 0;
while(i < 5)
{
synchronized(a)
{
System.out.println("Printer");
try{
a.wait();
}
catch(InterruptedException e) {}
System.out.println(a.i);
}
System.out.println("Printer2");
++i;
}
}
}
运行好了以后把
try
{
sleep(100);
}
catch(Exception e) {}
这几行注释掉再看看运行结果吧。
还有两点说下:
(1).假如几个线程因为Obj.wait()进入搁置的话,那么只要一个Obj.notifyAll()执行以后,他们都处于可以运行状态,不过到底谁先运行我们不就知道。
(2).sleep()和wait()有时候可以执行相同的功能不过要注意的是thread.sleep(long a)过了a毫秒以后,表示可以开始执行了,不代表thread立刻被执行。thread.wait()一但接受到了thread.notify()以后是立刻被执行的.
一、线程的概念:
线程与进程相似,是一段完成某个特定功能的代码,是程序中单个顺序的流控制;但与进程不同的是,同类的多个线程是共享一块内存空间和一组系统资源,而线程本身的数据通常只有微处理器的寄存器数据,以及一个供程序执行时使用的堆栈。所以系统在产生一个线程,或者在各个线程之间切换时,负担要比进程小的多,正因如此,线程被称为轻负荷进程(light-weight process)。一个进程中可以包含多个线程。
一个线程是一个程序内部的顺序控制流。
1. 进程:每个进程都有独立的代码和数据空间(进程上下文) ,进程切换的开销大。
2. 线程:轻量的进程,同一类线程共享代码和数据空间,每个线程有独立的运行栈和程序计数器(PC),线程切换的开销小。
3. 多进程:在操作系统中,能同时运行多个任务程序。
4. 多线程:在同一应用程序中,有多个顺序流同时执行。
Java内在支持多线程,它的所有类都是在多线程下定义的,Java利用多线程使整个系统成为异步系统。
1. 虚拟的CPU,封装在java.lang.Thread类中。
2. CPU所执行的代码,传递给Thread类。
3. CPU所处理的数据,传递给Thread类。
二、线程的构造
线程实例表示Java解释器中的真正的线程,通过它可以启动线程、终止线程、线程挂起等,每个线程都是通过类Thread在Java的软件包Java.lang中定义,它的构造方法为:
public Thread (ThreadGroup group,Runnable target,String name);
其中,group 指明该线程所属的线程组;target实际执行线程体的目标对象,它必须实现接口Runnable; name为线程名。Java中的每个线程都有自己的名称,Java提供了不同Thread类构造器,允许给线程指定名称。如果name为null时,则 Java自动提供唯一的名称。
当上述构造方法的某个参数为null时,我们可得到下面的几个构造方法:
public Thread ();
public Thread (Runnable target);
public Thread (Runnable target,String name);
public Thread (String name);
public Thread (ThreadGroup group,Runnable target);
public Thread (ThreadGroup group,String name);
一个类声明实现Runnable接口就可以充当线程体,在接口Runnable中只定义了一个方法 run():
public void run();
任何实现接口Runnable的对象都可以作为一个线程的目标对象,类Thread本身也实现了接口Runnable,因此我们可以通过两种方法实现线程体。
(一)定义一个线程类,它继承线程类Thread并重写其中的方法 run(),这时在初始化这个类的实例时,目标target可为null,表示由这个实例对来执行线程体。由于Java只支持单重继承,用这种方法定义的类不能再继承其它父类。
(二)提供一个实现接口Runnable的类作为一个线程的目标对象,在初始化一个Thread类或者Thread子类的线程对象时,把目标对象传递给这个线程实例,由该目标对象提供线程体 run()。这时,实现接口Runnable的类仍然可以继承其它父类。
三、线程的状态
每个线程都是通过某个特定Thread对象的方法run( )来完成其操作的,方法run( )称为线程体。下图表示了java线程的不同状态以及状态之间转换所调用的方法。
1. 创建状态(new Thread)
执行下列语句时,线程就处于创建状态:
Thread myThread = new Thread( );
当一个线程处于创建状态时,它仅仅是一个空的线程对象,系统不为它分配资源。
2. 可运行状态( Runnable )
Thread myThread = new Thread( );
myThread.start( );
当一个线程处于可运行状态时,系统为这个线程分配了它需的系统资源,安排其运行并调用线程运行方法,这样就使得该线程处于可运行( Runnable )状态。需要注意的是这一状态并不是运行中状态(Running ),因为线程也许实际上并未真正运行。由于很多计算机都是单处理器的,所以要在同一时刻运行所有的处于可运行状态的线程是不可能的,Java的运行系统必须实现调度来保证这些线程共享处理器。
3. 不可运行状态(Not Runnable)
进入不可运行状态的原因有如下几条:
1) 调用了sleep()方法;
2) 调用了suspend()方法;
3) 为等候一个条件变量,线程调用wait()方法;
4) 输入输出流中发生线程阻塞;
不可运行状态也称为阻塞状态(Blocked)。因为某种原因(输入/输出、等待消息或其它阻塞情况),系统不能执行线程的状态。这时即使处理器空闲,也不能执行该线程。
4. 死亡状态(Dead)
线程的终止一般可通过两种方法实现:自然撤消(线程执行完)或是被停止(调用stop()方法)。目前不推荐通过调用stop()来终止线程的执行,而是让线程执行完。
四、有关线程的一些长用的方法
1. sleep(long millis)
这个方法是一个静态的方法,也就是说我们可以直接调用它,如Thread.sleep(5000)就是指让目前正在运行的线程先停下工作等待5000毫秒。有一点需要注意的是:不能肯定这个线程在过5000毫秒肯定会立刻被执行。
2.interrupt()
这个方法用来打断一个线程(感觉说睡眠中的进程更加合适)。这个方法的作用可以举个例子来看一下:
public class TestInterrupt extends Thread
{
/** Creates a new instance of TestInterrupt */
public TestInterrupt()
{
}
public void run()
{
try
{
for ( int i=0; i<5; i++)
{
System.out.println("running the first loop " + i);
}
Thread.sleep(10000);
for ( int i=6; i<10; i++)
{
System.out.println("running the second loop" + i);
}
}catch (InterruptedException ie)
{
System.out.println("Sleep interrupted in run()");
for ( int i=11; i<15; i++)
{
System.out.println("running the third loop" + i);
}
}
}
public static void main(String[] args)
{
TestInterrupt ti = new TestInterrupt();
Thread t = new Thread(ti);
t.start();
//Delay for a few seconds to let the other thread get going
try
{
Thread.sleep(2500);
}catch (InterruptedException ie)
{
System.out.println("Sleep interrupted in main()");
}
System.out.println("About to wake up the other thread");
t.interrupt();
System.out.println("Exiting from Main");
}
}
上面的例子中假如没有t.interropt()的话,程序运行做的就是
for ( int i=6; i<10; i++)
{
System.out.println("running the second loop" + i);
}
加上以后做的就是catch中的内容了.
3.join()和join(long millis)
join()这个函数的作用是使得目前正在运行的线程假如为a停下来,一直到调用join()方法的这个线程b被执行完毕,再继续一开始的线程a;
看个例子好了:
public class TestJoin1 extends Thread
{
/** Creates a new instance of TestJoin1 */
public TestJoin1()
{
}
public void run()
{
try
{
for ( int i=0; i<5; i++)
{
System.out.println("running the first loop " + i);
}
Thread.sleep(1000);
for ( int i=6; i<10; i++)
{
System.out.println("running the second loop" + i);
}
}catch (InterruptedException ie)
{
System.out.println("Sleep interrupted in run()");
}
}
public static void main(String[] args)
{
try
{
TestJoin1 ti = new TestJoin1();
Thread t = new Thread(ti);
t.start();
t.join();
for ( int i=11; i<15; i++)
{
System.out.println("running the third loop" + i);
}
}catch (InterruptedException ie)
{
System.out.println("Join interrupted in run()");
}
System.out.println("Exiting from Main");
}
}
这个程序的结果是先让t.join()时刻正在运行的线程(其实就是main)被搁置,做完了t这个线程的所有内容,再回到main线程继续做的 t.join();语句后剩下的内容.假如把t.join()这行去掉的话,在一般的计算机上跑出来的结果应该是先做了main所有的内容再去做t线程的内容.
join(long millis)这个方法和join()方法差不多,都是正在执行的线程a被搁置,去做调用join(long millis)这个方法的线程b的run里面的内容。但后面的millis这个参数决定了b这个线程能被优先运行多少时间(millis代表多少毫秒),millis豪秒过后b线程即使没有运行完毕,也会回到线程a.
底下的一个程序能很好的说明这个问题:
public class TestJoin2 extends Thread
{
/** Creates a new instance of TestJoin2 */
public TestJoin2()
{
}
public void run()
{
try
{
for ( int i=0; i<5; i++)
{
System.out.println("running the first loop " + i);
}
Thread.sleep(3500);
for ( int i=6; i<10; i++)
{
System.out.println("running the second loop" + i);
}
}catch (InterruptedException ie)
{
System.out.println("Sleep interrupted in run()");
}
}
public static void main(String[] args)
{
try
{
TestJoin2 t2 = new TestJoin2();
Thread t = new Thread(t2);
t.start();
t.join(3000);
for ( int i=11; i<15; i++)
{
System.out.println("running the third loop" + i);
}
}catch (InterruptedException ie)
{
System.out.println("Join interrupted in run()");
}
System.out.println("Exiting from Main");
}
}
看了这么多以后,好象很容易产生一种误解join()这个函数就是让调用这个方法的线程b优先(第一个)被执行.其实事实并不是这样的,join()的作用如上面所说的,它只能让目前运行的线程a搁置等,等b执行完毕再开始执行a.
底下的程序可以让人消除这中误解:
public class Test extends Thread
{
public Test(String a)
{
super(a);
}
public void run()
{
System.out.println(this.getName());
}
public static void main(String [] args)
{
Test a = new Test("a");
Test b = new Test("b");
Test c = new Test("c");
a.start();
b.start();
c.start();
try
{
c.join();
}
catch(Exception e){
}
System.out.println("This is Main!");
}
}
看了运行结果是a,b先被执行了,然后才是c,最后是main^^;
4.关于synchronized
这个关键字出现的目的是为了让几个线程能同步,举一个最简单的例子。一个电影院有20张票要卖,它有3个售票员。
写个程序来证实一下不用synchronized的结果好了,需要使用sleep()函数来制造出这种可能(线程的执行时机谁也不能预料)出现的情况:
public class Sell
{
public static void main(String [] args)
{
SellThread sell = new SellThread();
Thread sell1 = new Thread(sell,"sellman1");
Thread sell2 = new Thread(sell,"sellman2");
Thread sell3 = new Thread(sell,"sellman3");
sell1.start();
sell2.start();
sell3.start();
}
}
class SellThread implements Runnable
{
private int i = 20;
public void run()
{
while(true)
{
if( i > 0)
{
try
{
Thread.sleep(100);
} catch(Exception e)
{
}
System.out.println(Thread.currentThread().getName() + " sell " + i--);
}
}
}
}
结果一共卖掉了22张票(估计电影院以为无缘无故多收了门票钱会很高兴,不过一会也许就要面对愤怒的顾客了....)
这个时候我们的synchronized应该发挥作用了^^修改程序如下:
public class Sell2
{
public static void main(String [] args)
{
SellThread sell = new SellThread();
Thread sell1 = new Thread(sell,"sellman1");
Thread sell2 = new Thread(sell,"sellman2");
Thread sell3 = new Thread(sell,"sellman3");
sell1.start();
sell2.start();
sell3.start();
}
}
class SellThread implements Runnable
{
private int i = 20;
String a = "now ok!";
public void run()
{
while(true)
{
synchronized(a)
{
if( i > 0)
{
try
{
Thread.sleep(100);
} catch(Exception e)
{
}
System.out.println(Thread.currentThread().getName() + " sell " + i--);
}
}
}
}
}
这样就好了只会卖20张票了, synchronized()中的括号中需要的是一个class的对象所以我们不能直接在括号中写上i,就定义了一个String的对象a,a的标志旗 (不知道说什么更合适)本来为1代表大家都能使用,这样一个售票员selln的卖票线程拿到了a以后他就可以开始卖票,同时他把a这个对象标志旗置为0, 然后其他售票员卖票的线程发现他们拿不到a这个对象了就只先搁置了.一直到selln的卖票线程释放了a,a的标志旗就又变成了1,这个时候其他售票员的卖票的线程就可以竞争了,看谁先拿到a这个对象.不过String a和卖票没什么关系,所以我们可以用this来代替synchronized()中的a,它和a的效果一样表示谁拿到了this对象才能执行.
这里有两个容易误解的地方:
(1).一个线程拿到synchronized的括号中的对象之后,其他也要需要拿到这个对象才能运行的线程不能被执行了.其实是其他线程也是可以执行的,但他们执行到了需要synchronized中对象的时候,他们发现对象的标志旗为0,所以只能又被搁置了。(看来幸运女神只能同时光顾一个人^^)所以我们用synchronized来使得线程同步的时候是以牺牲效率为代价的,所以不需要使用的地方就别用好了.
(2),一个线程拿到synchronized的括号中的对象之后,其他任何线程都不能执行了,其实假如其他不需要synchronized的对象才能继续执行的线程还是可以和拿到synchronized的括号中的对象的线程一起运行的。
有的方法前面被加上了synchronized.其实这个时候就是把这个方法的调用者,也就是this的标志旗置0了,他不能和其他需要this才能运行的线程一起执行,但可以和其他不需要这个this对象的线程一起运行。
5.wait()和notify()或者notifyAll()
这个几个函数是为了使得几个同步的线程按照一定的先后顺序执行。
都是和synchronized()一起使用,设()中对象为Obj吧。
有一点需要注意的是,我们应该让需要Obj.wait的线程先启动。因为执行顺序是需要Obj.wait()的线程a先启动,然后它运行到 Obj.wait()的时候,进入搁置状态,让其他线程先执行。于是带用Obj.notify()的线程b开始执行了,一直到b执行到了 Obj.notify()以后(Obj.notify()实际上就是通知因为Obj.wait()被搁置的线程:"轮到你了"),b被搁置,然后继续做a 的Obj.wait()以后的内容.
所以我们假如让带有Obj.notify()的线程b先运行的话,那么b执行完毕以后,b执行的Obj.notify()没有找到任何在因 Obj.wait()而进入搁置状态的线程.然后开始做带有Obj.wait()的线程a的话.a运行到了Obj.wait()就进入搁置状态,等待另外一个线程中的Obj.notify()来唤醒它,不过可惜它永远也等不到了,因为带有Obj.notify()的线程已经到搁置的线程中来找过它一次,很可惜的是没找到.于是线程a就一直搁置了...(感觉有点像爱情剧...);
举个例子好了:
public class ThreadTest
{
public static void main(String [] args)
{
Storage stor = new Storage();
Counter a = new Counter(stor);
Printer b = new Printer(stor);
a.start();
b.start();
}
}
class Storage
{
public int i = 0;
}
class Counter extends Thread
{
private Storage a;
public Counter(Storage stor)
{
a = stor;
}
public void run()
{
System.out.println("Hi");
try
{
sleep(100);
}
catch(Exception e) {}
int i = 0;
while(i < 5)
{
synchronized(a)
{
System.out.println("Counter");
a.i = (int)(Math.random()*50);
System.out.println(a.i);
a.notify();
}
System.out.println("Counter2");
++i;
}
}
}
class Printer extends Thread
{
private Storage a;
public Printer(Storage stor)
{
a = stor;
}
public void run()
{
int i = 0;
while(i < 5)
{
synchronized(a)
{
System.out.println("Printer");
try{
a.wait();
}
catch(InterruptedException e) {}
System.out.println(a.i);
}
System.out.println("Printer2");
++i;
}
}
}
运行好了以后把
try
{
sleep(100);
}
catch(Exception e) {}
这几行注释掉再看看运行结果吧。
还有两点说下:
(1).假如几个线程因为Obj.wait()进入搁置的话,那么只要一个Obj.notifyAll()执行以后,他们都处于可以运行状态,不过到底谁先运行我们不就知道。
(2).sleep()和wait()有时候可以执行相同的功能不过要注意的是thread.sleep(long a)过了a毫秒以后,表示可以开始执行了,不代表thread立刻被执行。thread.wait()一但接受到了thread.notify()以后是立刻被执行的.
发表评论
-
oracle 笔记下
2011-07-06 18:17 790建立表空间:建立表空 ... -
oracle 学习笔记上
2011-06-20 19:00 10861.oracle 安装完成后,会 ... -
jquery 对select中的option操作
2011-06-15 16:28 6263Jquery的功能很强大,下面 ... -
MySQL实现分页技术
2011-06-09 08:29 906先给出servlet package com.ourchr.s ... -
servlet cookie jsp
2011-05-19 18:21 915首先是登陆界面,其中有mes_zh等国际化 这个不重要 < ... -
Java Servlet和JSP 处理Cookie
2011-05-18 13:28 19549.1 Cookie概述 Cookie是服务器发送给浏 ... -
简单的qq实现
2011-05-17 18:42 742很好用 简洁 -
简单的日历控件
2011-05-17 18:40 798package Exception; import java ... -
完整的JDBC 简单列子
2011-05-17 18:38 690package dao; import java.sql.Co ... -
jsp jstl 标签3
2011-05-13 17:38 698<c:import> 作用:导入一个url的资源, ... -
jsp jstl 标签2
2011-05-13 17:38 742c:forEach 用于循环的<c:forEach&g ... -
jsp jstl 标签1
2011-05-13 17:36 1193一、引入包: jstl.jar (1 ... -
js 中this用发小结
2011-03-09 09:19 703js中this的总结 在面向对 ... -
js 正则表达式
2011-02-24 15:12 632js正则表达式 exec 方法 用正则表达式模式在字符串中运 ... -
WEB innerHTML中div 和span
2011-02-24 13:58 1243使用innerHTML属性来控制DIV和SPAN<$lo ... -
Myeclipse 快捷键打总结
2011-01-08 13:01 727xml、jsp、jsf、js等等,我们没有必要全部都去自动校验 ... -
java基础知识
2011-01-07 17:35 708 -
java文件夹之间的复制
2011-01-06 14:58 656package Exception; import java. ...
相关推荐
tables-3.6.1-cp39-cp39-win_amd64.whl
基于springboot大学生心理咨询平台源码数据库文档.zip
基于Java web 实现的仓库管理系统源码,适用于初学者了解Java web的开发过程以及仓库管理系统的实现。
基于springboot智能推荐旅游平台源码数据库文档.zip
内容概要:本文是一份详尽的Ruby语言教程,首先介绍了Ruby语言的基本信息和发展背景。接着详细讲解了Ruby的基础语法,如变量、数据类型、运算符、控制流等,并深入探讨了面向对象编程的关键概念,包括类、对象、继承、封装和多态。随后介绍了Ruby的一些高级特性,如模块、异常处理、迭代器和文件I/O操作。最后,讨论了Ruby在Web开发中的应用,尤其是与Rails框架的结合。每个部分都配有相应的代码示例,帮助读者更好地理解和实践。 适合人群:适用于初学者和有一定基础的程序员,特别是对Ruby语言感兴趣的人。 使用场景及目标:学习和掌握Ruby语言的各项基础知识和高级特性,为进一步进行Web开发或其他相关编程打下坚实的基础。 其他说明:教程中的每一部分内容都有详细的解释和代码示例,非常适合自学和教学使用。
GEE训练教程——Landsat5、8和Sentinel-2、DEM和各2哦想指数下载
基于springboot在线问诊系统源码数据库文档.zip
基于springboot的流浪猫狗救助系统源码数据库文档.zip
GEE训练教程
该资源内项目源码是个人的课程设计、毕业设计,代码都测试ok,都是运行成功后才上传资源,答辩评审平均分达到96分,放心下载使用! ## 项目备注 1、该资源内项目代码都经过严格测试运行成功才上传的,请放心下载使用! 2、本项目适合计算机相关专业(如计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载学习,也适合小白学习进阶,当然也可作为毕设项目、课程设计、作业、项目初期立项演示等。 3、如果基础还行,也可在此代码基础上进行修改,以实现其他功能,也可用于毕设、课设、作业等。 下载后请首先打开README.md文件(如有),仅供学习参考, 切勿用于商业用途。
本教程帮助您了解什么是 SQLite,它与 SQL 之间的不同,为什么需要它,以及它的应用程序数据库处理方式。需要的朋友们可以参考看看! SQLite是一个软件库,实现了自给自足的、无服务器的、零配置的、事务性的 SQL 数据库引擎。SQLite是一个增长最快的数据库引擎,这是在普及方面的增长,与它的尺寸大小无关。SQLite 源代码不受版权限制。 什么是 SQLite? SQLite是一个进程内的库,实现了自给自足的、无服务器的、零配置的、事务性的 SQL 数据库引擎。它是一个零配置的数据库,这意味着与其他数据库一样,您不需要在系统中配置。 就像其他数据库,SQLite 引擎不是一个独立的进程,可以按应用程序需求进行静态或动态连接。SQLite 直接访问其存储文件。 为什么要用 SQLite? 不需要一个单独的服务器进程或操作的系统(无服务器的)。 SQLite 不需要配置,这意味着不需要安装或管理。 一个完整的 SQLite 数据库是存储在一个单一的跨平台的磁盘文件。 SQLite 是非常小的,是轻量级的,完全配置时小于 400KiB,省略可选功能配置时小于250K
基于springboot学生选课系统源码数据库文档.zip
【Android UI】SurfaceView中使用 Canvas 绘制可缩放大图 ( 拖动和缩放相关的变量 | Canvas 绘图函数 | 手势识别 多点触控流程 | 拖动图片 | 缩放图片 ) 博客链接:https://blog.csdn.net/shulianghan/article/details/143950948 一、需求分析 和 核心要点 说明 1、需求说明 2、核心要点 - 拖动和缩放相关的变量 3、核心要点 - Canvas 绘图函数 4、核心要点 - 手势识别 多点触控流程 5、核心要点 - 拖动图片 6、核心要点 - 缩放图片 二、完整代码示例 1、环境说明 2、SurfaceView 完整代码 3、运行结果
基于springboot+web的二手闲置交易系统源码数据库文档.zip
GEE训练教程
该资源内项目源码是个人的课程设计、毕业设计,代码都测试ok,都是运行成功后才上传资源,答辩评审平均分达到96分,放心下载使用! ## 项目备注 1、该资源内项目代码都经过严格测试运行成功才上传的,请放心下载使用! 2、本项目适合计算机相关专业(如计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载学习,也适合小白学习进阶,当然也可作为毕设项目、课程设计、作业、项目初期立项演示等。 3、如果基础还行,也可在此代码基础上进行修改,以实现其他功能,也可用于毕设、课设、作业等。 下载后请首先打开README.md文件(如有),仅供学习参考, 切勿用于商业用途。
基于SpringBoot的宠物寄领养网站源码数据库文档.zip
基于springboot的电影院售票管理系统源码数据库文档.zip
该资源内项目源码是个人的课程设计、毕业设计,代码都测试ok,都是运行成功后才上传资源,答辩评审平均分达到96分,放心下载使用! ## 项目备注 1、该资源内项目代码都经过严格测试运行成功才上传的,请放心下载使用! 2、本项目适合计算机相关专业(如计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载学习,也适合小白学习进阶,当然也可作为毕设项目、课程设计、作业、项目初期立项演示等。 3、如果基础还行,也可在此代码基础上进行修改,以实现其他功能,也可用于毕设、课设、作业等。 下载后请首先打开README.md文件(如有),仅供学习参考, 切勿用于商业用途。
数据存放网盘,txt文件内包含下载链接及提取码,永久有效。失效会第一时间进行补充。样例数据及详细介绍参见文章:https://blog.csdn.net/T0620514/article/details/143956923