程序 进程 线程

1.程序,进程,线程

程序:为完成某种特定的功能,使用计算机语言编写的一系列指令的集合,即静态代码

进程:运行中的程序是操作系统分配资源的最小单位

线程:线程是进程内部最小的执行单元(具体要做的一件事情)是操作系统进任务调度的最小单元


早期cpu执行是以进程为单位

后来cpu执行改为以更小的线程为单位.

一个程序内部有多个线程(任务),只需要切换线程即可

2.线程和进程的关系

一个进程中可以包含多个线程的

线程必须属于某一个进程,不能独立存在

一个进程中必须有一个主线程,在主线程中可以创建其他子线程

一个进程中的所有线程,都共享该进程的资源

创建线程:

1.继承Thread类

注意:此处测试可以看到运行结果MyThread和main交叉执行

public class MyThread extends Thread{
    @Override
    public void run() {
        for (int i = 0; i < 1000; i++) {
            System.out.println("MyThread:"+i);
        }
    }
}






public class Test {
    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        //myThread.run();  仍是一个进程,没有创建新进程,仍按从上到下的顺序依次执行
        myThread.start();//创建新进程

        for (int i = 0; i < 1000; i++) {
            System.out.println("main:"+i);
        }
    }
}

2.实现Runnable接口

public class RunTask implements Runnable{
    @Override
    public void run() {
   //此处需要重写的是需要执行的任务
        for (int i = 0; i < 1000; i++) {
            System.out.println("RunTask:"+i);
        }
    }
}




public class Test {
    public static void main(String[] args) {
        RunTask runTask = new RunTask();//创建任务对象
        Thread thread = new Thread(runTask);//创建新线程,再将任务对象交给线程对象
        thread.start();

        for (int i = 0; i <1000 ; i++) {
            System.out.println("main:"+i);
        }
    }
}
public class RunTask implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getId());
        System.out.println(Thread.currentThread().getName());
    }
}




public class Test {
    public static void main(String[] args) {

        RunTask runTask = new RunTask();
        Thread thread = new Thread(runTask,"李亚鹏");//为线程执行的任务命名
        thread.start();

        System.out.println(Thread.currentThread().getId());//对现在正在执行的线程进行操作
        System.out.println(Thread.currentThread().getName());//获取信息(id 名字)
    }
}

区别:

继承Thread类之后,java中只能进行单继承,所以不能再继承其他类,比较局限

在实现Runnable接口之后,还可以继承其他类

Thread类中方法:

run();用来写线程需要执行的任务

start();用来执行线程

Thread(runTask);用来给线程赋予执行的任务

Thread(runTask,“mythread”);用来给线程赋予执行的任务,同时给线程命名

currentThread();现在正在执行的线程

getId();获取现在正在执行线程的id

getName();获取现在正在执行线程的名字

程序优先级:

计算机只有一个CPU,各个线程轮流获得CPU的使用权,才能执行任务;
优先级较高的线程有更多获得CPU的机会,反之亦然;
优先级用整数表示,取值范围是1~10,一般情况下,线程的默认优先级都是5,
但是也可以通过setPriority和getPriority方法来设置或返回优先级;
Thread类有如下3个静态常量来表示优先级
MAX_PRIORITY:取值为10,表示最高优先级。
MIN_PRIORITY:取值为1,表示最底优先级。
NORM_PRIORITY:取值为5,表示默认的优先级。
public class RunTask implements Runnable{
    @Override
    public void run() {
        
        System.out.println("thread");
    }
}



public class Test {
    public static void main(String[] args) {

        RunTask runTask = new RunTask();
        Thread thread = new Thread(runTask,"李亚鹏");//为线程执行的任务命名
        thread.start();

        //由于操作系统是综合性调度,所以高优先级也不一定是先执行 只是先获取cpu处理的可能性变大

        thread.setPriority(Thread.MAX_PRIORITY);//设置优先级,最大为10,最小为1,默认是5

        System.out.println(thread.getPriority());

        System.out.println(Thread.currentThread().getPriority());//main方法的线程优先级
    }
}

线程状态:

新建 :当一个Thread类或其子类的对象被声明并创建时,新生的线程对
象处于新建状态
就绪: 处于新建状态的线程被start()后,将进入线程队列等待CPU时
间片,此时它已具备了运行的条件,只是没分配到CPU资源
运行: 当就绪的线程被调度并获得CPU资源时,便进入运行状态,run
()方法定义了线程的操作和功能
阻塞: 在某种特殊情况下,被人为挂起或执行输入输出操作时,让出
CPU并临时中止自己的执行,进入阻塞状态
死亡: 线程完成了它的全部工作或线程被提前强制性地中止或出现异常
导致结束

守护线程:

Java 中的线程分为两类:用户线程和守护线程
任何一个守护线程都是整个 JVM 中所有非守护线程的保姆 , 只要当前 JVM 实例中尚存在
任何一个非守护线程没有结束,守护线程就全部工作;
只有当最后一个非守护线程结束时,守护线程随着 JVM 一同结束工作。
守护线程的作用是为其他线程的运行提供便利服务,守护线程最典型的应用就是 GC
( 垃圾回收器 ) ,它就是一个很称职的守护者。
用户线程和守护线程两者几乎没有区别,唯一的不同之处就在于虚拟机的离开:如果
用户线程已经全部退出运行了,只剩下守护线程存在了,虚拟机也就退出了。 因为没
有了被守护者,守护线程也就没有工作可做了,也就没有继续运行程序的必要了 .
设置守护线程 : setDaemon(boolean on)
注意:设置线程为守护线程必须在启动线程之前,否则会跑出一个
IllegalThreadStateException异常。
public class DaemonThread extends  Thread{
    @Override
    public void run() {
        int a = 0;
        while (true){

            System.out.println("thread:"+ a++);

        }
    }
}




public class Test {
    public static void main(String[] args) {

        DaemonThread daemonThread = new DaemonThread();

        daemonThread.setDaemon(true);//守护线程

        daemonThread.start();


        for (int i = 0; i < 100; i++) {

            System.out.println("main:"+i);
        }
    }
}

多线程:

概念:
  多线程是指程序中包含多个执行单元。
即在一个程序中可以同时运行多个不同的线程来执行不同的任务,也就是说允许单个程序创建多个并行执行的线程来完成各自的任务。
什么情况需要多线程:
        1.当程序需要执行两个或两个以上的任务。
        2.程序需要实现一些需要等待的任务时,如用户输入、文件读写操作、网络操作、搜索等。
多线程的优点:
        1. 提高程序的响应.
        2.提高CPU的利用率.
        3.改善程序结构,将复杂任务分为多个线程,独立运行
多线程的缺点:
        1.线程也是程序,所以线程需要占用内存,线程越多占用内存也越多;
        2.多线程需要协调和管理,所以需要CPU时间跟踪线程;
        3.线程之间对共享资源的访问会相互影响;
        

线程同步:

并发与并行
并行:多个CPU同时执行多个任务。比如:多个人同时做不同的事。
并发:在一个时间段内一次执行操作.例如卖票,抢购,秒杀看似同时进行,
              实际是一个一个执行.
多个线程同时读写同一份共享资源时,可能会引起冲突。所以引入线程“同步”机制,
即各线程间要有先来后到;
同步就是排队+锁:
几个线程之间要排队,一个个对共享资源进行操作,而不是同时进行操作;
为了保证数据在方法中被访问时的正确性,在访问时加入锁机制
模拟买票代码:
public class TicketThread extends Thread{
    /*
    synchronized关键字:
    修饰代码块:
    需要在synchronized(同步对象)
    要求:  多个线程对应的是同一个对象   用对象头中的一块区域记录线程有没有进入同步代码块中
    修饰方法:
    当同步锁修饰方法时,方法静态,那此时默认锁对象的是该类的Class对象(包含该类中的所有信息)
    当不是静态方法时,同步锁默认锁对象是this
     */
    static int num = 10;
    //static Object obj = new Object();

    public static synchronized void PrintTicket(){
        //当同步锁修饰方法时,方法静态,那此时默认锁对象的是该类的Class对象(包含该类中的所有信息)
        //当不是静态方法时,同步锁默认锁对象是this
       if (num>0){
           System.out.println(Thread.currentThread().getName()+":"+num);
           num--;
       }
    }
    @Override
    public void run() {
/*        while (true){
            synchronized (obj){
                if (num>0){
                    System.out.println(Thread.currentThread().getName()+":"+num);
                    num--;
                }else{
                    break;
                }
            }
        }*/


        while (true){
            if (num==0){
                break;
            }
            PrintTicket();
        }
    }
}




public class Test {
    public static void main(String[] args) {
        TicketThread t1 = new TicketThread();
        t1.setName("窗口1");
        t1.start();


        TicketThread t2 = new TicketThread();
        t2.setName("窗口2");
        t2.start();

    }
}

上锁除了synchronized之外还有Lock

锁提供了对共享资源的独占访问,每次只能有一个线程对Lock对象加锁,线程开始访问共享资源之前应先获得Lock对象。
ReentrantLock类实现了Lock,它拥有与synchronized相同的并发性和内存语义,在实现线程安全的控制中,比较常用的是ReentrantLock,可以显式加锁、释放锁【当然,synchronized是依靠底层代码实现的,所以是隐式的上锁解锁】
import java.util.concurrent.locks.ReentrantLock;

public class TicketThread extends Thread{

    static  int num = 10;

    static ReentrantLock lock = new ReentrantLock();

    //lock主动上锁 主动解锁
    //synchronized 靠底层代码默认上锁解锁

    @Override
    public void run() {
        while (true){
            lock.lock();//上锁
            if (num==0){
                break;
            }
            try {
                Thread.sleep(500);
                System.out.println(Thread.currentThread().getName()+":"+num);
                num--;
            } catch (InterruptedException e) {
                e.printStackTrace();
            }finally {
                lock.unlock();//解锁
            }

        }

    }
}



public class Test {
    public static void main(String[] args) {
        TicketThread ticketThread1 = new TicketThread();
        ticketThread1.setName("窗口1");
        TicketThread ticketThread2 = new TicketThread();
        ticketThread2.setName("窗口2");
        ticketThread1.start();
        ticketThread2.start();
    }
}

线程死锁:

死锁,做个比喻就是一个中国人和一个外国人吃饭

中国人需要的是两只筷子,外国人需要的是一把刀和一个叉子

恰好现在中国人拿了一只筷子一把刀,外国人拿了一个叉子一只筷子

俩人大眼瞪小眼,等着对方把自己需要的餐具还给自己。

那这顿饭就吃不了了,你不给我我也不给你,一顿僵持。

专业的话来说就是:

不同的线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源,就形成了线程的死锁。
出现死锁后,不会出现异常,不会出现提示,只是所有的线程都处于阻塞状态,无法继续。

注意:小编目前的代码水平在日常生活中还暂时没有多少概率遇到死锁问题,所以为了方便大家了解,此处我们人为编写一个死锁程序。

public class DieLockDemo extends Thread{
        boolean flag;
        static Object objA = new Object();
        static Object objB = new Object();
        public DieLockDemo(boolean flag){
            this.flag = flag;
        }



    @Override
    public void run() {
            if (flag){
                    synchronized (objA){
                        System.out.println("if objA");
                        synchronized (objB) {
                            System.out.println("if objB");
                        }
                    }
            }else {
                synchronized (objB){
                    System.out.println("else objB");
                    synchronized (objA){
                        System.out.println("else objA");
                    }
                }
            }
        }
    }




下方是test程序:


public class Test {
    public static void main(String[] args) {
        DieLockDemo dieLockDemo1 = new DieLockDemo(true);
        Thread t1 = new Thread(dieLockDemo1);
        t1.start();
        DieLockDemo dieLockDemo2 = new DieLockDemo(false);
        Thread t2 = new Thread(dieLockDemo2);
        t2.start();
    }


}

注意:如果第一次没有出现死锁情况,可以多试几遍哦。这是操作系统的锅,我可不背。

线程通信:

两个线程交替打印1-100之间的数字
public class WaitDemo extends Thread{
    static int num = 1;
    static Object object = new Object();
    /*
    wait()是Object类中定义的方法,必须用同步对象调用,让线程等待,必须再度唤醒才能继续执行
    notify()Object类中定义的方法,必须用同步对象调用,唤醒被wait的线程
    notifyAll()是Object类中定义的方法,必须用同步对象调用,用来唤醒所有被wait的线程
    三个方法都必须在同步代码块中执行
     */
    @Override
    public void run() {
        while (true){
            if (num<=100){
                synchronized (object){
                    object.notify();//唤醒等待的线程
                    System.out.println(Thread.currentThread().getName()+":"+num);
                    num++;
                    try {
                        object.wait();//让进来的线程等待,同时释放锁,可以让其他线程进来
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
}



public class Test {
    public static void main(String[] args) {
        WaitDemo waitDemo1 = new WaitDemo();
        waitDemo1.start();
        WaitDemo waitDemo2 = new WaitDemo();
        waitDemo2.start();
    }

}

此处涉及到的是wait()方法      ,notify()方法

wait()是Object类中定义的方法,必须用同步对象调用,让线程等待,必须再度唤醒才能继续执行
notify()Object类中定义的方法,必须用同步对象调用,唤醒被wait的线程
notifyAll()是Object类中定义的方法,必须用同步对象调用,用来唤醒所有被wait的线程
三个方法都必须在同步代码块中执行

我用我的话来说就是:

wait()出现,那么现在进入同步锁的线程就处于阻塞状态,同时这个锁被打开了,可以让别的线程前来执行任务。

与sleep()不同的是:wait之后的线程放手了,其他的线程也有机会执行任务了。但是sleep就是属于“占着茅坑不拉屎”,它休眠了,锁没解开,其他的进程着急也进不来。【牛马sleep】

notify()就是属于唤醒优先级较高的wait进程(如果就一个wait进程那当然只叫一个啦,那还用说),“哥们别睡了,一会该你上了!”

那么notifyAll()就是唤醒所有wait进程。

经典的进程通信问题:
生产者与消费者在柜台kuku交易,柜台一次只能交易一个商品。生产者生产完放到柜台,就得通知消费者来买。消费者买完之后就得通知生产者生产【我隔壁二大爷楼下邻居家的花母猪惊呆了,生产队的驴也不敢这么来啊!】
详情请看我的另一篇正经博客:

新增创建线程方式

实现Callable接口与使用Runnable相比,Callable功能更强大些.
特点:
相比run()方法,可以有返回值
方法可以抛出异常
支持泛型的返回值
• 需要借助FutureTask类,获取返回结果接收任务
FutureTask<Integer> futureTask = new FutureTask(任务);
创建线程
Thread t = new Thread(futureTask);
t.start();
Integer val = futureTask.get();获得线程call方法的返回值
是不是看不大懂?没关系,我也不懂......
给你看代码,自己悟吧,我也悟一下
自己写的奥 ! 没抄奥!不要误会我奥!
import java.util.concurrent.Callable;

public class SumTask implements Callable<Integer> {
    //Callable 有返回值 可以抛出异常
    @Override
    public Integer call() throws Exception {

        int sum = 0 ;
        for (int i = 0; i < 10; i++) {
            sum+=i;
        }
        return sum;
    }
}



import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

public class Test {
    public static void main(String[] args) {
        SumTask sumTask = new SumTask();
        FutureTask<Integer>futureTask = new FutureTask<>(sumTask);
        Thread thread = new Thread(futureTask);
        thread.start();

        try {
            Integer sum = futureTask.get();
            //get()获取该返回值
            System.out.println(sum);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }

}

OK,那么今天的小葵花妈妈课堂就结束啦!孩子学习老不好,多半是废了,没收手机就好了。

see u~~~~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值