Java 学习笔记(11)——多线程
Java内部提供了针对多线程的支持,线程是CPU执行的最小单位,在多核CPU中使用多线程,能够做到多个任务并行执行,提高效率。
使用多线程的方法
- 创建Thread类的子类,并重写run方法,在需要启动线程的时候调用类的start() 方法,每一个子类对象只能调用一次start()方法,如果需要启动多个线程执行同一个任务就需要创建多个线程对象
- 实现Runnable 接口并重写run 方法,传入这个接口的实现类构造一个Thread类,然后调用Thread类的start方法
- 实现Callable 接口并重写call方法,然后使用Future 来包装 Callable 对象,使用 Future 对象构造一个Thread对象,并调用Thread 类的start方法启动线程
平时在使用上第一个方式用的很少,一般根据情况使用第2中或者第3中,与第一种方式相比,它们具有的优势如下:
- 降低了程序的耦合性,它将设置线程任务和开启线程进行了分离
- 避免了单继承的局限性,一旦继承了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)——多线程的更多相关文章
- Java学习笔记11
package welcome; import java.util.Scanner; /* * 代数问题:求解2x2线性方程 */ public class ComputeLinearEquation ...
- Java学习笔记 11/15:一个简单的JAVA例子
首先来看一个简单的 Java 程序. 来看下面这个程序,试试看是否看得出它是在做哪些事情! 范例:TestJava.java // TestJava.java,java 的简单范例 public ...
- 【原】Java学习笔记032 - 多线程
package cn.temptation; public class Sample01 { public static void main(String[] args) { /* * [进程]:正在 ...
- Java学习笔记之——多线程
多线程编程 程序: 进程:一个程序运行就会产生一个进程 线程:进程的执行流程,一个进程至少有一个线程,称为主线程 如:QQ聊着天,同时在听音乐 一个进程可以有多个线程,多个线程共享同一个进程的资源 线 ...
- Java学习笔记:多线程(一)
Java中线程的五种状态: 新建状态(New) 就绪状态(Runnable) 运行状态(Running) 阻塞状态(Blocked) 凋亡状态(Dead) 其中阻塞状态(Blocked)又分为三种: ...
- java学习笔记(5)多线程
一.简介(过段时间再写,多线程难度有点大) --------------------------------------- 1.进程:运行时的概念,运行的应用程序 2.线程:应用程序内部并发执行的代码 ...
- Java 学习笔记(11)——异常处理
异常是程序中的一些错误,但并不是所有的错误都是异常,并且错误有时候是可以避免的. 比如说,你的代码少了一个分号,那么运行出来结果是提示是错误 java.lang.Error:如果你用System.ou ...
- java学习笔记(11) —— Struts2与Spring的整合
1.右键 项目名称 —— MyEclipse —— Add Spring Capabilities 2.选取 Copy checked Library contents to project fold ...
- Java学习笔记11(面向对象四:多态)
多态: 举例:描述一个事物的多种形态,如Student类继承了Person类,一个Student对象既是Student,又是Person 多态体现为:父类引用变量可以指向子类对象 多态的前提:必须有子 ...
随机推荐
- 封装好的PDO类
封装PDO类,方便使用: <?php header('content-type:text/html;charset=utf-8'); /** * 封装PDODB类 */ // 加载接口 // i ...
- hdu2149 巴什博奕
n表示先手胜 p表示先手负 1~N N+1 N+2~2*N+1 2*N+2 2*N+3~3*N+2 ... n p n p n ... m总归个数 每次取1~N个根据前面的规律得到,如果m/N==m% ...
- android 数据存储----android短信发送器之文件的读写(手机+SD卡)
本文实践知识点有有三: 1.布局文件,android布局有相对布局,线性布局,绝对布局,表格布局,标签布局等.各个布局能够嵌套的.本文的布局文件就是线性布局的嵌套 <LinearLayout x ...
- mysql 计算两个时间之间有多少分钟
SELECT TIMESTAMPDIFF(MINUTE, (DATE_FORMAT('2015-08-12 10:38:00','%Y-%m-%d %H:%i')), (DATE_FORMAT('20 ...
- Data Flow-File Read-详细过程
- Libev源码分析08:Libev中的信号监视器
Libev中的信号监视器,用于监控信号的发生,因信号是异步的,所以Libev的处理方式是尽量的将异步信号同步化.异步信号的同步化方法主要有:signalfd.eventfd.pipe.sigwaiti ...
- 我为什么飞行 10000 公里去西班牙参加 KubeCon?
2019 年 5 月 20 日至 23 日, 由 Cloud Native Computing Foundation (CNCF) 主办的云原生技术大会 KubeCon + CloudNativeCo ...
- 关于python 中的__future__模块
Python的每个新版本都会增加一些新的功能,或者对原来的功能作一些改动.有些改动是不兼容旧版本的,也就是在当前版本运行正常的代码,到下一个版本运行就可能不正常了. 具体说来就是,某个版本中出现了某个 ...
- mysql format时间格式化说明
原文地址为:mysql format时间格式化说明 date_format(datetime,formatting)可以格式日期和时间(例如 YYYY-MM-DD HH:MM:SS)和(HH:MM:S ...
- sleep usleep nanosleep alarm setitimer使用
sleep使用的是alarm之类的定时器,定时器是使得进程被挂起,使进程处于就绪的状态. signal+alarm定时器 alarm参数的类型为uint, 并且不能填0 #include <st ...