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. 集合--List&&ArrayList-LinkedList

    1.8新特性  List接口中的replaceAll()方法,替换指定的元素,函数式接口编程 List  元素是有序的并且可以重复 四种add();方法 ArrayList(用于查询操作),底层是数组 ...

  2. 洛谷P2522 [HAOI2011]Problem b (莫比乌斯反演+容斥)

    题意:求$\sum_{i=a}^{b}\sum_{j=c}^{d}[gcd(i,j)==k]$(1<=a,b,c,d,k<=50000). 是洛谷P3455 [POI2007]ZAP-Qu ...

  3. 2018-8-9-win10-uwp-装机必备应用-含源代码

    title author date CreateTime categories win10 uwp 装机必备应用 含源代码 lindexi 2018-8-9 9:7:31 +0800 2018-8-9 ...

  4. Cmakelists.txt中配置glew

    在cmakelists.txt中添加: add_library(glew_static STATIC IMPORTED) set_target_properties(glew_static PROPE ...

  5. Ubuntu matplotlib显示中文乱码的解决方法

    https://blog.csdn.net/huuinn/article/details/78968966

  6. python字符串、元组常用操作

    常用字符串操作函数: #Author:CGQ name="I \tam ChenGuoQiang" print(name.capitalize())#首字母大写,其他都小写 pri ...

  7. POLARDB 2.0 重磅升级,分别支持Oracle与PostgreSQL

    点击订阅新品发布会! 新产品.新版本.新技术.新功能.价格调整,评论在下方,下期更新!关注更多内容,了解更多 最新发布 POLARDB 2.0 重磅升级 2019年6月19日15时,阿里云 POLAR ...

  8. 14.libgdx的一些坑记录(持续更新)

    1. internal的文件路径 无法用list获取目录下文件     2.动态打包散图无法放入资源管理器,只能在资源加载器打包后的散图再合成打包,但都不如提前打包 3.资源加载器读入以texture ...

  9. java+内存分配及变量存储位置的区别

    Java内存分配与管理是Java的核心技术之一,之前我们曾介绍过Java的内存管理与内存泄露以及Java垃圾回收方面的知识,今天我们再次深入Java核心,详细介绍一下Java在内存分配方面的知识.一般 ...

  10. iptables 详细使用

    检查状态 先检查是否安装了iptables $ service iptables status 安装iptables $ yum install iptables 升级iptables $ yum u ...