JAVA中线程Thread

前言

JAVA线程是JAVA中重要的一个知识点,线程又可分为单线程和多线程,然而首先什么是线程?接下来我们慢慢深入了解

目录

  • 线程的介绍
  • 线程的使用
  • 线程的优先级别
  • 守护线程
  • 线程的同步

一、线程的介绍

世间万物都可以同时完成很多工作,例如:人体可以同时进行呼吸,吃东西,思考问题等活动;
用户既可以使用计算机听歌,也可以使用它打印文件,而这些活动完全可以同时进行;
这种思想放在JAVA中被称为并发,而将并发完成的每一件事情称为线程

一个线程则是进程中的执行流程,一个进程中可以同时包括多个线程,每个线程也可以得到一小段程序的执行时间,这样一个进程就可以具有多个并发执行的线程。
在单线程中,程序代码按调用顺序依次往下执行,如果需要一个进程同时完成多段代码的操作,就需要产生多线程。
简单理解一下,单线程就好像排队上厕所,有序进行,而多线程就是争抢着上厕所(大家都很急嘛O(∩_∩)O哈哈),无序进行。

具体代码我们看下面

/*
 * 单线程
 * 一个线程:main,test01,test02
 * main是主线程
 */
public class ThreadTest01 {
        public static void main(String[] args) {
            test01();
        }
        public static void test01(){
            test02();
        }
        public static void test02(){
            System.out.println("其实我是test03");
        }
}

这是一个简单的单线程,顺序往下调用,下面我们看看多线程的实现

二、线程的使用

实现多线程有以下俩种方法
继承Thread类:
Thread类是 java.lang 包中的一个类,从这个类中实例化的对象代表线程,程序员启动一个新线程需要建立 Thread 实例;
继承 Thread 类创建一个新的线程语法如下:
Public class ThreadTest extends Thread{
}
当一个类继承Thread类后,就可以在该类中覆盖 run() 方法,将实现该线程功能的代码写入 run() 方法中
Public void run(){
}
然后同时调用Thread类中的Start()方法执行线程,也就是调用run()方法
Public static void main(String args[]){
new ThreadTest().start();
}

使用 Runnable 接口:
实现 Runnable 接口的语法如下:
Public class Thread extends Object implements Runnable
使用 Runnable 接口启动新的线程步骤如下:
(1)建立 Runnable 对象;
(2)使用参数为 Runnable 对象的构造方法创建Thread实例;
(3)调用start()方法启动线程。

import java.lang.*;
/*
 * 多线程   有俩种实现方式
 * 1:继承Thread类
 * 2:实现Runnable
 */
public class ThreadTest02 {
    public static void main(String[] args) {
        /*第一种:继承Thread*/
        /*
        Thread hh = new C1();
        hh.start();
        */

        /*第二种:实现Runnable*/
        C2 tt = new C2();//实例化一个对象 但是没有直接的start()方法 因为它不是一个线程
        Thread hh = new Thread(tt);//Thread要求将runnable对象传入构成线程
        hh.start();
        for (int i = 0; i < 30; i++) {
            System.out.println("main->"+i);
        }

    }

}
/*
class C1 extends Thread{
    public void run(){
        for (int i = 0; i < 30; i++) {
            System.out.println("run->"+i);
        }
    }
}
*/
class C2 implements Runnable{

    @Override
    public void run() {
        // TODO Auto-generated method stub
        for (int i = 0; i < 30; i++) {
            System.out.println("run->"+i);
        }
    }

}

这里写图片描述

运行结果如图,main 线程 与 run 线程是同时进行的!!!另外我们也可以给线程取名字和获取名字,只需要三步☟

  • currentThread() 获取当前线程的对象
  • getName() 获得线程的名字
  • setName() 给线程取名字

三、线程的优先级别:

多线程的执行本身就是多个线程的交换执行,并非同时执行每个线程执行时都具有一定的优先级;
当调度线程时,会优先考虑级别高的线程

默认情况下,一个线程继承其父线程的优先级
使用 线程对象.setPriority(p)来改变线程的优先级

优先级影响CPU在线程间切换,切换的原则是:
当一个线程通过显式放弃、睡眠或者阻塞、自愿释放控制权时,所有线程均接受检查而优先级高线程将会优先执行

一个线程可以被一个高优先级的线程抢占资源
同级别的线程之间,则通过控制权的释放,确保所有的线程均有机会运行。

但是这里要注意的一点,优先级是一个高概率事件,并非高优先级就一定比低优先级的线程执行,就跟之前打的比方争厕所,高优先级的线程是跑的快的人,但低优先级的壮士也不是吃素的哈哈。。。。好了,回到正题看代码

/*
 * 线程有优先级 1-10 默认优先级为5
 */
public class ThreadTest04 {
    public static void main(String[] args) {

        Thread h1 = new Test();
        Thread h2 = new Test();

        System.out.println("最大优先级:"+Thread.MAX_PRIORITY);
        System.out.println("最小优先级:"+Thread.MIN_PRIORITY);
        System.out.println("默认优先级:"+Thread.NORM_PRIORITY);

        h1.setName("线程1");
        //查看线程优先级
        h1.setPriority(1);
        System.out.println("线程1的优先级:"+h1.getPriority());

        h2.setName("线程2");
        h2.setPriority(10);
        System.out.println("线程2的优先级:"+h2.getPriority());

        h1.start();
        h2.start();
        //优先级高的只是提高优先概率 并不是绝对的优先
    }

}

class Test extends Thread{
    public void run(){
        for (int i = 0; i < 30; i++) {
            System.out.println(Thread.currentThread().getName()+"->"+i);
        }
    }
}

至于运行结果,大家就自己自行查看这里就不贴图了!!

四、守护线程

在Java中有两类线程:User Thread (用户线程)、Daemon Thread (守护线程) Daemon 的作用是为其他线程的运行提供便利服务,比如垃圾回收线程就是一个很称职的守护者。
User 和 Daemon 两者几乎没有区别,唯一的不同之处就在于虚拟机的离开:如果 User Thread已经全部退出运行了,只剩下 Daemon Thread 存在了,虚拟机也就退出了。 因为没有了被守护者,Daemon 也就没有工作可做了,也就没有继续运行程序的必要了。
语法:
public final void setDaemon(boolean on)

注意:thread.setDaemon(true) 必须在 thread.start() 之前设置,否则会跑出一个 IllegalThreadStateException 异常。你不能把正在运行的常规线程设置为守护线程。

package com.tz.util;
/*
 * 守护线程:例如gc垃圾回收机制
 * 当用户线程全部退出时 守护线程也会在自动退出
 * 不能把正在运行的常规线程 设置为守护线程
 * 在守护线程中产生的新线程也是守护线程
 * 不是所有的应用都可以分配守护线程,比如读写的操作,计算的逻辑
 */
public class DaemonThread {

    public static void main(String[] args) {
        Thread c1 = new MyCommon();
        Thread c2 = new Thread(new MyDaemon());

        //设置守护线程
        c2.setDaemon(true);
        c1.start();
        c2.start();
    }
}
class MyCommon extends Thread{
    @Override
    public void run() {
        // TODO Auto-generated method stub
        for (int i = 0; i < 5; i++) {
            System.out.println("线程第"+i+"次执行");
            try {
                Thread.sleep(7);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
}

class MyDaemon implements Runnable{
    @Override
    public void run() {
        // TODO Auto-generated method stub
        for (int i = 0; i < 999999L; i++) {
            System.out.println("后台线程第"+i+"次执行");
            try {
                Thread.sleep(7);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
}

通过代码,我们设置守护线程执行999999次,而唯一的用户线程只执行5次,从运行结果看出,当用户线程执行完毕后守护线程也随它而去,双双结束了自己的生命

五、线程的同步

所谓同步,就是在发出一个功能调用时,在没有得到结果之前,该调用就不返回,同时其它线程也不能调用这个方法,也称为加锁。

在多线程编程里面,一些敏感数据不允许被多个线程同时访问,此时就使用同步访问技术,保证数据在任何时刻,最多有一个线程访问,以保证数据的完整性。

打个比方,车站买车票有很多个售票窗口,当有俩个窗口对同一张票操作时如果没有同步数据,则会出现一张票卖给了俩个人的情况,这样在车上可就尴尬了(\O_

package com.tz.util;
/*
 * 线程的同步
 * 同步:比如火车站买票 俩个客户在同一个窗口不能买到同一张票
 */
public class SyncharonizedThreadTest {
        static class Bank{
            public int account = 100;
            public int getAcount(){
                return account;
            }
            /*
             * 用同步方法实现同步 整个方法所有的代码都被同步
             * 同步方法是对this对象加锁
             */
            public synchronized int save(int money){

                account += money;
                return account;
            }
            /*
             * 也可以用同步代码块
             * 优点在于可以缩小同步范围和随意加锁
             */
            public int save1(int money){
                synchronized (this) {
                    account += money;
                }
                return account;
            }
            //去掉synchronized同步结果数据不一定正确
            public int save2(int money){
                account += money;
                return account;
            }

        }

        class MyThread implements Runnable{
            public Bank bank;
            public MyThread(Bank bank){
                this.bank = bank;
            }
            @Override
            public void run() {
                // TODO Auto-generated method stub
                for (int i = 0; i < 5; i++) {
                    System.out.println("用户余额为"+bank.save2(10));
                }
            }
        }
        /*
         * 开启了俩个线程,分别对账户进行存钱
         * 由于开启 了线程的同步,最后的结果肯定是正确的
         */
        public void useThread(){
            Bank bank = new Bank();
            MyThread thread = new MyThread(bank);
            System.out.println("线程1");
            Thread t1 = new Thread(thread);
            t1.start();

            System.out.println("线程2");
            Thread t2 = new Thread(thread);
            t2.start();

        }

        public static void main(String[] args) {
            SyncharonizedThreadTest st = new SyncharonizedThreadTest();
            st.useThread();
        }
}

save是使用关键字 synchronized 加锁方法,save1 是使用代码块,好处是可以控制同步范围(例如给有需要的数据加锁)和随意加锁,save2则是不加锁的情况,所以运行结果可能会出现差错,如图

这里写图片描述

正确答案应该是存钱200,而答案是190就是没有加锁的原因!!

关于线程的讲解大概就这些了,转载请注明出处☞ http://blog.csdn.net/W_ILU/article/details/50970031

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值