Java线程学习

线程

1.1 、线程的定义

  • 线程:线程是负责执行程序的控制单元,即应用程序的一个执行路径。
  • 多线程:一个应用程序可以启动多个线程,有多个执行路径。

1.2、线程和进程的区别

  • 进程:进程即正在运行的程序。进程是一个独立的应用程序,通常结束一个进程不会影响其他进程。每个进程都有自己独立的内存空间。
  • 线程:线程是由进程启动的一个应用的执行路径,多个线程可以共享一个内存空间。当一个线程结束后可能会影响其他线程。

1.3、线程的几个状态

1、创建状态:new一个对象之后的状态。
2、就绪状态:当我们调用了线程的start方法之后的状态。
3、运行状态:当线程正在执行run()方法时的状态。
4、中断状态:当线程停止执行的状态。其中有blocked和waiting状态差别是waiting状态不能自动唤醒。
5、死亡状态:线程死亡了(线程执行完run)。

2.1、创建线程的两个方法

1、实现Runnable接口

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

在主函数中创建实现Runnable的线程

    public static void main(String[] args) {
        MyThread my = new MyThread();
        Thread thread = new Thread(my);
        //thread.setName();
        thread.start();
        for (int i = 0; i < 20 ; i++) {
            System.out.println(Thread.currentThread().getName()+":"+i);
        }

    }

2、继承Thread类

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

在主函数中创建继承Thread的线程(main函数也是一个线程)

    public static void main(String[] args) {
//        MyThread my = new MyThread();
//        Thread thread = new Thread(my);
//        //thread.setName();
//        thread.start();
        for (int i = 0; i < 20 ; i++) {
            System.out.println(Thread.currentThread().getName()+":"+i);
        }
        MyThread2 my2 = new MyThread2();
        my2.start();
    }

2.2、线程常用方法

1、getId():返回线程的标识符即编号。
2、getName():返回线程的名字。有set方法
3、getPriority():返回线程的优先级。有set方法。优先级越高极有可能先运行,但不一定
4、isDaemon():测试这个线程是否是守护线程(精灵线程)。
5、sleep(long millis):进入睡眠,醒来进入就绪状态。
6、yield():出让CPU。
7、wait():导致当前线程等待,知道调用notify()或notifyAll()方法,或者指定时间已过。
8、currentThread():获取当前线程对象。

3线程同步

3.1、线程同步的概念

多个线程同时访问某个资源就称为线程同步。这时就有可能出现问题,先来举个例子。

public class MyThread3 implements Runnable{
    int n = 0 ;
    @Override
    public void run() {
        n++;
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+":"+"是第"+n+"个进入的线程");
    }
}

        MyThread3 my3 = new MyThread3();
        Thread t = new Thread(my3);
        Thread t1 = new Thread(my3);
        t.start();
        t1.start();

这里有两个线程如果要同时访问这个my3的对象那么结果会如何?
运行结果如此
可以看到出了问题我们理想状态下,一个应该是第1个进入和第2个进入,可是现在同时是第2个进入,出现了资源抢占。那么这时候就要用到synchronized关键字。


public class MyThread3 implements Runnable{
    int n = 0 ;
    @Override
    public void run() {
        synchronized ("abc") {
            n++;
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + ":" + "是第" + n + "个进入的线程");
        }
        System.out.println("我是"+Thread.currentThread().getName());
    }
}

增加了synchronized关键字后运行结果如下:
线程同步结果

3.2、synchronized关键字也可以修饰方法

public synchronized void sing() {
        System.out.println("唱歌");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

3.3、死锁

我们先来建立一个类

public class Person implements Runnable{
    Person p1;
    Person p2;

    public Person() {
    }

    public Person(Person p1 ,Person p2){
        this.p1 = p1;
        this.p2 = p2;
    }



    @Override
    public void run() {
        String name = Thread.currentThread().getName();
        if ("t1".equals(name)){
            synchronized (p1){
                System.out.println(name+"进入run方法");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            synchronized (p2){
                System.out.println("锁定p2");

                }
            }
        }else {
            synchronized (p2){
                System.out.println(name+"进入run方法");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            synchronized (p1){
                System.out.println("锁定p1");

                }
            }
        }
    }
}

在这个类里面有两个对象p1,p2而不同的线程会在执行run方法时锁住其中一个对象,同时又不释放自己锁定的对象,还去锁定另一个线程锁定的对象,这时候就会造成死锁。

 public static void main(String[] args) {
        Person p1 = new Person();
        Person p2 = new Person();
        Person p = new Person(p1,p2);
        Thread t1 = new Thread(p);
        Thread t2 = new Thread(p);
        t1.setName("t1");
        t2.setName("t2");
        t1.start();
        t2.start();
    }

运行结果如下:
死锁
可以看到虚拟机并不停止,而是一直等待。为了解决这个问题就需要一个设计模式了

4、生产者消费者设计模式

我们想去吃馒头,那么这里有个馒头类

public class ManTou {
    private int n ;

    public ManTou(int n) {
        this.n = n;
    }

    public int getN() {
        return n;
    }
}

假设有一个食堂,它提供生产馒头和消费馒头的服务

public class ShiTang {
    ManTou[] ms = new ManTou[5];

    int i = -1 ;//记录馒头位置
    //生产馒头
    public synchronized void put(ManTou m){
        if (ms[ms.length-1] != null){
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        i++;
        ms[i] = m;
        System.out.println(Thread.currentThread().getName()+"生产了:"+m.getN()+"号馒头");
        notify();//通知消费者消费
    }
    //消费馒头
    public synchronized void pop(){
        if (ms[0] == null){
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        ManTou m ;
        m = ms[i];
        ms[i] = null;//消费者消费馒头
        System.out.println(Thread.currentThread().getName()+"消费了:"+m.getN()+"号馒头");
        i--;
        notify();//通知生产者生产馒头
    }
}

那要生产馒头就得有生产者:

public class Producer implements Runnable{
    private ShiTang shiTang;
    public Producer(ShiTang shiTang){
        this.shiTang = shiTang;
    }

    @Override
    public void run() {
        int n = -1;
        while (true){
            n++;
            ManTou m = new ManTou(n);
            shiTang.put(m);
            try {
                Thread.sleep(3000);//生产一个就要休息一下
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

馒头生产出来就需要消费者消费馒头

public class Consumer implements Runnable{
    private ShiTang shiTang;
    public Consumer(ShiTang shiTang){
        this.shiTang = shiTang;
    }
    @Override
    public void run() {
        while (true){
            shiTang.pop();
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

建立测试类看一下运行结果

public class Test {
    public static void main(String[] args) {
        ShiTang shiTang = new ShiTang();
        Producer p = new Producer(shiTang);
        Consumer c= new Consumer(shiTang);
        Thread t = new Thread(p);
        Thread t1 = new Thread(c);
        t.setName("生产者");
        t1.setName("消费者");
        t.start();
        t1.start();
    }
}

运行结果如下:
在这里插入图片描述
这就是生产者消费者设计模式,合理运用了资源。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值