黑马程序员——JAVA基础------进程和线程

——Java培训、Android培训、iOS培训、.Net培训、期待与您交流! ——

一、进程

程序是指令和数据的有序集合,其本身没有任何运行的含义,是一个静态的概念。而进程是程序在处理机上的一次执行过程,它是一个动态的概念。
进程是一个具有一定独立功能的程序,一个实体,每一个进程都有它自己的地址空间。

二、进程的状态

进程执行时的间断性,决定了进程可能具有多种状态。事实上,运行中的进程具有以下三种基本状态。

  1. 就绪状态
  2. 运行状态
  3. 阻塞状态

    这里写图片描述

三、线程

线程实际上是进程中的一个独立的控制单元。是进程中的一个执行路径,共享一个内存空间,线程之间可以自由切换,并发执行,一个进程最少有一个线程(单线程程序)

PS:Java VM 启动的时候会有一个进程java.exe,该进程中至少一个线程负责java程序的执行。而且这个线程运行的代码存在于main方法中。该线程称之为主线程。
扩展:其实更细节说明jvm,jvm启动不止一个线程,还有负责垃圾回收机制的线程。

四、线程的两种实现方式

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

PS:优先使用第二种方式实现进程,因为第一种方式是继承类,如果有其它需求,就无法再继承;而第二种是实现接口,如果有其它需求,还可以再继承或实现。

代码演示:

package com.joe.thread;

import java.util.Date;

/**
 * 多线程的实现方式: 1、继承Thread类 2、实现Runnable接口
 * 两种方式的区别:
 * 继承Thread:线程代码存放在Thread子类run方法中
 * 实现Runnable:线程代码存放在接口的子类run方法中
 * @author joe
 *
 */
public class ThreadDemo {

    public static void main(String[] args) {
        // 使用自定义线程
        // 第一种实现线程方法:继承Thread类
        MyThread myThread = new MyThread();
        myThread.start();// 启动线程并执行该线程的run方法
        // myThread.run(); 仅仅是对象调用方法。而线程创建了,并没有运行。
        for (int i = 0; i < 20; i++) {
            System.out.println("main-" + i);
        }

        // 第二种实现线程的方法:实现Runnable接口
        MyRunnable myRunnable = new MyRunnable();
        // myRunnable.run();
        Thread thread2 = new Thread(myRunnable);
        thread2.start();

    }

}

// 自定义线程类1:继承Thread类
// 步骤:
// 1、定义类继承Thread
// 2、复写Thread类中的run方法
// 3、调用线程的start方法,该方法有两个作用:启动线程;调用run方法

// 为什么要覆盖run方法呢?
// Thread类用于描述线程,该类就定义了一个功能,用于存储线程要运行的代码。该存储功能就是run方法
// 也就是说Thread类中的run方法,用于存储线程要运行的代码
class MyThread extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println(new Date() + "-" + i);
        }
    }
}

// 自定义的线程2:实现Runnable接口
// 步骤:
// 1、定义类实现Runnable接口
// 2、覆盖Runnable接口中的run方法
// 将线程要运行的代码存放在该run方法中
// 3、通过Thread类建立线程对象
// 4、将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数
// 为什么要将Runnable接口的子类对象传递给Thread的构造函数
// 因为,自定义的run方法所属的对象是Runnable接口的子类对象
// 所以要让线程去指定指定对象的run方法,就必须明确该run方法所属的对象
// 5、调用Thread类的start方法开启线程并调用Runnable接口子类的run方法

class MyRunnable implements Runnable {

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println("MyRunnale-" + i);
        }
    }

}

五、线程的操作方法

  • Thread(Runnable target)

      分配新的 Thread 对象。
    
  • Thread(Runnable target, String name)

      分配新的 Thread 对象。
    
  • Thread(String name)

      分配新的 Thread 对象。
    
  • static Thread currentThread()

      返回对当前正在执行的线程对象的引用。
    
  • long getId()

      返回该线程的标识符。
    
  • String getName()

      返回该线程的名称。
    
  • void setName(String name)

      改变线程名称,使之与参数 name 相同。
    
  • boolean isAlive()

      测试线程是否处于活动状态。
    
  • static void sleep(long millis)

      在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。
    
  • static void sleep(long millis, int nanos)

      在指定的毫秒数加指定的纳秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。
    
  • void join()

      等待该线程终止。
    
  • void join(long millis)

      等待该线程终止的时间最长为 millis 毫秒。
    
  • void join(long millis, int nanos)

      等待该线程终止的时间最长为 millis 毫秒 + nanos 纳秒。
    
  • void interrupt()

      中断线程。
    
  • static boolean interrupted()

      测试当前线程是否已经中断。
    
  • void setPriority(int newPriority)

      更改线程的优先级。
    
  • int getPriority()

      返回线程的优先级。
    
  • boolean isDaemon()

      测试该线程是否为守护线程。
    
  • void setDaemon(boolean on)

      将该线程标记为守护线程或用户线程。
    
  • static void yield()

      暂停当前正在执行的线程对象,并执行其他线程。
    

六、线程同步

在多线程的操作中,多个线程有可能同时处理同一个资源,这就是多线程中的共享数据。但也发现共享数据的同时,有时会出现安全问题。

出现问题的原因:当多条语句在操作同一个线程共享数据时,一个线程对多天语句只执行了一部分,还没有执行完,另一个线程参与进来执行。导致共享数据的错误。

解决办法:对多条操作共享数据的语句,只能让一个线程都执行完。在执行过程中,其他线程不可以参与执行。Java对于多线程的安全问题提供了专业的解决方式——同步。

同步的方法有两种:

  1. 同步代码块
    synchronized(要同步的对象){
    要同步的操作;
    }
  2. 同步方法
    public synchronized void method(){
    要同步的操作;
    }

同步的准则:
当编写synchronized时,有几个简单的准则可以遵循,这些准则在便面死锁和性能危险的风险方面大有帮助:

  1. 使代码保持简短。把不随线程变化的预处理和后处理移出synchronized块
  2. 不要阻塞。如:InputStream.read()
  3. 在持有锁的时候,不要对其他对象调用方法

使用同步的前提:

  1. 必须要有两个或者两个以上的线程
  2. 必须是多个线程使用统一个锁

    代码演示:

package com.joe.thread;

/**
 * 多线程共享数据的安全问题,使用同步解决 1、同步代码块 2、同步方法 同步代码会带来性能降低的问题,提高数据的安全性
 * 
 * @author joe
 * 
 */
public class ThreadDemo01 {

    public static void main(String[] args) {
        MyThread my = new MyThread();
        Thread t1 = new Thread(my, "Joe");
        Thread t2 = new Thread(my, "kk");
        t1.start();
        t2.start();

    }

}

class MyThread implements Runnable {
    Object obj = new Object();// 同步的标记对象

    @Override
    public void run() {
        // 同步代码块
        synchronized (obj) {
            System.out.println(Thread.currentThread().getName() + "正在吃饭...");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "吃完了");

        }
        // doMethod();
    }

    /**
     * 同步方法:同步的是当前对象(this)
     */
    public synchronized void doMethod() {
        System.out.println(Thread.currentThread().getName() + "正在吃饭...");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "吃完了");

    }

}

七、线程死锁

过多的同步有可能出现死锁,死锁的操作一般是在程序运行的时候才有可能出现。

代码演示:

package com.joe.thread;

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

}

class Test implements Runnable {
    private boolean flag;

    Test(boolean flag) {
        this.flag = flag;
    }

    public void run() {
        if (flag) {
            synchronized (MyLock.locka) {
                System.out.println("if locka");
                synchronized (MyLock.lockb) {
                    System.out.println("if lockb");
                }
            }
        } else {
            synchronized (MyLock.lockb) {
                System.out.println("else lockb");
                synchronized (MyLock.locka) {
                    System.out.println("els locka");
                }
            }

        }
    }
}

class MyLock {
    static Object locka = new Object();
    static Object lockb = new Object();

}

八、wait方法和notify方法

代码演示:

package com.joe.thread;

/**
 * wait(); notify(); notifyAll() 都使用在同步中,因为要对持有监视器(锁)的线程操作,所以要使用在同步中,因为只有同步才具有锁
 * 
 * 为什么这些操作线程的方法要定义Object类中呢?
 * 因为这些方法在操作同步中线程时,都必须要标识它们所操作线程只有的锁,只有同一个锁上的被等待线程,可以被同一个锁上notify唤醒
 * ,不可以对不同锁中的线程进行唤醒,也就是说,等待和唤醒必须是同一个锁,而锁可以是任意对象,所以可以被任意对象调用的方法定义Object中
 * 
 * 
 * @author joe
 *
 */

// 主方法
public class Demo03 {
    public static void main(String[] args) {
        Res r = new Res();

        Input in = new Input(r);
        Output out = new Output(r);

        Thread t1 = new Thread(in);
        Thread t2 = new Thread(out);

        t1.start();
        t2.start();

    }

}

// 创建一个对象类
class Res {
    String name;
    String sex;
    boolean flag = false;
}

// 创建一个输入类实现Runnable接口,重写run方法
class Input implements Runnable {
    private Res r;

    Input(Res r) { // 通过传入对象的方法保证执行的与输出类是同一个对象
        this.r = r;
    }

    public void run() {
        int x = 0; // 定义一个数字,用来交替操作
        while (true) {  //利用while循环不断执行程序
            synchronized (r) { // 锁是同一个对象r
                if (r.flag)
                    try {
                        r.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                if (x == 0) {
                    r.name = "Kaykay";
                    r.sex = "girl";
                } else {
                    r.name = "琪琪";
                    r.sex = "女女";
                }
                x = (x + 1) % 2; // 模2,永远只出现0或1
                r.flag = true;
                r.notify();
            }
        }
    }
}

// 创建一个输出类实现Runnable接口,重写run方法
class Output implements Runnable {
    private Res r;

    Output(Res r) { // 通过传入对象的方法保证执行的与输入类是同一个对象

        this.r = r;
    }

    public void run() {
        while (true) {
            synchronized (r) { // 锁是同一个对象r
                if (!r.flag)
                    try {
                        r.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                System.out.println(r.name + "......" + r.sex);
                r.flag = false;
                r.notify();
            }
        }
    }
}

代码优化后:

package com.joe.thread;

/**
 * wait(); notify(); notifyAll() 都使用在同步中,因为要对持有监视器(锁)的线程操作,所以要使用在同步中,因为只有同步才具有锁
 * 
 * 为什么这些操作线程的方法要定义Object类中呢?
 * 因为这些方法在操作同步中线程时,都必须要标识它们所操作线程只有的锁,只有同一个锁上的被等待线程,可以被同一个锁上notify唤醒
 * ,不可以对不同锁中的线程进行唤醒,也就是说,等待和唤醒必须是同一个锁,而锁可以是任意对象,所以可以被任意对象调用的方法定义Object中
 * 
 * 
 * @author joe
 *
 */

// 主方法
public class Demo03 {
    public static void main(String[] args) {
        Res r = new Res();

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

    }

}

// 创建一个对象类
class Res {
    private String name;
    private String sex;
    private boolean flag = false;

    public synchronized void set(String name, String sex) {
        if (flag)
            try {
                this.wait();
            } catch (Exception e) {
            }
        this.name = name;
        this.sex = sex;
        flag = true;
        this.notify();
    }

    public synchronized void out() {
        if (!flag)
            try {
                this.wait();
            } catch (Exception e) {
            }
        System.out.println(name + "......" + sex);
        flag = false;
        this.notify();
    }
}

// 创建一个输入类实现Runnable接口,重写run方法
class Input implements Runnable {
    private Res r;

    Input(Res r) { // 通过传入对象的方法保证执行的与输出类是同一个对象
        this.r = r;
    }

    public void run() {
        int x = 0; // 定义一个数字,用来交替操作
        while (true) { // 利用while循环不断执行程序

            if (x == 0)
                r.set("Kaykay", "girl");
            else {
                r.set("琪琪", "女女");
            }
            x = (x + 1) % 2; // 模2,永远只出现0或1

        }
    }

}

// 创建一个输出类实现Runnable接口,重写run方法
class Output implements Runnable {
    private Res r;

    Output(Res r) { // 通过传入对象的方法保证执行的与输入类是同一个对象

        this.r = r;
    }

    public void run() {
        while (true) {
            r.out();
        }
    }
}

JDK1.5升级操作

package com.joe.thread;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * JDK1.5中提供了多线程升级解决方案。将同步Synchronized替换成了显示Lock操作。将Object中的wait,notify,
 * notifyAll替换了Condition对象。 该对象可以Lock锁,进行获取。
 * 在此示例中,实现了本方只唤醒对方操作
 * 
 * 代码思路:
 * 1、Lock lock = new ReentrantLock();       //创建锁
 *  Condition c1  = new Condition();        //创建Condition
 *  Condition c2  = new Condition();
 * 2、lock.lock();   //调用lock方法
 * try{     c1.await;......;c2.signal;      }   //让本方等待,唤醒对方操作
 * finally{     lock.unlock();              }   //释放锁
 * @author joe
 *
 */

public class CopyOfProducerConsumerDemo {

    public static void main(String[] args) {
        Resource r = new Resource();

        Producer pro = new Producer(r);
        Consumer con = new Consumer(r);

        Thread t1 = new Thread(pro);
        Thread t2 = new Thread(pro);
        Thread t3 = new Thread(con);
        Thread t4 = new Thread(con);

        t1.start();
        t2.start();
        t3.start();
        t4.start();
    }

}

class Resource01 {
    private String name;
    private int count = 1;
    private boolean flag = false;

    private Lock lock = new ReentrantLock();    //创建锁
    private Condition conditon_pro = lock.newCondition();   //创建第一个Condition
    private Condition conditon_con = lock.newCondition();   //创建第二个Condition

    public void set(String name) throws InterruptedException {
        lock.lock();
        try {
            while (flag)
                conditon_pro.await();
            this.name = name + "--" + count++;
            System.out.println(Thread.currentThread().getName() + "...生产者..."
                    + this.name);

            flag = true;
            conditon_con.signal();
        } finally {
            lock.unlock();
        }
    }

    public void out() throws InterruptedException {
        lock.lock();
        try {
            while (!flag)
                conditon_con.await();
            System.out.println(Thread.currentThread().getName()
                    + "...消费者......" + this.name);
            flag = false;
            conditon_pro.signal();
        } finally {
            lock.unlock();
        }
    }
}

class Producer01 implements Runnable {
    private Resource01 res;

    Producer01(Resource01 res) {
        this.res = res;
    }

    public void run() {
        while (true) {
            try {
                res.set("+商品+");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

class Consumer01 implements Runnable {
    private Resource01 res;

    Consumer01(Resource01 res) {
        this.res = res;
    }

    public void run() {
        while (true) {
            try {
                res.out();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值