Java内部提供了针对多线程的支持,线程是CPU执行的最小单位,在多核CPU中使用多线程,能够做到多个任务并行执行,提高效率。

使用多线程的方法

  1. 创建Thread类的子类,并重写run方法,在需要启动线程的时候调用类的start() 方法,每一个子类对象只能调用一次start()方法,如果需要启动多个线程执行同一个任务就需要创建多个线程对象
  2. 实现Runnable 接口并重写run 方法,传入这个接口的实现类构造一个Thread类,然后调用Thread类的start方法
  3. 实现Callable 接口并重写call方法,然后使用Future 来包装 Callable 对象,使用 Future 对象构造一个Thread对象,并调用Thread 类的start方法启动线程

平时在使用上第一个方式用的很少,一般根据情况使用第2中或者第3中,与第一种方式相比,它们具有的优势如下:

  1. 降低了程序的耦合性,它将设置线程任务和开启线程进行了分离
  2. 避免了单继承的局限性,一旦继承了Thread 类,那么他就不能继承其他类。使用重写接口的方式可以再继承一个别的类

第3中方式相比第二种方式来说,它提供了一个获取线程返回值的方式。我们在call函数中返回值,通过 FutureTask 对象的get方法来获取返回值

public class ThreadTask extends Thread{
@Override
public void run(){
System.out.println("当前线程:" + getName() + "正在运行");
}
} public class ThreadDemo{
public static void main(String[] args){
new ThreadTask().start();
new ThreadTask().start();
}
}
public class ThreadDemo{
public static void main(String[] args){
//使用匿名内部类的方式
Runnable thread1 = new Runnable(){
@Override
public void run(){
System.out.println("当前线程:" + Thread.currentThread().getName() + "正在运行");
}
} new Thread(thread1).start();
new Thread(thread1).start();
}
}
public class ThreadDemo implements Callable<Integer>{
public static void main(String[] args){
FutureTask<Integer> ft = new new FutureTask<>(new ThreadDemo());
new Thread(ft).start();
System.out.println("线程返回值:" + ft.get());
} @Override
public Integer call() throws Exception
{
System.out.println("当前线程:" + Thread.currentThread().getName() + "正在运行");
return 1;
}
}

thread 状态

在操作系统原理中讲到,线程有这么几种状态:新建、运行、阻塞、结束。而Java中将线程状态进行了进一步的细分,根据阻塞原因将阻塞状态又分为:等待(调用等待函数主动让出CPU执行权), 阻塞(线程的时间片到达,操作系统进行线程切换)

它们之间的状态如下:

等待唤醒

入上图所示,可以使用wait/sleep方法让线程处于等待状态。在另一个线程中使用wait线程对象的notify方法可以唤醒wait线程。

wait/notify 方法定义于 Object 方法,也就是说所以的对象都可以有wait/notify 方法。

void wait()  ;调用该函数,使线程无限等待,直到有另外的线程将其唤醒
void wait(long timeout);调用该函数,使线程进行等待,直到另外有线程将其唤醒或者等待时间已过
void notify(); 唤醒正在等待对象监视器的单个线程
void notifyAll(); 唤醒正在等待对象监视器的所有线程。

上面说过这些方法都是在 Object 类中实现的,也就是说所有的对象都可以调用。上面的等待监视器就是随意一个调用了wait 的对象。这个对象会阻塞它所在的线程。

线程同步

我们知道在访问多个线程共享的资源时可能会发生数据的安全性问题。因此这个时候需要做线程的同步

Java中同步的方法有: 同步代码块、同步方法和Lock锁的机制

同步代码块

同步代码块是使用synchronized来修饰需要进行同步的代码块

同步代码块需要提供一个锁对象,当一个线程执行到这个代码块时,该线程获得锁对象。当另外的线程也执行到同一个锁对象的同步代码块时,由于无法获取到锁对象因此会陷入等待。直到获得锁对象的线程执行完同步代码块,并释放锁。这里获取、释放锁由Java虚拟机自己完成。

例如

public static void synchronizedCode(){
Runnable thread = new Runnable(){
private int ticket = 100;
@Override
public void run(){
synchronized (this){
while(ticket > 0){
//这里休眠10s,用来表示提交订单后的付款等操作
try{
Thread.sleep(10);
}catch(Exception e){
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "正在卖票:" + ticket);
ticket--;
} System.out.println(Thread.currentThread().getName() + "票已售罄");
}
}
}; new Thread(thread).start();
new Thread(thread).start();
new Thread(thread).start();
}

同步方法

同步方法是使用 synchronized 关键字修饰的方法。它与同步代码块的原理相同,保证了多个线程只有一个处于同步代码块中。同步方法中也有一个锁对象,这个锁对象是this这个对象,静态方法的锁对象是本类的class文件对象。

public static void synchronizedMethod(){
Runnable thread = new Runnable(){
private int ticket = 100;
@Override
public void run(){
payTicket();
} public synchronized void payTicket(){
while(ticket > 0){
//这里休眠10s,用来表示提交订单后的付款等操作
try{
Thread.sleep(10);
}catch(Exception e){
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "正在卖票:" + ticket);
ticket--;
} System.out.println(Thread.currentThread().getName() + "票已售罄");
}
}; new Thread(thread).start();
new Thread(thread).start();
new Thread(thread).start();
}

Lock 锁机制

除了上述方法,可以使用lock锁来进行同步,在执行代码前先调用 lock方法获得锁,执行完成之后使用unlock 来释放锁。

例如下列的例子

public static void lockMethod(){
Runnable thread = new Runnable(){
private int ticket = 100;
private Lock lock = new ReentrantLock();
@Override
public void run(){
try{
lock.lock();
while(ticket > 0){
//这里休眠10s,用来表示提交订单后的付款等操作
Thread.sleep(10);
System.out.println(Thread.currentThread().getName() + "正在卖票:" + ticket);
ticket--;
}
}catch(Exception e){
e.printStackTrace();
}finally{
lock.unlock();
} System.out.println(Thread.currentThread().getName() + "票已售罄");
}
}; new Thread(thread).start();
new Thread(thread).start();
new Thread(thread).start();
}

Java 学习笔记(11)——多线程的更多相关文章

  1. Java学习笔记11

    package welcome; import java.util.Scanner; /* * 代数问题:求解2x2线性方程 */ public class ComputeLinearEquation ...

  2. Java学习笔记 11/15:一个简单的JAVA例子

    首先来看一个简单的 Java 程序. 来看下面这个程序,试试看是否看得出它是在做哪些事情! 范例:TestJava.java   // TestJava.java,java 的简单范例  public ...

  3. 【原】Java学习笔记032 - 多线程

    package cn.temptation; public class Sample01 { public static void main(String[] args) { /* * [进程]:正在 ...

  4. Java学习笔记之——多线程

    多线程编程 程序: 进程:一个程序运行就会产生一个进程 线程:进程的执行流程,一个进程至少有一个线程,称为主线程 如:QQ聊着天,同时在听音乐 一个进程可以有多个线程,多个线程共享同一个进程的资源 线 ...

  5. Java学习笔记:多线程(一)

    Java中线程的五种状态: 新建状态(New) 就绪状态(Runnable) 运行状态(Running) 阻塞状态(Blocked) 凋亡状态(Dead) 其中阻塞状态(Blocked)又分为三种: ...

  6. java学习笔记(5)多线程

    一.简介(过段时间再写,多线程难度有点大) --------------------------------------- 1.进程:运行时的概念,运行的应用程序 2.线程:应用程序内部并发执行的代码 ...

  7. Java 学习笔记(11)——异常处理

    异常是程序中的一些错误,但并不是所有的错误都是异常,并且错误有时候是可以避免的. 比如说,你的代码少了一个分号,那么运行出来结果是提示是错误 java.lang.Error:如果你用System.ou ...

  8. java学习笔记(11) —— Struts2与Spring的整合

    1.右键 项目名称 —— MyEclipse —— Add Spring Capabilities 2.选取 Copy checked Library contents to project fold ...

  9. Java学习笔记11(面向对象四:多态)

    多态: 举例:描述一个事物的多种形态,如Student类继承了Person类,一个Student对象既是Student,又是Person 多态体现为:父类引用变量可以指向子类对象 多态的前提:必须有子 ...

随机推荐

  1. iPhone 7 Plus 维修记 (一)(2019-08-07)

    iPhone 7 Plus 维修记 问题 电池鼓包,屏幕已经被撑起,偶尔死机突然关机. 分析 初步分析是电池损坏. 维修 由于电池没有双易拉条需要将后壳加热后再取出电池. 更换电池后测试,发现电量一会 ...

  2. Linus 本尊也来了!为什么 KubeCon 越来越火了?

    2015年11月,第一届 KubeCon 在美国旧金山开始的时候,还只是个200人的小会议,2019年的7月,KubeCon 第二次在中国举办,就有 3500 多位云原生和开源领域工程师齐聚一堂. 连 ...

  3. Quick BI独创千人千面的行级权限管控机制

    摘要 就数据访问权限而言,阿里巴巴以“被动式授权”为主,你需要什么权限就申请什么权限.但是,在客户交流过程中,我们发现绝大多数企业都是集中式授权,尤其是面向个人的行级权限管控,管理复杂度往往呈几何增长 ...

  4. 在JS中模拟表单的post提交,进行页面的跳转

    原文链接:https://blog.csdn.net/jal517486222/article/details/83147761 /* *功能: 模拟form表单的提交 *参数: URL 跳转地址 P ...

  5. python == 符号

  6. @loj - 2174@ 「FJOI2016」神秘数

    目录 @description@ @solution@ @accepted code@ @details@ @description@ 一个可重复数字集合 S 的神秘数定义为最小的不能被 S 的子集的 ...

  7. css浮动规则

    1.浮动元素会从文档正常流中删除,但它仍会影响布局 2.浮动非替换元素必须为其指定width,否则元素的width会趋于0而导致浮动元素不能完整显示3.一旦元素具有了浮动属性,它便成为了一个块级元素. ...

  8. linux之docker

    简介 Docker是一个开源的应用容器引擎,基于go语言,遵循从Apache2.0协议开源 Docker可以让开发者打包他们的应用以及依赖包到一个轻量级,可移植的容器中,然后发布到任何流行的linux ...

  9. H3C 代理ARP

  10. oracle多个平等的索引

    当SQL语句的执行路径可以使用分布在多个表上的多个索引时, ORACLE会同时使用多个索引并在运行时对它们的记录进行合并, 检索出仅对全部索引有效的记录. 在ORACLE选择执行路径时,唯一性索引的等 ...