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. 从零学React Native之07View

    View 组件是React Native最基本的组件.绝大部分其他React Native 组件. View组件的颜色和边框 backgroundColor 键用来指定颜色. RN 0.19版本开始, ...

  2. python 空值(NoneType)

  3. 安装pip3遇到:E: Unmet dependencies. Try 'apt-get -f install' with no packages (or specify a solution).

    安装pip3遇到:E: Unmet dependencies. Try 'apt-get -f install' with no packages (or specify a solution). 具 ...

  4. zip解决杨辉三角问题

    杨辉三角原型: / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ 实现: a = [1] while True: print(a) ...

  5. VSCode配置启动Vue项目

    下载安装并配置VSCode 随便百度上搜个最新的VSCode安装好后,点击Ctrl + Shit + X打开插件扩展窗口进行插件扩展,这里要安装两个插件. 1.vetur插件的安装 该插件是vue文件 ...

  6. 最适合 Python 入门的资源有哪些?

    https://blog.csdn.net/zV3e189oS5c0tSknrBCL/article/details/81230593 学习任何一门编程语言或者技能基本上都遵循3个步骤,第一步是看,第 ...

  7. es6新增语法之`${}`

    这是es6中新增的字符串方法 可以配合反单引号完成拼接字符串的功能 1.反单引号怎么打出来?将输入法调整为英文输入法,单击键盘上数字键1左边的按键. 2.用法step1: 定义需要拼接进去的字符串变量 ...

  8. 前端开发之BOM和DOM(转载)

    BOM BOM:是指浏览器对象模型,它使JavaScript可以和浏览器进行交互. 1,navigator对象:浏览器对象,通过这个对象可以判定用户所使用的浏览器,包含了浏览器相关信息. naviga ...

  9. [转]Android Studio实现代码混淆

     1,在build.grandle添加,其中规则写在proguard-rules.pro中,也可以自定义一个文件,将其代替,比如eclipse常用的 proguard-project.txt: bui ...

  10. 2012-2013 Northwestern European Regional Contest (NWERC 2012)

    B - Beer Pressure \(dp(t, p_1, p_2, p_3, p_4)\)表示总人数为\(t\),\(p_i\)对应酒吧投票人数的概率. 使用滚动数组优化掉一维空间. 总的时间复杂 ...