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的更多相关文章

  1. 多线程MT和多线程MD的区别

    这段时间司在招实习生,而不管是远程的电话面试或者是实际现场面试中领导都喜欢问你这个问题,但是可惜的是能很好答上来的人很少.后来发现不管是应届的实习生,甚至有些实际参加工作几年的人也未必真的了解这个问题 ...

  2. 详解多线程MT和多线程MD的区别

    这段时间司在招实习生,而不管是远程的电话面试或者是实际现场面试中领导都喜欢问你这个问题,但是可惜的是能很好答上来的人很少.后来发现不管是应届的实习生,甚至有些实际参加工作几年的人也未必真的了解这个问题 ...

  3. java 22 - 22 多线程之 匿名内部类的方式实现多线程

    首先回顾下之前的匿名内部类: 匿名内部类的格式: new 接口或者接口名(){ 重写方法 }; 本质:是该类或者接口的子类对象 匿名内部类方式使用多线程 1.new Thread(){代码-}.sta ...

  4. 从零开始的Python学习Episode 22——多线程

    多线程 线程 线程是操作系统能够进行运算调度的最小单位.它被包含在进程之中,是进程中的实际运作单位.一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务. ...

  5. C++面试问题总结

    转自:http://blog.csdn.net/wangtengqiang/article/details/8061806 1.static用法 static 的成员函数和成员变量,可直接通过类名:: ...

  6. Md5的加密 java实现

    百度百科对MD5的说明是: Message Digest Algorithm MD5(中文名为消息摘要算法第 五版)为计算机安全领域广泛使用的一种散列函数,用以提供消息的完整性保护. MD5即Mess ...

  7. Jenkins与网站代码上线解决方案

    1.1 前言 Jenkins是一个用Java编写的开源的持续集成工具.在与Oracle发生争执后,项目从Hudson项目独立. Jenkins提供了软件开发的持续集成服务.它运行在Servlet容器中 ...

  8. Jenkins与网站代码上线解决方案【转】

    转自 Jenkins与网站代码上线解决方案 - 惨绿少年 https://www.nmtui.com/clsn/lx524.html 1.1 前言 Jenkins是一个用Java编写的开源的持续集成工 ...

  9. .NET中的那些受特别对待的类型(CriticalFinalizerObject)

    转自:http://www.cnblogs.com/yuyijq/archive/2009/08/09/1542435.html 股票里面有个ST股,就是Special Treatment的意思.就是 ...

随机推荐

  1. Java - 20 Java 继承

    Java 继承 继承是java面向对象编程技术的一块基石,因为它允许创建分等级层次的类.继承可以理解为一个对象从另一个对象获取属性的过程. 如果类A是类B的父类,而类B是类C的父类,我们也称C是A的子 ...

  2. python连接RabbitMQ

    安装PIP wget "https://pypi.python.org/packages/source/p/pip/pip-1.5.4.tar.gz tar -xzvf pip-.tar.g ...

  3. QQ聊天字体选择

    <!DOCTYPE html> <html lang="en"> <head>     <meta charset="UTF-8 ...

  4. python 27 获取时区转换后的时间

    python3的datetime有timezone属性,这里介绍python2.7环境下,获取时区转换后的时间. 利用第三方插件,pytz,没有安装的话安装一下. #!/usr/bin/env pyt ...

  5. 【Selenium-WebDriver自学】WebDriver断言处理(十二)

    断言使用 http://www.cnblogs.com/itliucheng/p/5578788.html http://blog.csdn.net/gzh0222/article/details/7 ...

  6. LeetCode 7 Reverse Integer & int

    Reverse Integer 想用余10直接算,没想到 -123%10 是 7, 原因 -123-(-123//10*10) r=a-n*[a/n] 以上,r是余数,a是被除数,n是除数. 唯一不同 ...

  7. 1950261 - SAP HANA Database Backup Policy Recommendations and Regular Backup Script

    =====Symptom For SAP Business One, version for SAP HANA users, SAP HANA provides a range of database ...

  8. python中的remove

    remove()从左到右寻找,移除找到的第一个指定元素,举个例子: 1 a = ["hello", "world", "world", &q ...

  9. jQuery 移除事件与模拟事件

    <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta http ...

  10. php单图片上传。

    1.input:file form 提交 /** * 用户头像上传 * @param [type] $file 图像信息 */ function domeadd($file){ if (is_arra ...