22.多线程.md
1.线程的创建和启动
1.1继承Thread类创建线程
package com.liyue.studycode.thread;
public class FirstThread extends Thread {
private int sum = 0;
public void run(){
for(; sum<100; sum++){
System.out.println(this.getName() + "" + sum);
}
}
}
package com.liyue.studycode.thread;
public class ThreadPrint {
public static void main(String[] args) {
// TODO Auto-generated method stub
System.out.println("主线程: " + Thread.currentThread().getName());
for(int i=0; i<100; i++){
System.out.println(Thread.currentThread().getName() + " " + i);
if(i == 90){
//definition first thread
new FirstThread().start();
//definition second thread named H
Thread t = new FirstThread();
t.setName("H");
t.start();
}
}
}
}
1.2继承Runnable接口实现创建线程
package com.liyue.studycode.thread;
public class SecondThread implements Runnable {
private int sum = 0;
@Override
public void run() {
for(; sum<10; sum++){
//can not use 'this.getName()'
System.out.println(Thread.currentThread().getName() + "" + sum);
}
}
}
package com.liyue.studycode.thread;
public class ThreadPrint {
public static void main(String[] args) {
// TODO Auto-generated method stub
System.out.println("主线程: " + Thread.currentThread().getName());
for(int i=0; i<20; i++){
System.out.println(Thread.currentThread().getName() + " " + i);
if(i == 10){
//definition class SecondThread
SecondThread t = new SecondThread();
//
new Thread(t, "one").start();
}
}
}
}
1.3使用Callable和Future创建
package com.liyue.studycode.thread;
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
public class ThreadPrint {
public static void main(String[] args) {
// TODO Auto-generated method stub
System.out.println("主线程: " + Thread.currentThread().getName());
/*pack all object to thread*/
//create a new object
ThreadPrint p = new ThreadPrint();
//use Lambda create Callable<String>
//use FutureTask<String> pack Callable<String>
FutureTask<String> f = new FutureTask<String>((Callable<String>)()->{
int sum = 0;
for(; sum<20; sum++){
System.out.println(Thread.currentThread().getName() + "" + sum);
}
//
System.out.println("子线程执行完成");
return sum+"";
});
for(int i=0; i<30; i++){
System.out.println("主线程: " + Thread.currentThread().getName() + " " + i);
if(i == 10){
new Thread(f, "有返回值的线程").start();
}
}
//
try {
System.out.println("子线程的返回值:" + f.get());
} catch (Exception e) {
e.printStackTrace();
}
}
}
下面是一个拆分的例子:
package per.liyue.code.theradtest;
import java.util.concurrent.Callable;
public class CallableClass implements Callable<Integer>{
private int age;
public CallableClass(Integer age) {
// TODO Auto-generated constructor stub
this.age = age;
}
@Override
public Integer call() throws Exception {
// TODO Auto-generated method stub
age += age;
return this.age;
}
}
//...
public static void main(String[] args) {
// TODO Auto-generated method stub
//Thread
//ThreadClass t = new ThreadClass();
//t.start();
//Runnable
//RunnableClass r = new RunnableClass();
//new Thread(r).start();
//FutureTask
//先创建一个Callable对象
CallableClass c = new CallableClass(15);
//再创建一个FutureTask来接收Callable对象
FutureTask<Integer> f = new FutureTask<>(c);
//执行函数,本质还是Callable对象来执行
new Thread(f, "有返回值的线程").start();
//获取返回值
try {
System.out.println("Callable的返回值:" + f.get());
} catch (Exception e) {
// TODO: handle exception
}
}
2.控制线程
2.1 Thread类的join方法
使用当线程类调用join方法时候,此线程优先执行,正在执行的线程暂停等待此线程执行完成后继续:
package per.liyue.code.theradtest;
public class ThreadClass extends Thread {
@Override
public void run() {
// TODO Auto-generated method stub
for (int i = 0; i < 10; i++) {
System.out.println("ThreadClass Number 1 to 10:" + i);
}
}
}
package per.liyue.code.theradtest;
import java.util.concurrent.FutureTask;
public class MainPrint {
public static void main(String[] args) throws InterruptedException {
// TODO Auto-generated method stub
for (int i = 0; i < 5; i++) {
System.out.println("main num:" + i);
if(i == 2){
ThreadClass t = new ThreadClass();
t.start();
//调用join后,主线程暂停执行。等待t执行完成后才能继续
t.join();
}
}
}
}
2.2后台线程
- 调用Thread的setDaemon方法,可以是得对应线程类成为后台线程
- 调用方法setDaemon,必须在调用start方法之前
- 后台线程和前台线程没有明显的区别,但是可以调用isDaemon来判断是否是后台线程
- 前台线程死亡后后台线程也会死亡
package per.liyue.code.theradtest;
public class DeamonThread extends Thread{
@Override
public void run() {
// TODO Auto-generated method stub
for (int i = 0; i < 100; i++) {
System.out.println("Deamon num:" + i);
}
}
}
package per.liyue.code.theradtest;
import java.util.concurrent.FutureTask;
public class MainPrint {
public static void main(String[] args) throws InterruptedException {
// TODO Auto-generated method stub
DeamonThread d = new DeamonThread();
d.setDaemon(true);
d.start();
//main主线程执行完成后d就结束了,不会再继续
for (int i = 10; i > 0; i--) {
System.out.println("关闭倒计时:" + i);
}
}
}
2.3线程的睡眠和让步
- 线程的睡眠通过Thread的sleep方法实现;
- 线程的让步通过Thread的yield方法实现,不过现在不推荐使用
3.线程同步
Demo:售票,有一个票仓库。两个线程同时卖票。
package per.liyue.code.threadtest;
import javax.xml.crypto.dsig.keyinfo.RetrievalMethod;
public class TicketDb {
//总的票数
private Integer totalTicketNum;
//初始化总的票数
public TicketDb(Integer totalNum){
totalTicketNum = totalNum;
}
private TicketDb(){
}
public Integer getTotalTicketNum() {
return totalTicketNum;
}
//希望卖的票数
public Integer setTotalTicketNum(Integer SellTicketNum) throws NullPointerException{
//有余票则售出
if(0 < (this.totalTicketNum-SellTicketNum)){
this.totalTicketNum -= SellTicketNum;
return SellTicketNum;
}
//卖出剩余票
else if((0 < (this.totalTicketNum-totalTicketNum)) &&
(0 < this.totalTicketNum)){
Integer returnNum = this.totalTicketNum;
this.totalTicketNum = 0;
return returnNum;
}
//否则返回没票了
else {
return 0;
}
}
}
package per.liyue.code.threadtest;
import java.util.Random;
public class SellTicketThread implements Runnable {
//票库对象
private TicketDb tickdb;
public void setTickdb(TicketDb tickdb) {
this.tickdb = tickdb;
}
//模拟要买的票数,假设每次限购最多10张票
public Integer RandomTicketNum(){
Random random = new Random();
return random.nextInt(10);
}
private int totalSell = 0;
@Override
public void run() throws NullPointerException{
// TODO Auto-generated method stub
while(true){
Integer sellNum = tickdb.setTotalTicketNum(RandomTicketNum());
if(0 != sellNum){
System.out.println("售票窗口:" + Thread.currentThread().getName() + "售出票:" + sellNum + "张");
totalSell += sellNum;
}
else{
System.out.println("售票窗口:" + Thread.currentThread().getName() + "没有余票了!!!" + "此窗口总工卖票:" + totalSell);
break;
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
break;
}
}//while
}
}
package per.liyue.code.threadtest;
public class MainPrint {
public static void main(String[] args) {
// TODO Auto-generated method stub
TicketDb totalDb = new TicketDb(100);
//创建两个线程还是售票
SellTicketThread s = new SellTicketThread();
s.setTickdb(totalDb);
new Thread(s, "售票点1").start();
new Thread(s, "售票点2").start();
}
}
输出代码:
售票窗口:售票点2售出票:2张
售票窗口:售票点1售出票:9张
售票窗口:售票点2售出票:9张
售票窗口:售票点1售出票:1张
售票窗口:售票点1售出票:7张
售票窗口:售票点2没有余票了!!!此窗口总工卖票:21
售票窗口:售票点1售出票:2张
售票窗口:售票点1售出票:1张
售票窗口:售票点1售出票:8张
售票窗口:售票点1售出票:1张
售票窗口:售票点1售出票:6张
售票窗口:售票点1售出票:4张
售票窗口:售票点1没有余票了!!!此窗口总工卖票:50
3.1同步代码块
同步代码块使用关键字synchronized来同步代码,格式为:
synchronized (Object) {
//code...
}
这里的Object可以是**任何对象*
3.2同步方法
3.3同步锁
4.线程通信
4.1传统的线程通信
传统的线程通信,也就是用synchronized关键字来执行,使用同步方法或者同步代码块的时候,可以通过Object的wait()、notifyAll()来控制。
4.2使用Condition控制
如果没有使用synchronized关键字,使用了Lock锁。那么这里就需要使用一个Condition变量关联这个Lock锁,来保证线程间的通信。
Demo:
使用两个线程来实现交替打印:12A34B....5152Z
package per.liyue.code.printinfo;
/*
* 数字打印线程类
*/
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
public class PrintNum implements Runnable{
//这里的锁要公用
private Lock lock;
public void setLock(Lock lock) {
this.lock = lock;
}
//字母线程的控制量
private Condition conditionLetter;
public void setConditionLetter(Condition conditionLetter) {
this.conditionLetter = conditionLetter;
}
//本线程的控制量
private Condition conditionNum;
public void setConditionNum(Condition conditionNum) {
this.conditionNum = conditionNum;
}
private final int maxNum = 52;
private int sum = 0;
private int count = 1;
public void PirntNumByCondition() throws InterruptedException {
//加锁保证同步有效
lock.lock();
//开始打印
while(count <= maxNum){
if(2 == sum){
sum = 0;
//先启动对方,否则当前线程停止就不能启动对方了
conditionLetter.signal();
//对方启动后, 本线程等待
conditionNum.await();
}
else{
//正常打印
System.out.print(count);
count++;
sum++;
}
}
//数字先执行完,字母还得再执行一次
conditionLetter.signal();
//这里一定不要忘记解锁,可以用try将前面的执行包含,这里用finally来包含解锁
lock.unlock();
}
@Override
public void run() {
// TODO Auto-generated method stub
try {
PirntNumByCondition();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
package per.liyue.code.printinfo;
/*
* 字母打印线程类
*/
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
public class PrintLetter implements Runnable {
//这里的锁要公用
private Lock lock;
public void setLock(Lock lock) {
this.lock = lock;
}
//数字线程的控制量
private Condition conditionNum;
public void setConditionNum(Condition conditionNum) {
this.conditionNum = conditionNum;
}
//本线程的控制量
private Condition conditionLetter;
public void setConditionLetter(Condition conditionLetter) {
this.conditionLetter = conditionLetter;
}
private int sum = 0;
public void PirntNumByCondition() throws InterruptedException {
lock.lock();
char charFirst = 'A';
char charEnd = 'Z';
int firstNum = (int)charFirst;
int endNum = (int)charEnd;
while(firstNum <= endNum){
if (1 == sum) {
sum = 0;
//先启动对方,否则当前线程停止就不能启动对方了
conditionNum.signal();
//对方启动后, 本线程等待
conditionLetter.await();
} else {
char ch = (char)firstNum;
System.out.print(ch);
firstNum++;
sum++;
}
}
//这里一定不要忘记解锁,可以用try将前面的执行包含,这里用finally来包含解锁
lock.unlock();
}
@Override
public void run() {
// TODO Auto-generated method stub
try {
PirntNumByCondition();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
package per.liyue.code.printinfo;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/*
* 模拟的主线程类
*/
public class Main {
public static void main(String []args){
System.out.println("开始打印");
//创建公用锁对象
Lock lock = new ReentrantLock();
//将锁和两个控制量相关联
Condition conNum = lock.newCondition();
Condition conLetter = lock.newCondition();
//传入初始化值
PrintLetter pL = new PrintLetter();
pL.setLock(lock);
pL.setConditionNum(conNum);
pL.setConditionLetter(conLetter);
//传入初始化值
PrintNum pN = new PrintNum();
pN.setLock(lock);
pN.setConditionNum(conNum);
pN.setConditionLetter(conLetter);
//两个线程中,通过对自己和对方的控制量的调用,达到相互交替执行的目的
//启动线程
new Thread(pN).start();
new Thread(pL).start();
}
}
5.线程池
5.1线程池
5.2分解任务ForkJoinPool;
6.线程相关类
6.1ThreadLocal类
6.2不安全的线程集合
在Java中,ArrayList、LinkedList、HashSet、TreeSet、HashMap、TreeMap都是线程不安全的!如果多线程并发访问这些集合,那么需要用**Collections类包装后才是线程安全的。
Collections提供静态方法:
Collection:
public static Collection synchronizedCollection(Collection c)
Map:
public static <K,V> Map<K,V> synchronizedMap(Map<K,V> m)
List:
public static List synchronizedList(List list)
Set:
public static Set synchronizedSet(Set s)
Demo:
HashMap m = (HashMap) Collections.synchronizedMap(new HashMap<>());
6.3线程安全集合类
Java5开始,在java.util.concurrent下提供大量的线程安全集合类
22.多线程.md的更多相关文章
- 多线程MT和多线程MD的区别
这段时间司在招实习生,而不管是远程的电话面试或者是实际现场面试中领导都喜欢问你这个问题,但是可惜的是能很好答上来的人很少.后来发现不管是应届的实习生,甚至有些实际参加工作几年的人也未必真的了解这个问题 ...
- 详解多线程MT和多线程MD的区别
这段时间司在招实习生,而不管是远程的电话面试或者是实际现场面试中领导都喜欢问你这个问题,但是可惜的是能很好答上来的人很少.后来发现不管是应届的实习生,甚至有些实际参加工作几年的人也未必真的了解这个问题 ...
- java 22 - 22 多线程之 匿名内部类的方式实现多线程
首先回顾下之前的匿名内部类: 匿名内部类的格式: new 接口或者接口名(){ 重写方法 }; 本质:是该类或者接口的子类对象 匿名内部类方式使用多线程 1.new Thread(){代码-}.sta ...
- 从零开始的Python学习Episode 22——多线程
多线程 线程 线程是操作系统能够进行运算调度的最小单位.它被包含在进程之中,是进程中的实际运作单位.一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务. ...
- C++面试问题总结
转自:http://blog.csdn.net/wangtengqiang/article/details/8061806 1.static用法 static 的成员函数和成员变量,可直接通过类名:: ...
- Md5的加密 java实现
百度百科对MD5的说明是: Message Digest Algorithm MD5(中文名为消息摘要算法第 五版)为计算机安全领域广泛使用的一种散列函数,用以提供消息的完整性保护. MD5即Mess ...
- Jenkins与网站代码上线解决方案
1.1 前言 Jenkins是一个用Java编写的开源的持续集成工具.在与Oracle发生争执后,项目从Hudson项目独立. Jenkins提供了软件开发的持续集成服务.它运行在Servlet容器中 ...
- Jenkins与网站代码上线解决方案【转】
转自 Jenkins与网站代码上线解决方案 - 惨绿少年 https://www.nmtui.com/clsn/lx524.html 1.1 前言 Jenkins是一个用Java编写的开源的持续集成工 ...
- .NET中的那些受特别对待的类型(CriticalFinalizerObject)
转自:http://www.cnblogs.com/yuyijq/archive/2009/08/09/1542435.html 股票里面有个ST股,就是Special Treatment的意思.就是 ...
随机推荐
- python中变量的缓存机制
同一文件中, 变量的缓存机制 (在此范围内的相同值内存地址一样) Number: int: -5 ~ 正无穷 float: 非负数 bool: ...
- Java - 31 Java 发送邮件
Java 发送邮件 使用Java应用程序发送E-mail十分简单,但是首先你应该在你的机器上安装JavaMail API 和Java Activation Framework (JAF) . 你可以在 ...
- 深入Spring Boot:怎样排查 Cannot determine embedded database driver class for database type NONE
ref:https://www.journaldev.com/13830/spring-boot-cannot-determine-embedded-database-driver-class-for ...
- linux中tree命令
需要安装tree包(安装:yum -y install tree). tree命令的选项说明如下: [ 匹配选项:] -L:用于指定递归显示的深度,指定的深度必须是大于0的整数. -P:用于显示统配符 ...
- 剑指offer(一):二维数组中的查找
说明: 1.本系列是根据<剑指Offer>这个系列做的一个小笔记. 2.直接动力是因为师兄师姐找工作很难,而且机械出生的我面试算法更难. 3.刚开始准备刷LeetCode.LintCode ...
- Java 递归详解
递归详解: 1.递归一句话通俗讲就是一个方法自动重复调用自己的过程. 2.因为是重复调用自己了,所以看起来像一个循环,所以为了避免内存溢出系统崩溃,我们需要在方法里加一个返回值判断,用于递归循环的跳出 ...
- File Input Features
文件输入功能 1.该插件将将一个简单的 HTML 文件输入转换为高级文件选取器控件.将有助于对不支持 JQuery 或 Javascript 的浏览器的正常 HTML 文件输入进行回退. 2.文件输入 ...
- 最好的ie版本检测方式
预备知识:NodeList的实时性 通过 document.getElementsByTagName,document.forms ,document.images 等方法获取到nodelist以后 ...
- git 常用操作总结
廖雪峰博客的git 教程写得不错, 很详细,但是却总结的不是很好. 这里哥再详细总结一遍吧! Git鼓励大量使用分支: 查看分支:git branch 创建分支:git branch 切换分支:git ...
- hive 函数
collect_set(x) 列转行函数---没有重复, 组装多列的数据的结构体collect_list(x) 列转行函数---可以有重复,组装多列的数据的结构体concat_ws 拼接函数, 用 ...