Java 中的线程创建的方式(一)

导语:

争取每周至少写一篇,要么是技术分享,要么写点其他的东西。但写作我会坚持下去,真心希望看到这篇文章的童鞋也可以坚持写作,你会发现写作真的很有锻炼价值,当然这篇文章主要是写一写 Java 中的线程的一些知识。

程序、进程和线程

在说线程之前,我觉得有必要来说一下程序、进程和线程的概念。

  • 程序:我们现在编写的都是程序。程序可以是java,c语言,c++,c#,php 等。
  • 进程:程序运行起来会产生一个进程。
    比如:QQ等。当 QQ 静止不运行的时候,只是程序,这个程序提供我们快捷方式,可以双击运行这个程序,然后就会产生一个进程。我们可以通过任务管理器查看进程:
任务管理器—进程
  • 那什么是线程呢?比如在 QQ 中,你正在分别和 A、B 聊天,那么你和 A 聊天是一个线程,和 B 聊天是另外一个线程。

程序运行起来,产生一个进程,进程会被加载到内存中运行。那么进程和线程之间是什么关系呢?

我们都知道计算机的核心是CPU,它承担了所有的计算任务。它就像一座工厂,时刻在运行。假定工厂的电力有限,一次只能供给一个车间使用。也就是说,一个车间开工的时候,其他车间都必须停工。背后的含义就是,单个CPU一次只能运行一个任务。进程就好比工厂的车间,它代表CPU所能处理的单个任务。任一时刻,CPU总是运行一个进程,其他进程处于非运行状态。一个车间里,可以有很多工人。他们协同完成一个任务。线程就好比车间里的工人,一个进程可以包括多个线程。车间的空间是工人们共享的,比如许多房间是每个工人都可以进出的。这象征一个进程的内存空间是共享的,每个线程都可以使用这些共享内存。

那么既然说单个 CPU(这里只以单个 CPU 举例)任意时刻只能进行一项任务,那么为什么我们平常用电脑听歌的同时还能和别人在网上聊天然后还能浏览网页呢?

因为 CPU 会给每个进程分配时间片(CPU 将时间片分配给进程,进程又分配给下面的线程),即该进程允许运行的时间,使各个程序从表面上看是同时进行的。如果在时间片结束时进程还在运行,则CPU将被剥夺并分配给另一个进程。

举个栗子:假如一台计算机只有A,B 两个程序在运行,那么这两个程序都会被分配时间片,然后 CPU 会先执行 A 的时间片(其实先执行哪个无所谓),当 A 的时间片被执行完之后 CPU 就会去执行 B 的时间片,当 B 的时间片被执行完之后 CPU 又去执行 A 的时间片,就这样依次交替运行,但是呢,CPU 的切换速度特别快,所以在宏观上:我们可以同时打开多个应用程序,每个程序并行不悖,同时运行。但在微观上:由于只有一个CPU,一次只能处理程序要求的一部分,如何处理公平,一种方法就是引入时间片,每个程序轮流执行。

1.线程的创建,启动方式一

线程创建的第一种方式是继承 Thread 类,代码如下:

1 public class TestThread001 extends Thread{//只是创建了一个普通类。继承了Thread类之后,代表你这个类具备了多线程的能力。

2    //我们要重写Thread类中的run方法,代表假如你有线程启动了,我就会执行run方法中的内容----线程体
3   @Override
4   public void run() {
5       for (int i = 1; i <=10; i++) {
7           System.out.print(i+" ");
8       }
9   }
10 }

这段代码仅仅代表创建了一个普通类 TestThread001,只是这个类继承了 Thread 类,然后具备了多线程的能力。需要注意的是,继承了 Thread 类之后需要重写Thread类中的run方法,代表假如你有线程启动了,就会执行run方法中的内容(又叫线程体)。

(接上段代码)

1 public class Test {
2   public static void main(String[] args) {
3       TestThread001 tt=new TestThread001();//创建了一个线程对象tt。
4       tt.start();//启动线程
5       for (int i = 1; i <=10; i++) {
6           System.out.print("main"+i+" ");
7       }   
8   }
9 }

第三行代码表示仅表示创建了一个线程对象 tt,这个对象需要等待被启动,第四行表示启动这个线程,一定要记住了,若要启动创建的线程,一定要用 start() 方法!!如果第四行写成 tt.run(),那这就成了创建一个普通的对象,然后调用它的 run() 方法。那么有一个问题出现了,这段代码中有几个线程呢?其实是两个线程,一个是主方法线程,另外一个就是新创建的线程。

那么这段代码的运行为:

运行结果

图中红色区域表示 tt 线程的运行结果。从运行结果可以看出来,tt 线程和主方法线程是同时运行的,这里的同时运行是指宏观上的同时运行,微观上,CPU 对于两个线程是交替执行的(前面提到的时间片的概念)。

我们可以画一下这段程序的大概流程图如下所示:

程序流程(大概)

首先主线程开始启动,运行主方法里的代码,运行的过程中发现新创建的 tt 线程启动了,于是,tt 线程和主方法线程并驾齐驱的运行,也就是上面提到的宏观上的同时运行。

2 给线程起名字

2.1 通过setName方法设置线程的名字

代码如下(只写出取名的代码):

 TestThread001 tt=new TestThread001();//创建了一个线程对象tt。
 tt.setName("--线程1--");

tt 线程就被命名为 "--线程1--"。

2.2 通过构造器

第二种给对象起名字的方法就是通过构造器了。

1 public class TestThread001 extends Thread{
2    public TestThread001(String name) {
3       super(name);
4   }
5   //重写Thread类中的run方法,线程体
6   @Override
7   public void run() {
8       for (int i = 1; i <=10; i++) {
9           System.out.println(Thread.currentThread().getName()+i);
10      }
11  }   
12 }
13 public class Test {
14  public static void main(String[] args) {
15      for (int i = 1; i <=10; i++) {
16          System.out.println(i);
17      }
18      TestThread001 tt=new TestThread001("线程1-----");//创建了一个线程对象tt。
19      tt.start();//我们启动线程 ,用的方法是start()方法。只能通过start启动,你要是只调用run的话,并没有启动成功。
20      for (int i = 1; i <=10; i++) {
21          System.out.println(Thread.currentThread().getName()+i);
22      }   
23  }
24 }

代码第 18 行表示通过构造器的方法来给线程对象命名。那么我们可以通过 Thread.currentThread().getName() 方法来获取当前线程对象的名字。

至于第 3 行为什么会调用 super(name),感兴趣的同学可以看一下 Thread 的源码,在这里就不解释啦,不重要。只需要知道怎么给对象命名就行。

3 龟兔赛跑

下面带大家写一个龟兔赛跑的故事来感受一下线程:
下面其实是三段代码,为了节省空间,放在一块,希望大家不要误解啊

//第一段代码:
1 public class WuGui  extends Thread {
2    public WuGui(String name) {
3       super(name);
4   }
5   @Override
6   public void run() {
7       while(true){
8           System.out.println("我是乌龟。。我会跑。。。");
9       }
10  }   
11 }
//第二段代码:
12 public class Tuzi extends Thread{
13    public Tuzi(String name) {
14      super(name);
15  }
16  @Override
17  public void run() {
18      while(true){
19          
20          System.out.println("我是兔子 我不睡觉了。。。");
21      }
22  }   
23 }
//第三段代码:
24 public class Test {
25  public static void main(String[] args) {
26      WuGui wg=new WuGui("乌龟");//创建线程对象
27      wg.start();//启动
28      Tuzi tz=new Tuzi("兔子");
29      tz.start();
30  }
31 }

这段代码运行结果如下 :

龟兔赛跑

wg 线程和 tz 线程是在微观上是交替运行的,即 "我是乌龟。。我会跑。。。" 和 "我是兔子 我不睡觉了。。。"这两句话是交替出现的(又用到了上面提到的时间片的概念)。

4 火车票

下面以我们平常买票为例,再次感受下线程的 “魅力”... ...

//第一段代码
1 public class HuoChePiao extends Thread{
2   static int ticketNum=10;
3   public HuoChePiao(String name) {
4       super(name);
5   }
    //每个窗口相当于一个线程,排队排了100人
6   @Override
7   public void run() {
8       for (int i = 1; i <=100; i++) {
9           if(ticketNum>0){                
10              System.out.println("我在"+Thread.currentThread().getName()+",买了第"+(ticketNum--)+"张车票");
11          }
12      }
13  }
14 }
//第二段代码
15 public class Test {
16  public static void main(String[] args) {
17      HuoChePiao hcp001=new HuoChePiao("窗口1");//创建线程hcp001 
18      hcp001.start();//启动线程
19      HuoChePiao hcp002=new HuoChePiao("窗口2");//创建线程hcp002
20      hcp002.start();//启动线程
21      HuoChePiao hcp003=new HuoChePiao("窗口3");//创建线程hcp003
22      hcp003.start();//启动线程       
23  }
24 }

运行结果如下:


火车票运行结果1

这样的运行结果应该是正确的,先不解释。

那么,把代码第 2 行的 static 去掉呢,运行结果又如何?

火车票运行结果2

惊不惊喜,刺不刺激,意不意外,按照生活常识每张票只能被买一次,而现在运行结果表明同一张票被卖了三次,这显然是不符合逻辑的。

下面来分析一下:

火车票线程原理简图

由上图可以看出,在主线程之后,会创建三个火车票线程对象并依次启动,然后这四个线程并行运行(当然只是宏观上看是一起运行)。

特别需要注意的是:** ticketNum 变量为 static!就是说,这个变量属于类类型的变量,它被这个类所创建的所有对象共享,共享的意思就是说,这个类创建的所有对象中只要有一个对象修改了这个变量,那么,其他对象所拥有的就是修改过的变量。**

拿这个代码举栗子:起初 ticketNum=10,当 hcpoo1 线程对象使用了 ticketNum 变量之后 ticketNum=9,那么,当 hcpoo2 线程对象再使用 ticketNum 时,这时的 ticketNum就等于 9,这就是 static 类型的变量被这个类所创建的所有对象共享,一旦这个变量被修改,其他对象所拥有的就是修改过的变量。我想我应该表达清楚了吧?


关于线程的第一种创建方式今天先写到这里,未完待续,敬请期待。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,684评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,143评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,214评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,788评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,796评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,665评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,027评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,679评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,346评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,664评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,766评论 1 331
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,412评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,015评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,974评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,073评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,501评论 2 343

推荐阅读更多精彩内容