黑马程序员——简单创建多线程
------- android培训、java培训、期待与您交流! ----------
进程是正在运行的应用程序作为一个进程,进程中一个执行路径是一个线程<负责代码执行>。
说到这里很多人都会有一个疑问,线程是负责代码的执行,有时我们没创建线程,为什么代码一样可以执行呢?
因为一个java的应用程序,至少有俩个线程,都是jvm创建的,一个是主线程,负责main的代码执行,同时还会创建一个垃圾回收器线程, 负责垃圾回收。
那么多线程的优点:
1、可以在进程中同时执行多个任务代码。
2、提高了资源的利用率。而不是效率...
多线程的弊端:
1、增加了cpu的负担。
2、降低了一个进程线程的执行概率,
2、会出现线程安全问题。
3、引发了死锁问题。
说了这么多了,那怎么自定义线程呢?
sun公司给我们提供了一个线程基类Thread
步骤1:自定义一个类继承Thread
2:重写Thread里面run()方法,把自定义线程的代码放入run()方法中。
3:创建线程对象,调用start方法开启线程任务,线程一旦开启就会自动调用run()方法执行里面的代码。
注意:run方法不能直接的调用,如果直接调用了run方法,那么没有开启一个新的线程 ,只是相当于调用另一个普通方法而已。一定要调用start方法开启一个线程,线程启动时就回去执行run方法的代码了。
说了这么多了,那我们就来自定义一个多线程吧。
class MyThread extends Thread{//声明一个类继承线程Thread类
//重写run()把自定义线程执行的代码放入其中
public void run(){
for(int i=0;i<1000;i++){
System.out.println("自定义线程执行了..."+i);
}
}
}
class Demo1{
public static void main(String[] args){
//创建线程对象
MyThread my = new MyThread();
//调用start方法开启线程。
my.start();
}
}
当我们再来使用多线程模拟车站售票时,却发现事没那么简单。
package cn.chen.thread;
//模拟车站卖票。。
class SaleTicket extends Thread{
//必须用静态修饰,因为保证3个窗口使用同一份数据。非静态成员变量会在每创建一个对象维护一份。
static int tickets=50;
- //使用锁对象的时候,多个线程必须是使用同一个锁对象。
static Object o = new Object();
public SaleTicket(String name){
super(name);
}
public void run(){
while(true){
//锁对象是任意的对象,但必须保证同一份...
synchronized (o){
if(tickets>0){
System.out.println(Thread.currentThread().getName()+"卖出第"+tickets+"张票");
tickets--;
}else{
System.out.println("票已经售馨...");
break;
}
}
}
}
}
public class Demo3 {
public static void main(String[] args) {
//创建线程对象
SaleTicket st1 = new SaleTicket("一号窗口");
SaleTicket st2 = new SaleTicket("二号窗口");
SaleTicket st3 = new SaleTicket("三号窗口");
//开启线程
st1.start();
st2.start();
st3.start();
}
}
楼上这个例子必须注意:使用同步代码块是为了解决线程安全问题,防止出现卖票错乱,在同步代码块中的锁对象,多个线程必须是使用同一个锁对象。没有线程安全,不建议使用同步机制,因为会降低操作效率。
同步机制的方式有两种:
同步代码块:
格式:
synchronized(锁对象){
需要被同步的代码...
}
注意:任何的对象都可以作为一个锁对象。
同步函数:
格式:
修饰符 synchronized 返回类型 函数名(){
}
定义实例如下:
public static synchronized void getMoney(){ //关闭
while(true){
//synchronized (o) {
if(count>0){
System.out.println(Thread.currentThread().getName()+"取走了一千块,账户的总额还剩余:"+ (count-1000));
count-=1000;
}else{
System.out.println("账户的余额不足...");
break;
}
//}
}
同步函数需要注意的细节:
1、如果是非静态函数锁对象是this对象,而且同步函数锁对象是不能更改的。
2、如果是静态函数中锁对象是当前函数所属
类文件对象。
大家都知道使用锁同步时,每个线程使用的锁必须是同一个,所以切记以上2点细节。
推荐使用同步代码块。因为锁可以任意,可以指定同步代码,很灵活。
自定义线程还有一种方法就是实现Runnable接口。重写run方法,
具体步骤如下:
1、自定义类实现Runnable接口,实现接口中run方法。把自定义线程任务写在run方法中。
2、创建Runnable实现类对象。
3、创建线程Thread对象,把Runnable实现类对象作为参数传递给线程Thread。
4、调用Thread中start方法开启线程。
同样是卖票,用实现Runnable接口又如何呢? 且看代码:
public class Demo6 {
public static void main(String[] args) {
//创建实现类对象。
TickThread t = new TickThread();
//创建线程3个。把实现类对象传递到线程
Thread thread1 = new Thread(t,"一号窗口");
Thread thread2 = new Thread(t,"二号窗口");
Thread thread3 = new Thread(t,"三号窗口");
//调用start方法开启线程。
thread1.start();
thread2.start();
thread3.start();
}
}
class TickThread implements Runnable{
//这里就不需要static修饰了,因为只创建一个此类实例对象。
int ticks=50;
public void run(){
synchronized ("哈哈"){
while(true){
if(ticks>0){
System.out.println(Thread.currentThread().getName()+"售出"+ticks+"张票");
ticks--;
}else{
System.out.println("票已经售馨...");
break;
}
}
}
}
}
推荐使用:实现Runnable接口的方式。 因为java是单继承语言,如果自定义还需要有其他操作,继承了Thread很有局限。
注意:
Runnable实现类的对象其实并不是一个线程对象,只是实现了Runnable接口类的对象而已。
只有是Thread或者Thread的子类才是一个线程对象。
在调用Thread对象的start方法开启一个线程的时候,那么Thread对象就会去执行自己类中的run方法,其实这内部调用了Runnable的实现类的run方法,换菊花说实际上
是把Runnable实现类的run方法作为了Thread对象的任务代码去执行了。