内容导航:

1、多线程的实现方式

2、线程安全问题

3、线程间通信

4、生产者消费者模式

第一部分多线程的实现方式

在java中多线程实现方式有2种

一、自定义一个类A,继承Thread类

 public class ThreadA extends Thread {
public void run(){ }
} }

此时ThreadA是一个线程类,在ThreadA中重写Thread类中的run方法

调用方式如下

ThreadA A = new ThreadA();
A.start();

二、自定义一个类B,实现Runable接口

 public class ThreadB implements Runnable {
public void run() { }
}

此时ThreadB不是一个线程类,在ThreadB中重写Thread类中的run方法

ThreadB b = new ThreadB();
Thread t1 = new Thread(b);
t1.start();

需要在初始化Thread的时候,把Runnable接口的实现类的对象b作为参数传入


第二部分是线程安全问题

线程安全问题产生的场景:

 public class SaleTickets implements Runnable {

     private int num = 100;   //表示100张火车票   这是共享资源

     //买100张火车票
public void run() {
for (int i = 0; i < num; i++) {
if (num > 0) {
System.out.println(Thread.currentThread().getName() + "买了编号为" + num + "的火车票");
num--;
try {
Thread.sleep(500);
} catch (InterruptedException e) {
}
}
}
}
}
SaleTickets st=new SaleTickets ();
Thread tA=new Thread(st); //用户A
Thread tB=new Thread(st); //用户B tA.setName("用户A线程");
tB.setName("用户B线程"); tA.start();
tB.start();

进入12306购买火车票的时候,为了解决多个人同时进行抢票问题,需要使用多线程来增加系统效率,假如票共有100张,用户A和用户B同时进行买票,如果是单线程,每次用户买票的时候,首先会打印用户买到了哪张票,然后票的总数num减1。

但是由于是多线程,多线程有一个特性就是线程的执行顺序由cpu来分配,所以执行顺序是不确定的,假如用户A执行SaleTickets方法中的第10行的时候,会打印出用户A线程买了编号为100的火车票,此时用户B线程也开始执行SaleTickets方法,用户B也会打印出用户B线程买了编号为100的火车票

这种结果不是合理的,所以就引发线程安全问题

解决线程安全问题的方式有2种

1、同步代码块

 public class SaleTickets implements Runnable {

     private int num = 100;   //表示100张火车票   这是共享资源

     //买100张火车票
public void run() {
for (int i = 0; i < num; i++) {
synchronized (this){
if (num > 0) {
    System.out.println(Thread.currentThread().getName() + "买了编号为" + num + "的火车票");
    num--;
    try {
    Thread.sleep(500);
    } catch (InterruptedException e) {
    }
     }
}
}
}
}

2、同步方法

 public class SaleTickets implements Runnable {

     private int num = 100;   //表示100张火车票   这是共享资源

     public synchronized  void saleOne(){
if (num > 0) {
System.out.println(Thread.currentThread().getName() + "买了编号为" + num + "的火车票");
num--;
try {
Thread.sleep(500);
} catch (InterruptedException e) {
}
}
} //买100张火车票
public void run() {
for (int i = 0; i < 10; i++) {
saleOne();
}
}
}

此时同步方法中的synchronized的锁默认为this


第三部分是线程间通信

线程间的通信机制为等待唤醒机制,多个线程通信的前提是共用同一把锁

wait(long  timeout) 当前线程释放锁,并等待timeout毫秒

notify 唤醒持有同一锁的某个线程

1、定义一个静态object对象,作为线程间通信的锁

 public class MyLock {
public static Object obj = new Object();
}

2、定义线程A

 //定义一个线程类  输出1
public class ThreadA extends Thread { public void run(){
for(int j=0;j<10;j++){
synchronized (MyLock.obj) {
System.out.println(1);
MyLock.obj.notify();
try {
MyLock.obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
} }

3、定义线程B

 //定义一个线程类   输出2
public class ThreadB extends Thread { public void run(){
for(int j=0;j<10;j++){
synchronized (MyLock.obj) {
System.out.println(2);
MyLock.obj.notify();
try {
MyLock.obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
} }

4、定义主函数执行线程A和线程B

 public class TestThreadForSignal {
public static void main(String[] args){
new ThreadA().start();
new ThreadB().start();
}
}

执行结果:交替打印1和2

执行原理:先执行线程A,会被同步代码块synchronized锁住,打印1,然后唤醒共用MyLock.obj锁的线程B,然后线程A进入等待,并释放锁。然后执行线程B,会被同步代码块synchronized锁住,打印2,然后唤醒共用MyLock.obj锁的线程A,然后线程B进入等待,并释放锁。接下来继续执行线程A循环往复.......


第四部分是生产者消费者模式

利用线程间通信可以很好的实现生产者消费者模式

因为这个代码跟上面线程通信的代码很类似,这里博主不打算继续码代码了,用文字做一个简要说明

线程A (农夫)  往篮子里放苹果   如果篮子满了(苹果数量=10),线程A wait,如果没满(苹果数量<10),往篮子里放苹果,同时告诉B(小孩儿) notify唤醒

线程B (小孩儿)从篮子里拿苹果吃   如果篮子空了(苹果数量=0),线程B wait   如果篮子里还有苹果(苹果数量>0),从篮子里拿苹果吃   同时告诉A(农夫)notify唤醒

在这个场景里线程A(农夫)是生产者,线程B(小孩儿)是消费者

Java基础篇---多线程的更多相关文章

  1. 金三银四跳槽季,BAT美团滴滴java面试大纲(带答案版)之一:Java基础篇

    Java基础篇: 题记:本系列文章,会尽量模拟面试现场对话情景, 用口语而非书面语 ,采用问答形式来展现.另外每一个问题都附上“延伸”,这部分内容是帮助小伙伴们更深的理解一些底层细节的补充,在面试中可 ...

  2. 小白—职场之Java基础篇

    java基础篇 java基础 目录 1.java是一种什么语言,jdk,jre,jvm三者的区别 2.java 1.5之后的三大版本 3.java跨平台及其原理 4.java 语言的特点 5.什么是字 ...

  3. java基础篇---I/O技术

    java基础篇---I/O技术   对于任何程序设计语言而言,输入输出(I/O)系统都是比较复杂的而且还是比较核心的.在java.io.包中提供了相关的API. java中流的概念划分 流的方向: 输 ...

  4. java基础篇---HTTP协议

    java基础篇---HTTP协议   HTTP协议一直是自己的薄弱点,也没抽太多时间去看这方面的内容,今天兴致来了就在网上搜了下关于http协议,发现有园友写了一篇非常好的博文,博文地址:(http: ...

  5. java基础篇---I/O技术(三)

    接上一篇java基础篇---I/O技术(二) Java对象的序列化和反序列化 什么叫对象的序列化和反序列化 要想完成对象的输入或输出,还必须依靠对象输出流(ObjectOutputStream)和对象 ...

  6. Java基础篇 - 强引用、弱引用、软引用和虚引用

    Java基础篇 - 强引用.弱引用.软引用和虚引用 原创零壹技术栈 最后发布于2018-09-09 08:58:21 阅读数 4936 收藏展开前言Java执行GC判断对象是否存活有两种方式其中一种是 ...

  7. java基础篇 之 构造器内部的多态行为

    java基础篇 之 构造器内部的多态行为 ​ 我们来看下下面这段代码: public class Main { public static void main(String[] args) { new ...

  8. java基础篇1

    JAVA基础篇1 注释 单行注释 //这是一个单行注释,由两个斜杠组成,不能嵌套多行注释 多行注释 /*这是一个 多行注释 ,//里面不能嵌套多行注释, 但是可以嵌套单行注释*/ 文档注释 /**ja ...

  9. Java基础篇(JVM)——类加载机制

    这是Java基础篇(JVM)的第二篇文章,紧接着上一篇字节码详解,这篇我们来详解Java的类加载机制,也就是如何把字节码代表的类信息加载进入内存中. 我们知道,不管是根据类新建对象,还是直接使用类变量 ...

随机推荐

  1. VUE 异步数据传递给 component props 的问题

    案例一 父组件parent.vue // asyncData为异步获取的数据,想传递给子组件使用 <template> <div> 父组件 <child :child-d ...

  2. fputcsv

    1.a是從結尾添加,w是從頭添加,不知爲什麽 這樣寫最後一行會是錯的,似乎不會自動添加換行符,只有在最後以行寫上"\r\n",才會正確. $fp = sugar_fopen('fi ...

  3. 【概率论】3-3:累积分布函数(Cumulative Distribution Function)

    title: [概率论]3-3:累积分布函数(Cumulative Distribution Function) categories: Mathematic Probability keywords ...

  4. Python学习日记(五)——初识函数(set、深浅拷贝、三目运算、函数、全局变量和局部变量)

    基本数据类型补充 set set集合,是一个无序且不重复的元素集合 #创建 s = {11,22,33,44}#类似字典 s = set() #转换 l = (11,22,33,44) s1 = se ...

  5. jenkins安装NodeJS遇到的问题

    1.通过插件管理安装插件失败 可以修改地址或者手动上传 下载插件失败查看:https://www.cnblogs.com/SmilingEye/p/11424235.html 2.不显示NodeJS配 ...

  6. 测试puppeteer模拟度检测

    var puppeteer = require('puppeteer'); const devices = require('puppeteer/DeviceDescriptors'); const ...

  7. vue js select下拉框

    <template> <ul id="select"> <li> <div class="select-head"&g ...

  8. JS合并多个数组去重算法

    var arr1 = ['a','b']; var arr2 = ['a','c','d']; var arr3 = [1,'d',undefined,true,null]; //合并两个数组,去重 ...

  9. Linux发行版的选择

    1,需要稳定的服务器,选择CentOS 或 RHEL 2,需要自己定制的桌面系统,选择Ubuntu 3,摸索linux 各方面的知识,选择Gentoo 4,需要稳定性高的系统,选择FreeBSD 5, ...

  10. 以太坊联盟链 parity 节点搭建

    https://www.cnblogs.com/sumingk/articles/9097996.html 上一篇文章介绍了以太坊私有链 geth节点的搭建,本篇介绍下企业级应用 联盟链搭建,运用pa ...