Java——Thread线程基础知识学习

1.进程与线程

一个程序中至少有一个进程,一个进程中至少会有一个线程。一个进程可以包含多个线程。

看到一个蛮容易理解的比方:

cpu可以看做一个工厂,而进程可以看做是一个车间,每次这个工厂只能有一个车间工作,一旦有一个车间工作其他车间就需要停止工作。也就是说在任何时候,cpu都只运行一个进程。线程就是车间里的工人,一个车间可以有多个工人同时工作,他们可以协调共同完成一个任务。而车间内的房间,工人们是共享的,车间内的工人们都可以进出。这也就意味着一个进程的内存空间,线程是共享的。车间内的房间大小不等,有的房间只能容下一个人,其他人想使用这个房间的话,只有这个小房间内人出去后才可以。这代表一个线程使用某些共享内存时,其他线程必须等它结束,才能使用这一块内存。

以上的理解是看到这篇博客进程与线程的一个简单解释写的通俗易懂,配图特别形象。


2.Thread类

Thread 状态:

  • 创建(new): 使用new的方式创建一个Thread对象。
  • 就绪(runnable) :线程对象调用start()方法,进入就绪状态,此时的线程位于可运行池中,等待cpu调度,获取cpu的使用权
  • 运行(running):获取到cpu的使用权,占用cpu,执行run方法中的代码
  • 阻塞(blocked):运行状态的线程,遇到阻塞事件,进入阻塞状态,此时虚拟机不会再给线程分配cpu。当阻塞状态解除时,进入就绪状态。

阻塞状态又分几种种情况:1)位于对象等待池中的阻塞状态,也就是调用wait()方法;2)对象锁中的阻塞状态,在使用某个对象的同步锁时,该对象的同步锁已经被其他线程占用;3).使用了sleep()方法或者join()方法

Thread的状态

2.1 两种创建线程的方式

创建一个线程一共有三种方式,其中两种常见的是:

  1. 继承Thread类
  2. 实现Runnable接口

2.1.1继承Thread类

class MyThread_1 extends Thread {
    public MyThread_1(String name) {
         super(name);
     }
    @Override
    public void run() {
        System.out.println(System.currentTimeMillis());
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName()+"---->" + i);
        }
        System.out.println(System.currentTimeMillis());
    }
}

开启线程就是:
new MyThread_1("thread_1").start()

Thread的构造方法有8种,常见的也就4种:

  • Thread()
  • Thread(Runnable target)
  • Thread(Runnable target, String name)
  • Thread(String name)

2.1.2实现Runnable接口

class Ticket implements Runnable {
    private int num = 100;
    @Override
    public void run() {
        while (true) {
            if (num > 0) {
                    System.out.println("--->" + num--);
            }
        }
    }
}

开启线程就是:
new Thread(new Ticket()).start()

受限于Java单继承可多实现的特点,一般都常用第二种方式,往往也是直接采用匿名内部类。


2.2 Thread类的常见方法

  • setName(String name) getName()

给Thread设置自定义的名字。也可以采用构造方法传值。对应的便是拿到Thread的名字。如果不设置线程的名字,拿到的默认名字是"Thread-0,1,2..."。

  • currentThread()

获取当前线程

  • start()

开启线程。启动线程后,开始执行run()方法中的代码。

  • setPriority(int newPriority) getPriority()

为线程设置优先级。优先级从1到10。默认是5。

  • sleep(long millis)

休眠毫秒数

  • join()

线程加入或者线程合并,加入的线程执行结束才会执行其他的线程。

  • yield()

线程让步,暂停当前正在执行的线程,把执行权给其他线程,执行其他的线程

  • interrupt()

中断线程。并不是停止一个线程,给线程加一个中断标记。使线程从阻塞(冻结)状态到运行状态。

  • isInterrupted()

判断线程是否中断成功。


上面的几个方法,也都是简单学习。了解并不深入。

2.3线程全问题

上面的比方中说,线程就像车间的工人,而车间内的房间工人们都可以出入的。当多个工人同时工作时,有时需要使用同一个房间内的原材料,如果多名对原材料有操作资格的工人同时都进入这个房间的话,都同时对材料进行操作会存在安全隐患,这时就需要考虑给这个的房间设置一些限制,只有在没人使用原材料的情况下,有操作资格的工人才可以进入。一旦有一名工人在对原材料进行操作,其他工人就要等待,直到房间内对原材料进行操作的工人出来,下一名对原材料有操作资格的工人才可以进入。

也就是说多线程在一些情况下会出一些安全情况。


2.3.1同步锁

当多个线程要同时操作一个共享的数据,共享数据执行过程中还进行了变换,其中一个线程正在对共享数据进行操作变换时,没有执行结束,另外一个线程就参与进来,就会导致共享数据的操作错误。这个安全问题其实就是大名鼎鼎的生产者消费者问题。

解决的思路就是每次只允许一个线程进行共享数据的操作,加入了同步锁。

public class Thread_2 {

    public static void main(String[] args) {
        Ticket ticket = new Ticket();
        Thread t_1 = new Thread(ticket, "one");
        Thread t_2 = new Thread(ticket, "two");
        
        t_1.start();
        t_2.start();
        
    }

}

class Ticket implements Runnable {
    private int num = 100;
    
    @Override
    public void run() {
        while (true) {
            synchronized (Ticket.this) {
                if (num > 0) {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + "--->" + num--);
                }
            }
        }
    }
}

2.3.2 关于wait,notify,notiryAll

需要注意的是,wait(),notify(),notifyall()这几个方法并不是Thread类中的,而是Objecet类的。这几个方法是根据需要有时要用在同步锁中,同步锁的对象是可以为任何对象的。在同步代码块中,调用这些方法是需要同步锁对象的,如果这3个方法是在Thread类中,就不符合实际的需要,不满足能够被任意同步锁调用。

贴一个学习时,敲的代码:

public class Thread_3 {
    public static void main(String[] args) {
        Person person = new Person();

        new Thread(new Input(person)).start();
        new Thread(new Output(person)).start();

    }
}

class Input implements Runnable {
    private Person person;

    public Input(thread_demo.Person person) {
        this.person = person;
    }

    @Override
    public void run() {
        if (person != null) {
            int x = 0;
            while (true) {

                if (x == 0) {
                    person.set("Java", "22");
                } else {
                    person.set("李四", "五");
                }

                x = (x + 1) % 2;

            }
        }
    }
}

class Output implements Runnable {
    private Person person;

    public Output(Person person) {
        this.person = person;
    }

    @Override
    public void run() {
        if (person != null) {
            while (true) {
                person.out();
            }
        }
    }

}

class Person {
    private String name;
    private String age;
    private boolean flag;

    public synchronized void set(String name, String age) {
        while (flag) {
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        this.name = name;
        this.age = age;
        flag = true;
        this.notifyAll();
    }

    public synchronized void out() {
        while (!flag) {
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println(name + "--->" + age);
        flag = false;
        this.notifyAll();
    }
}

2.3.3单例模式-懒汉式 防止多线程创建多个对象

public class Singleton {
    private static Singleton singleton = null;

    private Singleton() {};

    public static Singleton getInstance() {
        if (singleton == null) {//为了减少对同步锁的判断 
            synchronized (Singleton.class) {//静态方法中没有this
                if (singleton == null) {
                    singleton = new Singleton();
                }
            }
        }
        return singleton;
    }
}

最后

一直以来就感觉多线程是个很难的东西。大三学习Java课程老师上课讲解生产者消费者时,觉得好复杂啊,多线程果然好难。虽然现在工作经验不多,但时隔一年半,再来看这个问题时,也显得不那么复杂了。不过,对于多线程依然觉得很难。目前对于线程的状态还不是很清晰

在我目前的Android开发经验中,对于多线程的使用不多,虽然Android中不允许在UI主线程来进行网络请求,但目前各个网络请求框架都有对异步线程封装,结合Handler便可以实现对UI内容的更新,显示网络请求拿到的数据。目前对Thread的了解比较浅显。

使用的场景也往往比较简单。记得比较清楚的两个场景,一个开启一个线程用来计时,一个用于实现广告页无限轮播。感觉这两个使用场景也可以用Android中的Handler来代替,而且实现过程也相对简单。接下来打算用两种方式再来实现一下在Android中计时。然后找本书,再深入点学习,就自己尝试一下实现多线程下载一个大文件。

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

推荐阅读更多精彩内容