/*
java虚拟机允许程序并发地运行多个线程。
JVM启动至少有两个线程,一个是主线程(代码在mian方法中);另一个是负责垃圾回收机制的线程。
创建线程
方法一
在java.lang包中有一个Thread类
继承Thread类
步骤:
1,定义类继承Thread.
2,复写Threa类中的run方法;
目的:将自定义代码存储在run方法中,让线程运行。
3,调用start(),该方法有两个作用,就是启动线程,调用run方法。
*/
// 例:三个窗口分别买票,每一个窗口卖10张票
class ThreadDemo extends Thread //定义类继承Thread.
{
public void run() //复习run方法
{
int x=1;
while(x<=10)
{
System.out.println(Thread.currentThread().getName()+"....."+x);//打印正在运行的线程
x++;
}
}
}
class Test
{
public static void main(String[] String)
{
new ThreadDemo().start();//创建第一个线程并运行
new ThreadDemo().start();//创建第二个线程并运行
new ThreadDemo().start();//创建第三个线程并运行
}
}
//要点:run方法用于存放线程要运行的代码。
/*
方法二:通过实现Runable接口
步骤:
1,定义类实现(implements)Runnable接口;
2,覆盖Runnable接口的run()方法
3,通过Thread类建立线程对象。
4,将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数。
5,调用Thread类的start方法开启线程并调用Runnable的run方法。
实现方式与继承方式的区别
继承Thread:线程代码存放在Thread子类的run方法中。
实现Runnable:线程代码存放在Runnable接口子类的run方法中。
*/
//例如:购票小程序
//有100张票三个窗口卖。三个窗口共享100张票。
class ThreadDemo2 implements Runnable//定义类实现(implements)Runnable接口;
{
private int tick=10;
public void run()//覆盖Runnable接口的run()方法
{
while(true)
{
if(tick>0)
{
//为了问题看起来明显一点在此睡上10毫秒,10好秒后自动回到可执行状态
try{Thread.sleep(100);} catch(Exception e){}
System.out.println(Thread.currentThread().getName()+"......."+tick--);
}
}
}
}
class Test2
{
public static void main(String[] args)
{
ThreadDemo2 th=new ThreadDemo2();//创建ThreadDemo2对象,
new Thread(th).start();//将th作为参数传递给Thread创建线程对象
new Thread(th).start();//同上
new Thread(th).start();//同上
}
}
//以上程序是不安全的,存在线程安全性问题。此多线程操做的是共享数据,因为一个线程对多条语句只执行了一部分,还没完,这时另一个线程进来执行
//导致共享数据错误。
/*
线程的周期
一个线程有4种状态,任何一个线程肯定处于这4中状态中的一种。
1,初始状态(New):当线程对象被建立之后,并在调用start()方法之前。此时线程处于初始状态。
这时线程对象已经产生,但是没被启动。此时可调用start()启动线程和stop()方法停止线程。
2,可执行状态(Runnable):当线程对象调用start方法后,线程就转入可执行状态。当有多个线程处于可执行状态时,
这些线程都一个线程池中,排程器会按照先后顺序排列它们,当有CPU资源时,排程器会依次启动这些线程。
所以,处于可执行状态的线程可能在它正在执行,也可能在线程池中等待。
3,阻塞状态(NonRunnable):如线程对象调用wait()方法后,线程就处于阻塞状态。
此时排程器会忽略它,不对它进行排程。由阻塞状态可以通过resume()方法恢复多可执行状态,它有可能重新执行。
4,退出状态(Done):当一个线程正常结束或调用stop()方法。这个线程就退出了。
控制线程周期的方法
strat() 开启一个线程,由New状态Runnable状态
stop() 结束一个线程,由初始状态或可执行状态转入Done状态。
sleep(long) 线程暂停一段时间。由Runnable状态转入NonRunnable状态,时间到自动返回可执行状态。
wait() 停止正在运行的线程,由Runnable状态转入NonRunnable状态。需要notify()唤醒。
notify();唤醒由wait()停止的线程。
notifyAll() 唤醒所有等待的线程。
suspend() 挂起执行,由Runnable状态转入NonRunnable状态。
resume() 恢复执行,由NonRunnable状态转入Runnable状态。
yield() 明确放弃执行。线程由Runnable状态转入Runnable状态。
多线程的安全性问题
当多线程在操作共享数据是,就会出现安全性问题。因为一个线程对多条语句只执行了一部分,还没完,这时另一个线程进来执行
导致共享数据错误。
解决办法就是:同步代码块。
同步代码块原理:当多线程操作共享数据时,一个线程执行完,才让另一个线程进来执行。
格式:
synchronized(对象) 此对象可以是任意的
{
需要被执行的代码(被多线程操作的共享数据)
}
同步代码块中对象如同锁,就是当一个线程执行到操作同步代码时,就把它锁住,不让其他的线程进入执行。
当此线程执行操作完时,就释放锁。这时其他的线程才可以继续执行同步代码。
同步的前提:
1,必须有两个以上的线程
3,多线程必须使用同一个锁
同步的好处:解决了多线程的安全性问题
同步的弊端:较为消耗资源
同步函数
同步函数只需要在方法前加synchronized修饰即可
同步非静态方法的锁是 this
而同步静态方法的锁是 类名.Class() 因为静态进入内有存时,还没本类对象,只有该类对应的字节码文件对象。
*/
//解决上述程序的线程安全性问题。
//代码如下:
class ThreadDemo3 implements Runnable
{
private int tick=10;
//复写Runnable接口的run方法
public void run()
{
while(true)
{
//让线程暂停一会儿后自动回去可执行状态
try{Thread.sleep(100);}catch(Exception e){}
synchronized(new Object())
{
if(tick>0)
{
System.out.println(Thread.currentThread().getName()+"...."+tick--);
}
}
}
}
}
class Test3
{
public static void main(String[] args)
{
ThreadDemo3 th=new ThreadDemo3();
new Thread(th).start();
new Thread(th).start();
new Thread(th).start();
}
}
|