多线程快速入门+ 多线程之间通讯

多线程快速入门

一、线程与进程的区别 

二、为什么要用到多线程 

三、多线程应用场景 

四、使用继承方式创建线程 

五、使用Runnable接口方式创建线程 

六、使用匿名内部类方式创建线程 

七、多线程常用api 

八、守护线程与非守护线程 

九、多线程几种状态

十、join方法介绍 
 

十二、使用多线程分批处理信息 

多线程之间通讯

一、多线程之间通讯 

多线程之间通讯需求
需求:第一个线程写入(input)用户,另一个线程读取(output)用户,实现一个读,一个写操作。

/**
 * 共享资源实体类
 */
class Res{
    public String userName;
    public String sex;
}

/**
 * 写的线程
 */
class Input extends Thread{

    private Res res;

    public Input(Res res){
        this.res = res;
    }

    @Override
    public void run() {
        // 写的操作
        int count = 0;
        while (true){
            if(count == 0){
                res.userName = "小红";
                res.sex = "女";
            }else{
                res.userName = "小明";
                res.sex = "男";
            }
            // 计算奇数或者偶数公式
            count=(count+1)%2;
        }
    }
}

/**
 * 读的线程
 */
class Output extends Thread{
    private Res res;

    public Output(Res res){
        this.res = res;
    }

    @Override
    public void run() {
        while (true){
            System.out.println(res.userName+","+res.sex);
        }
    }
}

/**
 * Created by yz on 2018/04/02.
 */
public class OutputAndInputThread {
    public static void main(String[] args) {
        // 多个线程共享同一资源
        Res res = new Res();
        Input input = new Input(res);
        Output output = new Output(res);
        input.start();
        output.start();
    }
}

运行结果:

原因解析:小红明明定义的是女的,但是因为两个线程同时操作Res对象,一个写,一个读,当读到小红性别时,这时候写的线程将姓名赋值为小明,性别赋值为男,读的线程读取到性别男,所以产生线程安全问题。

解决:两个线程不能在同一时刻操作资源,读的时候,不要写,使用同一把锁。

/**
 * 写的线程
 */
class Input extends Thread{

    private Res res;

    public Input(Res res){
        this.res = res;
    }

    @Override
    public void run() {
        // 写的操作
        int count = 0;
        while (true){
            synchronized (res){
                if(count == 0){
                    res.userName = "小红";
                    res.sex = "女";
                }else{
                    res.userName = "小明";
                    res.sex = "男";
                }
                // 计算奇数或者偶数公式
                count=(count+1)%2;
            }
        }
    }
}

/**
 * 读的线程
 */
class Output extends Thread{
    private Res res;

    public Output(Res res){
        this.res = res;
    }

    @Override
    public void run() {
        while (true){
            synchronized (res){
                System.out.println(res.userName+","+res.sex);
            }
        }
    }
}

二、wait()、notify()区别 

wait()、notify()、notifyAll()这三个定义Object类里的方法,可以用来控制线程状态。
这三个方法都是jvm里native方法。同步中使用。
如果对象调用了wait方法,就会使持有该对象的线程把该对象的控制权交出去,然后处于等待状态。
如果对象调用了notify方法,就会通知某个正在等待的这个对象的控制权的线程可以继续运行。
如果对象调用了notifyAll方法,就会通知所有等待这个对象控制权的线程继续运行。

使用synchronized关键字虽然解决了线程同步,但是现在没有按照写一个读一个的顺序执行:

解决办法:生产者线程生产一个,消费者线程立马消费。生产者没有任何生产时,消费者不能读。消费者没有消费完,生产者不能再继续生产。wait()和notify() 一起使用,一个当前线程等待,一个唤醒线程

/**
 * 共享资源实体类
 */
class Res{
    public String userName;
    public String sex;
    // true 生产者线程等待,消费者线程消费; false 生产者线程生产,消费者线程等待
    public boolean flag = false;
}

/**
 * 写的线程
 */
class Input extends Thread{

    private Res res;

    public Input(Res res){
        this.res = res;
    }

    @Override
    public void run() {
        // 写的操作
        int count = 0;
        while (true){
            synchronized (res){
                if(res.flag){
                    try {
                        // 让当前线程从运行状态变为休眠状态 并且释放锁的资源
                        res.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                if(count == 0){
                    res.userName = "小红";
                    res.sex = "女";
                }else{
                    res.userName = "小明";
                    res.sex = "男";
                }
                // 计算奇数或者偶数公式
                count=(count+1)%2;
                res.flag = true;
                // 唤醒对方等待的线程
                res.notify();
            }
        }
    }
}

/**
 * 读的线程
 */
class Output extends Thread{
    private Res res;

    public Output(Res res){
        this.res = res;
    }

    @Override
    public void run() {
        while (true){
            synchronized (res){
                if(!res.flag){
                    try {
                        // 生产者线程生产时,消费者线程等待
                        res.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println(res.userName+","+res.sex);
                res.flag = false;
                // 唤醒对方等待的线程
                res.notify();
            }
        }
    }
}

/**
 * Created by yz on 2018/04/02.
 */
public class OutputAndInputThread {
    public static void main(String[] args) {
        // 多个线程共享同一资源
        Res res = new Res();
        Input input = new Input(res);
        Output output = new Output(res);
        input.start();
        output.start();
    }
}

实现效果,生产一个,读取一个:

wait与sleep区别是什么?

wait 用于同步中,可以释放锁的资源。Object类。
sleep 不会释放锁的资源。Thread类
wait 需要notify才能从休眠状态变为运行状态
sleep 时间到期,从休眠状态变为运行状态
wait和sleep相同功能都是做休眠。
 
三、jdk1.5Lock锁

synchronized(res){

}
从什么时候开始上锁 代码开始
从什么时候释放锁 代码结束
synchronized是内置锁 自动锁

缺点:效率低、扩展性不高、不能自定义

JDK1.5并发包
java.util.concurrent.atomic
java.util.concurrent.locks 

Lock锁 保证线程安全问题
 

lock锁与synchronized同步锁区别:

1)Lock是一个接口,而synchronized是Java中的关键字,synchronized是内置的语言实现,synchronized是在JVM层面上实现的,不但可以通过一些监控工具监控synchronized的锁定,而且在代码执行时出现异常,JVM会自动释放锁定,但是使用Lock则不行,lock是通过代码实现的,要保证锁定一定会被释放,就必须将 unLock()放到finally{} 中;

2)synchronized在发生异常时,会自动释放线程占有的锁,因此不会导致死锁现象发生;而Lock在发生异常时,如果没有主动通过unLock()去释放锁,则很可能造成死锁现象,因此使用Lock时需要在finally块中释放锁;

3)Lock可以让等待锁的线程响应中断,线程可以中断去干别的事务,而synchronized却不行,使用synchronized时,等待的线程会一直等待下去,不能够响应中断;

4)通过Lock可以知道有没有成功获取锁,而synchronized却无法办到。

5)Lock可以提高多个线程进行读操作的效率。

lock锁手动开启上锁,手动释放锁 是接口
synchronized 程序有异常自动释放锁 是jvm关键字

多线程并发(多个Thread操作同一资源)与网站并发(多个请求同时请求一台服务器)

Lock lock = new ReentrantLock(); 重入锁,重复使用

wait、notify只能在synchronized中使用

Condition用法
Condition的功能类似于在传统的线程技术中Object.wait()和Object.notify()的功能。
代码:
Condition condition = lock.newCondition();
res.condition.await();类似wait

res.Condition.Signal(); 类似notity

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

/**
 * 共享资源实体类
 */
class Res{
    public String userName;
    public String sex;
    // true 生产者线程等待,消费者线程消费; false 生产者线程生产,消费者线程等待
    public boolean flag = false;
    // ReentrantLock重入锁
    Lock lock = new ReentrantLock();
}

/**
 * 写的线程
 */
class Input extends Thread{

    private Res res;
    // Condition的功能类似于在传统的线程技术中Object.wait()和Object.notify()的功能
    Condition condition;
    public Input(Res res,Condition condition){
        this.res = res;
        this.condition = condition;
    }

    @Override
    public void run() {
        // 写的操作
        int count = 0;
        while (true){
            try {
                // 表示开始上锁
                res.lock.lock();
                if(res.flag){
                    condition.await(); // 类似wait
                }
                if(count == 0){
                    res.userName = "小红";
                    res.sex = "女";
                }else{
                    res.userName = "小明";
                    res.sex = "男";
                }
                // 计算奇数或者偶数公式
                count=(count+1)%2;
                res.flag = true;
                condition.signal(); // 类似notity
            } catch (Exception e) {
                e.printStackTrace();
            }finally {
                // 释放锁 如果异常永远释放不了一定要try catch
                res.lock.unlock();
            }
        }
    }
}

/**
 * 读的线程
 */
class Output extends Thread{
    private Res res;
    // Condition的功能类似于在传统的线程技术中Object.wait()和Object.notify()的功能
    Condition condition;

    public Output(Res res,Condition condition){
        this.res = res;
        this.condition = condition;
    }

    @Override
    public void run() {
        while (true){
            try {
                // 表示开始上锁
                res.lock.lock();
                if(!res.flag){
                    condition.await(); // 类似wait
                }
                System.out.println(res.userName+","+res.sex);
                res.flag = false;
                condition.signal();// 类似notity
            } catch (Exception e) {
                e.printStackTrace();
            }finally {
                // 释放锁 如果异常永远释放不了一定要try catch
                res.lock.unlock();
            }
        }
    }
}

/**
 * Created by yz on 2018/04/02.
 */
public class OutputAndInputThread {
    public static void main(String[] args) {
        // 多个线程共享同一资源
        Res res = new Res();
        Condition condition = res.lock.newCondition();
        Input input = new Input(res,condition);
        Output output = new Output(res,condition);
        input.start();
        output.start();
    }
}

lock锁比同步代码块效率高

 
四、怎么停止线程 

为什么stop方法不好?

程序没有执行完,stop的话,程序不会回滚,不可恢复,不安全。

设计怎么停止线程思路?

代码执行完毕。经常循环、无非让循环停止,这个线程就停止。

/**
 * Created by yz on 2018/4/3.
 */
public class StopThread {
    public static void main(String[] args) throws InterruptedException {
        StopThreadDemo stopThreadDemo = new StopThreadDemo();
        stopThreadDemo.start();
        for (int i = 1; i < 10; i++) {
            System.out.println("我是主线程,i:"+i);
            Thread.sleep(1000);
            if(i == 3){
                // 停止线程  interrupt()让当前等待的线程直接抛出异常,并不是停止线程
                stopThreadDemo.interrupt();
                // stopThreadDemo.stopThread();
                //stopThreadDemo.stopThread();
            }
        }
    }
}

class StopThreadDemo extends Thread{

    private volatile boolean flag = true;

    @Override
    public synchronized void run() {
        System.out.println("子线程开始执行...");
        while (flag){
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
                // 抛出异常后,修改flag值 线程停止有延迟
                stopThread();
            }
        }
        System.out.println("子线程结束执行...");
    }

    public void stopThread(){
        System.out.println("调用stopThread方法...");
        this.flag = false;
        System.out.println("已经将flag修改为"+flag);
    }
}

多线程基础

内容:


1.线程与进程
2.创建线程的两种方式
3.线程的生命周期
4.cup时间片切换原理
5.线程休眠之sleep与interrupt
6.线程阻塞之join
7.线程阻塞之yield与线程优先级
8.线程练习
9.线程同步和线程同步块
10.生产者与消费者

1.线程与进程

进程Process:操作系统中正在运行的程序就是进程
  java程序是运行在jvm之上,因此这个进程肯定是和jvm有关,这个进程的名字是:javaw.exe
我们是站在操作系统的角度看进程
        进程(就是运行中的程序) 一个程序两种状态:静止状态(可执行文件,不是进程),运行中状态(进程).

线程

线程(Thread):进程中命令的执行轨迹,因为它是线状的,所以称为线程.
单线程:如果一个进程中命令的执行轨迹只有一条,那就是单线程.

多线程:如果一个进程中命令的执行轨迹有两条或两条以上,那就是多线程(迅雷同时下载多个文件).

2.创建线程的两种方式

继承Thread类 或 实现Runable接口

Runable接口实现类:
Thread(线程) run() 
MyTask(任务) run()

代码:

/**
 * Created by yz on 2018/2/28.
 */
public class MyThread extends Thread{
    @Override
    public void run() {
        System.out.println("test Thread 当前线程是:"+Thread.currentThread().getName());
    }

    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        myThread.setName("yz");
        myThread.start();
    }
}


/**
 * Created by yz on 2018/2/28.
 */
public class MyRunnable  implements Runnable{
    public void run() {
        System.out.println("test Runnable 当前线程是:"+Thread.currentThread().getName());
    }

    public static void main(String[] args) {
        MyRunnable myTask = new MyRunnable();
        Thread t = new Thread(myTask);
        t.setName("yz");
        t.start();
    }
}

调用关系图:

3.线程的生命周期

一个线程Thread(一生)有5个阶段即生命周期(由jvm规定)
初始阶段 刚new出线程对象 MyThread myThread = new MyThread();
就绪阶段 myThread.start();
运行阶段 run()
阻塞阶段 可人为干涉 
终止阶段 

4.cup时间片切换原理

cup时间片用完了,任务没完成,该任务返回就绪,cup继续加载另一个线程,来来回回切换线程.
cup时间片用完了,任务也完成,任务终止

5.线程休眠之sleep与interrupt

sleep(被动放弃cpu到阻塞状态);interrupt(中断该线程)

6.线程阻塞之join

/**
 * Created by yz on 2018/2/28.
 */
public class MyThread extends Thread{
    @Override
    public void run() {
        for(int i=0;i<=10000;i++){
           System.out.println("Thread 当前线程是:"+Thread.currentThread().getName()+ "i="+i);
        }
    }

    /**
     * 两个线程并发执行测试
     * @param args
     */
    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        myThread.setName("yz");
        myThread.start();
        for(int i=0;i<=10000;i++){
            System.out.println("Main 当前线程是:"+Thread.currentThread().getName()+ "i="+i);
            if(i==5){
                System.out.println("现在是"+Thread.currentThread().getName()+"它开始等待myThread线程任务全部完成再继续自己的任务");
                try {
                    //一直等到myThread线程任务完成,再继续运行main线程
                    myThread.join();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
 

7.线程阻塞之yield与线程优先级

yield(被动放弃cpu,被踢出后到运行阶段,继续抢线程)

线程优先级:

static int MAX_PRIORITY  线程可以拥有的最大优先级。  static int MIN_PRIORITY  线程可以拥有的最小优先级。  static int NORM_PRIORITY 分配给线程的默认优先级。

8.线程练习

package com.yz.thread;

/**
 * 在一个线程中算出数组元素之和
 * Created by yz on 2018/2/28.
 */
public class Addition {
    public static void main(String[] args) throws InterruptedException {
        int[] nums = {1,2,3,4,5,6};
        int[] result = new int[1];
        //希望你再启动一个线程,在这个线程中算出nums数组中元素之和
        //把结果放入result数组中,最后在main线程中打印result[0]
        YzThread yt = new YzThread(nums,result);
        yt.start();
        //先让yt线程运行完毕,在运行main线程
        Thread.sleep(1000);
        System.out.println(result[0]);

    }
}

class YzThread extends Thread{
    private int[] nums;
    private int[] result;
    YzThread(int[] nums,int[] result){
        this.nums = nums;
        this.result = result;
    }
    @Override
    public void run() {
        int sum = 0;
        for (int n:nums){
            sum+=n;
        }
        result[0] = sum;
    }
}

9.线程同步和线程同步块

线程同步区分:多个线程访问同一个资源动作是否一致?因为只有动作一致,数据才安全

synchronized 同步锁(线程排队) 同步的代码越多,效率越低,使用同步代码块

//同步代码块 唯有同步代码块内容,线程只有一个一个访问 缩小同步范围,效果更高
synchronized(this){
if(number>0){
number--;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("购票成功,现在还有票"+number+"张");
}else{
System.out.println("对不起,票已售完");
}

}

10.生产者与消费者
this.wait()
this.notify()
 

wait和notify方法必须在synchronized内部

public static void main(String[] args) throws Exception {
    Object object = new Object();
    Thread t1 = new Thread(() -> {
        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName()+",i="+i);
            if (i==5){
                synchronized (object) {
                    try {
                        System.out.println(Thread.currentThread().getName()+" 开始等等。。。");
                        object.wait();
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        }
    });

    Thread t2 = new Thread(() -> {
        synchronized (object) {
            System.out.println(Thread.currentThread().getName()+" 发送notify通知。。。");
            object.notify();
        }
    });
    // 启动第一个线程
    t1.start();
    Thread.sleep(1000);
    System.out.println(t1.getName()+" 状态:"+t1.getState());
    // 启动第二个线程
    t2.start();
}

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值