java自学之路十五(多线程)

多线程

并发:交替执行

并行:同时进行

小贴士:command + shift + f7 高亮所有相同变量

public class DemoMultiThread extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            System.out.println("run"+i);
        }
    }
}
    public static void main(String[] args) {
        DemoMultiThread mt = new DemoMultiThread();
        mt.start();

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

执行原理

  1. main方法压栈执行
  2. run方法是单线程,start方法会开辟一个新的栈空间
  3. 多个线程之间互不影响,在不同的栈空间

Thread

获取线程的名称

  1. Thread类中的getName()
  2. 可以获取当前正在执行的线程,使用线程中getName()获取线程名称
public class DemoGetName extends Thread{
    @Override
    public void run() {
        String thread_name = getName();
        System.out.println(thread_name);
    }
}
    private static void show02() {
        DemoGetName mt = new DemoGetName();
        mt.start(); //第一个线程
        new DemoGetName().start(); //第二个线程
    }
        Thread t = Thread.currentThread();
        System.out.println(t);
        String name = t.getName();
        System.out.println(name);

设置线程的名称

  1. setName(名字)
  2. 构造一个无参和有参方法,把线程名字传递给父类,让父类给子线程起名字

sleep方法

        for (int i = 0; i < 60; i++) {
            System.out.println(i);
            Thread.sleep(1000);
        }

静态方法,传递的是一个毫秒值

创建多线程方式二

实现Runnable接口的类,该类然后实现run方法。Thread构造方法中可以传递该类,start开启线程

  1. 创建Runable接口的实现类

    public class DemoRunable implements Runnable {
        @Override
        public void run() {
            for (int i = 0; i < 10; i++) {
                System.out.println("i");
            }
        }
    }
    
  2. 在实现类中重写run方法

  3. 创建runable接口的实现类对象

  4. 创建thread类对象,传递runable接口的实现类对象

  5. 调用thread中的start方法

  6.     private static void show04() {
            DemoRunable run = new DemoRunable();
            new Thread(run).start();
        }
    

#### 继承Thread类和Runnable接口区别

- Runnable接口创建多线程的好处
1. 避免了单继承的局限性
2. 增强了程序的扩展性,降低了耦合性

#### 匿名内部类创建多线程

private static void show05() {
    new Thread(new Runnable(){
        @Override
        public void run() {
            for (int i = 0; i < 30; i++) {
                System.out.println("hello"+i);
            }
        }
    }).start();
}

#### 同步代码块

syncronized(锁对象){
可能会出现线程安全问题的代码
}


注意

1. 锁对象可以是任意的对象
2. 多线程的锁对象是同一个

private static void show06() {
DemoTicket ticket = new DemoTicket();
Thread mt1 = new Thread(ticket);
Thread mt2 = new Thread(ticket);
Thread mt3 = new Thread(ticket);

mt1.start();
mt2.start();
mt3.start();

}


public class DemoTicket implements Runnable{
private int ticket = 100;
Object obj = new Object();

@Override
public void run() {


    while(true){
        synchronized (obj){
            if(ticket>0){

/* 展现bug

                try{
                    Thread.sleep(1000);
                }catch (InterruptedException e){
                    e.printStackTrace();
                }*/

                System.out.println(Thread.currentThread().getName()+"-->正在卖"+ticket+"张");
                ticket --;
            }else{
                break;
            }
        }
    }
}

}


原理:

使用了一个锁对象,这个锁对象叫同步锁,也叫同步监视器

3个线程一起抢夺cpu执行权,如果进程1先抢到,执行run方法,遇到sychronized代码块这时进程1会检查同步代码块是否有锁对象,如果有,就会获取到锁对象,进入到同步中执行。

进程2抢到了cpu执行权,执行run方法,遇到synchronized代码块,会检查同步代码块是否有锁对象,发现没有就会进入阻塞状态,会一直等待进程1归还锁对象。

同步中的线程,没有执行完毕不会释放锁,同步外的线程没有锁进不去同步

#### 同步方法

1. 把访问了共享数据的代码抽取出来,放到一个方法中
2. 在方法上添加synchronized修饰符

修饰符 synchrond 返回值类型 方法名(params){}


public class DemoSellTicket implements Runnable{
private int ticket = 100;

@Override
public void run() {
    while (true){
        sellTicket();
    }
}

public synchronized void sellTicket(){
    if(ticket>0){
        System.out.println(Thread.currentThread().getName()+"-->正在卖"+ticket+"张");
        ticket --;
    }else{
        return;
    }
}

}


同步方法也会将方法内部的代码锁住,只让一个线程执行,锁对象就是实现类对象,也就是this

静态同步方法

静态方法 访问 静态变量

public class DemoSellTicket implements Runnable{
private static int ticket = 100;

@Override
public void run() {
    while (true){
        sellTicket();
    }
}

public static synchronized void sellTicket(){
    if(ticket>0){
        System.out.println(Thread.currentThread().getName()+"-->正在卖"+ticket+"张");
        ticket --;
    }else{
        return;
    }
}

}


**this是创建对象之后产生的,静态方法优先于对象,静态方法的锁对象是本类的class属性**

相当于synchronized(RunnableImpl.class){}

#### Lock锁

1. 在成员位置创建一个reentrantlock对象

2. 在可能出现安全问题的代码钱使用Lock接口类中的lock获取锁

3. 释放锁

@Override
public void run() {
while (true){
lock.lock();
if(ticket>0){
System.out.println(Thread.currentThread().getName()+"-->正在卖"+ticket+"张");
ticket --;
}else{
lock.unlock();
break;
}
lock.unlock();
}

}


释放锁最好配合finally一起使用

#### 等待唤醒机制

多线程间的一种协作机制,一个线程进行了规定操作后,就进入等待状态wait,等待其他线程执行完他们的指定代码后,再将其唤醒notify,再有多个线程进行等待时,如果需要可以使用notifyall来唤醒所有的等待线程。

注意:被通知的等待线程,不能立即恢复执行,因为它当初中断的地方是在同步块内,而此刻它已经不持有锁,所以需要重新去获取锁,才能在调用wait方法之后的地方恢复执行。

- 如果可以获取锁,线程就从waiting编程runnable
- 否则从wait set出来,进入entry set,线程从waitting变成blocked

1. wait和notify方法必须要由同一个锁对象调用。
2. wait方法和notify方法是属于object类的方法。
3. wait方法和notify方法必须要在同步代码块或者同步函数中使用

public class ProduceNood implements Runnable {

Noodles noodles = new Noodles();

public ProduceNood(Noodles noodles) {
    this.noodles = noodles;
}

@Override
public void run() {
    synchronized (noodles){

            while (true){
                System.out.println("start");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("end");
                noodles.notify();
                noodles.setStatus(true);
                if(noodles.isStatus()){
                    try {
                        noodles.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
    }
}

}


public class ConsumerNood implements Runnable {
Noodles noodles = new Noodles();

public ConsumerNood(Noodles noodles) {
    this.noodles = noodles;
}

@Override
public void run() {
    synchronized (noodles){
        while (true){
                System.out.println("consuming");
                noodles.setStatus(false);
                noodles.notify();
                if(!noodles.isStatus()){
                    try {
                        noodles.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
        }
    }
}

}


public class DemoTest {
public static void main(String[] args) {
Noodles noodles = new Noodles();
noodles.setStatus(false);

    ProduceNood mt1 = new ProduceNood(noodles);
    ConsumerNood mt2 = new ConsumerNood(noodles);

    new Thread(mt1).start();
    new Thread(mt2).start();

}

}


#### 线程池

static ExecutorService newFixedThreadPool(int nThreads)

参数 创建线程池中包含线程数量

返回值: executorService接口,返回的是ExecutorService接口的实现类对象,可以用ExecutorService接收(面向接口编程)

用来从线程池中获取线程调用start方法,执行线程任务,submit(runnable task)提交一个runnable任务用于执行,void shutdown() 关闭

步骤

1. 使用线程池工厂类Excutors提供静态方法newFixedThreadPool生产一个指定线程数量的线程池
2. 创建一个类,实现runnable接口,重写run方法,设置线程任务
3. 调用ExecutorService中的submit,传递线程任务(实现类),开启线程,执行run
4. 调用ExecutorService的shutdown销毁线程池(不建议执行)

public class DemoThreadPool implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "创建了线程池");
}
}


private static void show01() {
ExecutorService es = Executors.newFixedThreadPool(2);
es.submit(new DemoThreadPool());
es.submit(new DemoThreadPool());
es.submit(new DemoThreadPool());
}

原文地址:https://www.cnblogs.com/jimmyhe/p/11967727.html