Java多线程、线程池和线程安全整理
多线程
1.1 多线程介绍
进程指正在运行的程序。确切的来说,当一个程序进入内存运行,即变成一个进程,进程是处于运行过程中的程序,并且具有一定独立功能。
1.2 Thread类
通过API中搜索,查到Thread类。通过阅读Thread类中的描述。Thread是程序中的执行线程。Java 虚拟机允许应用程序并发地运行多个执行线程。
l 构造方法
l 常用方法
发现创建新执行线程有两种方法。
一种方法是将类声明为 Thread 的子类。该子类应重写 Thread 类的 run 方法。创建对象,开启线程。run方法相当于其他线程的main方法。
另一种方法是声明一个实现 Runnable 接口的类。该类然后实现 run 方法。然后创建Runnable的子类对象,传入到某个线程的构造方法中,开启线程。
创建线程的步骤:
1 定义一个类继承Thread。
2 重写run方法。
3 创建子类对象,就是创建线程对象。
4 调用start方法,开启线程并让线程执行,同时还会告诉jvm去调用run方法。
package com.oracle.xiancheng;
public class Demo01 extends Thread {
public static void main(String[] args) {
//创建线程
MyThread mt=new MyThread();
//创建线程
mt.start();
//获取正在执行的对象名称 调用 getname
for(int i=0;i<100;++i){
System.out.println("main-------"+i);
}
}
}
自定义线程类
package com.oracle.Runnable;
public class MyRunnable implements Runnable{
@Override
public void run() {
for(int i=0;i<50;i++){
System.out.println("run-----"+i);
}
}
}
1.2.1 实现Runnable的原理
实现Runnable接口,避免了继承Thread类的单继承局限性。覆盖Runnable接口中的run方法,将线程任务代码定义到run方法中。
创建Thread类的对象,只有创建Thread类的对象才可以创建线程。线程任务已被封装到Runnable接口的run方法中,而这个run方法所属于Runnable接口的子类对象,所以将这个子类对象作为参数传递给Thread的构造函数,这样,线程对象创建时就可以明确要运行的线程的任务。
1.2.2 实现Runnable的好处
第二种方式实现Runnable接口避免了单继承的局限性,所以较为常用。实现Runnable接口的方式,更加的符合面向对象,线程分为两部分,一部分线程对象,一部分线程任务。继承Thread类,线程对象和线程任务耦合在一起。一旦创建Thread类的子类对象,既是线程对象,有又有线程任务。实现runnable接口,将线程任务单独分离出来封装成对象,类型就是Runnable接口类型。Runnable接口对线程对象和线程任务进行解耦。
1.3 线程的匿名内部类使用
package com.oracle.Runnable;
public class MyRunnable implements Runnable{
@Override
public void run() {
for(int i=0;i<50;i++){
System.out.println("run-----"+i);
}
}
}
package com.oracle.Runnable;
public class Demo02 {
public static void main(String[] args) {
//创建线程子类对象
//匿名内部类对象
//创建线程对象时,直接重写Thread类中的run方法
Thread th=new Thread(){
public void run() {
System.out.println(Thread.currentThread().getName()+"run");
};
};
//开启线程
th.start();
//使用匿名内部类的方式实现Runnable接口,重新Runnable接口中的run方法
/*Runnable r=new Runnable(){
public void run() {
System.out.println(Thread.currentThread().getName()+"run");
};
};
//创建线程任务对象
Thread th=new Thread(r);
//开启线程
th.start();*/
}
}
运行结果:
线程池
2.1 线程池概念
线程池,其实就是一个容纳多个线程的容器,其中的线程可以反复使用,省去了频繁创建线程对象的操作,无需反复创建线程而消耗过多资源
2.2 使用线程池方式--Runnable接口
l Executors:线程池创建工厂类
public static ExecutorService newFixedThreadPool(int nThreads):返回线程池对象
l ExecutorService:线程池类
Future<?> submit(Runnable task):获取线程池中的某一个线程对象,并执行
l Future接口:用来记录线程任务执行完毕后产生的结果。线程池创建与使用
l 使用线程池中线程对象的步骤:
创建线程池对象
创建Runnable接口子类对象
提交Runnable接口子类对象
关闭线程池
package com.oracle.Runnable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Demo03 {
public static void main(String[] args) {
// Executors 线程池工厂类
// ExecutorService 线程池工厂类
// 获取线程池对象
ExecutorService es = Executors.newFixedThreadPool(2);
// 创建线程任务对象
MyRunnable mr = new MyRunnable();
// 执行线程任务
es.submit(mr);
es.submit(mr);
es.submit(mr);
//释放资源
es.shutdown();
}
}
package com.oracle.Runnable;
public class MyRunnable implements Runnable{
@Override
public void run() {
for(int i=0;i<50;i++){
System.out.println("run-----"+i);
}
}
}
运行结果:
等等
2.3 使用线程池方式—Callable接口
Callable接口:与Runnable接口功能相似,用来指定线程的任务。其中的call()方法,用来返回线程任务执行完毕后的结果,call方法可抛出异常。
ExecutorService:线程池类
<T> Future<T> submit(Callable<T> task):获取线程池中的某一个线程对象,并执行线程中的call()方法
Future接口:用来记录线程任务执行完毕后产生的结果。线程池创建与使用
使用线程池中线程对象的步骤:
创建线程池对象
创建Callable接口子类对象
提交Callable接口子类对象
关闭线程池
package com.oracle.Runnable;
import java.util.concurrent.Callable;
public class MyCallable implements Callable<String> {
@Override
public String call() throws Exception {
return "abc";
}
}
package com.oracle.Runnable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorCompletionService;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class Demo04 {
public static void main(String[] args) throws InterruptedException, ExecutionException {
//创建线程任务
MyCallable mc=new MyCallable();
//获取线程池工厂
ExecutorService es=Executors.newFixedThreadPool(2);
Future<String> f=es.submit(mc);
//创建返回值
String str=f.get();
System.out.println(str);
}
}
运行结果:
2.4 线程池练习:返回两个数相加的结果和乘积的结果
和
package com.oracle.Demo01;
import java.util.concurrent.Callable;
public class MyCallables implements Callable<Integer> {
private int num1;
private int num2;
public MyCallables(int num1,int num2) {
this.num1=num1;
this.num2=num2;
}
@Override
public Integer call() throws Exception {
return num1+num2;
}
}
package com.oracle.Demo01;
import java.math.BigInteger;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class Test1 {
public static void main(String[] args) throws InterruptedException, ExecutionException {
//和
MyCallables mc1=new MyCallables(100,150);
MyCallables mc2=new MyCallables(10,15);
ExecutorService es=Executors.newFixedThreadPool(2);
Future<Integer> num1=es.submit(mc1);
Future<Integer> num2=es.submit(mc2);
int s1=num1.get();
int s2=num2.get();
System.out.println(s1);
System.out.println(s2);
es.shutdown();
}
}
运行结果:
积:
package com.oracle.Demo01;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.concurrent.Callable;
public class MyCallablesr implements Callable<String > {
private int num;
public MyCallablesr(int num) {
this.num=num;
}
@Override
public String call() throws Exception {
String base="1";//超long的范围
for(int i=1;i<=num;i++){
//用BigDecimal转换
BigDecimal stra=new BigDecimal(base);
BigDecimal end=new BigDecimal(i);
BigDecimal re=end.multiply(stra);
base=re.toString();
}
return base ;
}
}
package com.oracle.Demo01;
import java.math.BigInteger;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class Test1 {
public static void main(String[] args) throws InterruptedException, ExecutionException {
//积
MyCallablesr ms1=new MyCallablesr(100);
MyCallablesr ms2=new MyCallablesr(200);
ExecutorService es=Executors.newFixedThreadPool(2);
Future<String> base1=es.submit(ms1);
Future<String> base2=es.submit(ms2);
String s1=base1.get();
String s2=base2.get();
System.out.println(s1);
System.out.println(s2);
es.shutdown();
}
}
运行结果:
多线程
3.1 线程安全
如果有多个线程在同时运行,而这些线程可能会同时运行这段代码。程序每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。
l 我们通过一个案例,演示线程的安全问题:
电影院要卖票,我们模拟电影院的卖票过程。假设要播放的电影是 “功夫熊猫3”,本次电影的座位共100个(本场电影只能卖100张票)。
我们来模拟电影院的售票窗口,实现多个窗口同时卖 “功夫熊猫3”这场电影票(多个窗口一起卖这100张票)
需要窗口,采用线程对象来模拟;需要票,Runnable接口子类来模拟
3.2 线程同步(线程安全处理Synchronized)
java中提供了线程同步机制,它能够解决上述的线程安全问题。
线程同步的方式有两种:
方式1:同步代码块
方式2:同步方法
3.2.1 同步代码块
同步代码块: 在代码块声明上 加上synchronized
synchronized (锁对象) {
可能会产生线程安全问题的代码
}
同步代码块中的锁对象可以是任意的对象;但多个线程时,要使用同一个锁对象才能够保证线程安全。
模拟售票:
package com.oracle.xianchengchi;
public class MyRunnable implements Runnable {
// 卖电影票
private int ticket = 100;
private Object obj = new Object();
@Override
public void run() {
while (true) {
synchronized (obj) {
if (ticket > 0) {
try {
Thread.sleep(50);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "出售第" + ticket-- + "张票");
}
}
}
}
}
测试:
package com.oracle.xianchengchi;
public class Test01 {
public static void main(String[] args) {
//明确线程任务
MyRunnable mr=new MyRunnable();
Thread t0=new Thread(mr);
Thread t1=new Thread(mr);
Thread t2=new Thread(mr);
//开启线程
t0.start();
t1.start();
t2.start();
}
}
运行结果:
3.2.2 同步方法
l 同步方法:在方法声明上加上synchronized
public synchronized void method(){
可能会产生线程安全问题的代码
}
同步方法中的锁对象是 this
使用同步方法,对电影院卖票案例中Ticket类进行如下代码修改:
package com.oracle.xianchengchi;
public class MyRunnables implements Runnable {
// 卖电影票
private int ticket = 100;
private Object obj = new Object();
@Override
public void run() {
while (true) {
sale();
}
}
public synchronized void sale() {
if (ticket > 0) {
try {
Thread.sleep(50);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "出售第" + ticket-- + "张票");
}
}
}
package com.oracle.xianchengchi;
public class Test02 {
public static void main(String[] args) {
//明确线程任务
MyRunnables mr=new MyRunnables();
Thread t0=new Thread(mr);
Thread t1=new Thread(mr);
Thread t2=new Thread(mr);
//开启线程
t0.start();
t1.start();
t2.start();
}
}
运行结果:
3.3 Lock接口
查阅API,查阅Lock接口描述,Lock
实现提供了比使用 synchronized
方法和语句可获得的更广泛的锁定操作。
l Lock接口中的常用方法
Lock提供了一个更加面对对象的锁,在该锁中提供了更多的操作锁的功能。
我们使用Lock接口,以及其中的lock()方法和unlock()方法替代同步,对电影院卖票案例中Ticket类进行如下代码修改:
package com.oracle.xianchengchi;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class MyRunnable2 implements Runnable {
// 卖电影票
private int ticket = 100;
private Lock lock=new ReentrantLock();
@Override
public void run() {
while (true) {
lock.lock();
if (ticket > 0) {
try {
Thread.sleep(50);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "出售第" + ticket-- + "张票");
}
lock.unlock();
}
}
Java多线程、线程池和线程安全整理的更多相关文章
- java多线程详解(7)-线程池的使用
在前面的文章中,我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题: 如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了, 这样频繁创建线程就会大大降低系 ...
- java多线程总结五:线程池的原理及实现
1.线程池简介: 多线程技术主要解决处理器单元内多个线程执行的问题,它可以显著减少处理器单元的闲置时间,增加处理器单元的吞吐能力. 假设一个服务器完成一项任务所需时间为:T1 创 ...
- java多线程(四)-自定义线程池
当我们使用 线程池的时候,可以使用 newCachedThreadPool()或者 newFixedThreadPool(int)等方法,其实我们深入到这些方法里面,就可以看到它们的是实现方式是这样的 ...
- -1-5 java 多线程 概念 进程 线程区别联系 java创建线程方式 线程组 线程池概念 线程安全 同步 同步代码块 Lock锁 sleep()和wait()方法的区别 为什么wait(),notify(),notifyAll()等方法都定义在Object类中
本文关键词: java 多线程 概念 进程 线程区别联系 java创建线程方式 线程组 线程池概念 线程安全 同步 同步代码块 Lock锁 sleep()和wait()方法的区别 为什么wait( ...
- java多线程系列六、线程池
一. 线程池简介 1. 线程池的概念: 线程池就是首先创建一些线程,它们的集合称为线程池. 2. 使用线程池的好处 a) 降低资源的消耗.使用线程池不用频繁的创建线程和销毁线程 b) 提高响应速度,任 ...
- Java多线程面试题:线程锁+线程池+线程同步等
1.并发编程三要素? 1)原子性 原子性指的是一个或者多个操作,要么全部执行并且在执行的过程中不被其他操作打断,要么就全部都不执行. 2)可见性 可见性指多个线程操作一个共享变量时,其中一个线程对变量 ...
- (Java多线程系列九)线程池
线程池 1.什么是线程池 线程池是指在初始化一个多线程应用程序过程中创建一个线程集合,然后在需要执行新的任务时重用这些线程而不是新建一个线程.线程池中线程的数量通常取决于可用内存数量和应用程序的需求. ...
- Java多线程并发04——合理使用线程池
在此之前,我们已经了解了关于线程的基本知识,今天将为各位带来,线程池这一技术.关注我的公众号「Java面典」了解更多 Java 相关知识点. 为什么使用线程池?线程池做的工作主要是控制运行的线程的数量 ...
- Java多线程系列 JUC线程池03 线程池原理解析(二)
转载 http://www.cnblogs.com/skywang12345/p/3509954.html http://www.cnblogs.com/skywang12345/p/351294 ...
随机推荐
- ERROR 1071 (42000): Specified key was too long; max key length is 767 bytes
今天在MySQL 5.6版本的数据库中修改InnoDB表字段长度时遇到了"ERROR 1071 (42000): Specified key was too long; max key le ...
- [转]QQ空间、新浪微博、腾讯微博等一键分享API链接代码
转自------ 1.新浪微博:http://service.weibo.com/share/share.php?url= count=表示是否显示当前页面被分享数量(1显示)(可选,允许为空)&am ...
- TinScrapy-简化的Scrapy原码-查看爬虫的执行流程
学习了自定义的TinyScrapy框架,整理出以下定注释的代码 from twisted.web.client import getPage,defer from twisted.internet i ...
- mybatis使用oracle的sequence
oracle数据库创建SEQUENCE CREATE SEQUENCE SEQ_COM_MASTER START INCREMENT MINVALUE MAXVALUE NOCYCLE CACHE ; ...
- [RHEL 6]GPT分区--parted
对于2T以上的硬盘,划分分区表需要GPT分区,RHEL 6中使用parted进行分区 用法:parted [选项]... [设备 [命令 [参数]...]...] 将带有“参数”的命令应用于“设备”. ...
- bibli直播弹幕实时爬取
1 分析数据来源 在不知道弹幕信息在哪里的时候,只能去all里面查看每一个相应的信息,看信息是否含有弹幕信息 在知道弹幕信息文件的时候,我们可以直接用全局文件搜索,定位到弹幕数据文件.操作如下图 2 ...
- supervisor管理nginx
command = /usr/local/bin/nginx 这个命令默认是后台启动,但是supervisor不能监控后台程序,所以supervisor就一直执行这个命令. 加上-g 'daemon ...
- LInkedHashMap实现最近被使用(LRU)缓存
在最近的面试中,我曾被多次问到,怎么实现一个最近最少使用(LRU)的缓存.缓存可以通过哈希表来实现,然而为这个缓存增加大小限制会变成另一个有意思的问题.现在我们看一下怎么实现. 最近最少使用缓存的回收 ...
- Golang 入门系列(三)Go语言基础知识汇总
前面已经了 Go 环境的配置和初学Go时,容易遇到的坑,大家可以请查看前面的文章 https://www.cnblogs.com/zhangweizhong/category/1275863.html ...
- elementUi源码解析(1)--项目结构篇
因为在忙其他事情好久没有更新iview的源码,也是因为后面的一些组件有点复杂在考虑用什么方式把复杂的功能逻辑简单的展示出来,还没想到方法,突然想到element的组件基本也差不多,内部功能的逻辑也差不 ...