学习目标:
一周掌握 多线程四种实现方式以及用法 入门知识
学习内容:
- 自定义类,继承Thread ,重写run()方法
- 自定义类,实现Runable ,重写run()方法
- 线程池
- 自定义类,实现callable接口重写call()
- 总结其中的函数
学习产出:
一.自定义类,继承Thread ,重写run()方法
package rrr;
import java.io.*;
public class thread {
public static void main(String[] args) throws IOException {
//创建线程`
myThread myThread = new myThread();
myThread myThread1 = new myThread();
// 修改名字
myThread.setName("郑");
myThread1.setName("李");
//启动线程
myThread.start();
myThread1.start();
}
}
package rrr;
import java.io.*;
import java.net.*;
public class myThread extends Thread{
int d=0;
@Override
public void run() {
for (int a=0;a<100;a++){
if (a%2==0) {
d=d+a;
// 获取当前线程名字并输出
System.out.println(Thread.currentThread().getName() + d);
}
}
}
}
二.自定义类,实现Runable ,重写run()方法
package rrr;
import java.io.*;
public class thread {
public static void main(String[] args) throws IOException {
myThread myThread = new myThread();
//创建线程`
Thread thread = new Thread(myThread);
Thread thread1 = new Thread(myThread);
// 修改名字
thread.setName("郑");
thread1.setName("李");
//启动线程
thread.start();
thread1.start();
}
}
package rrr;
import java.io.*;
import java.net.*;
public class myThread implements Runnable {
int d=0;
@Override
public void run() {
for (int a=0;a<100;a++){
if (a%2==0) {
d=d+a;
// 获取当前线程名字并输出
System.out.println(Thread.currentThread().getName() + d);
}
}
}
}
三.线程池
package rrr;
import java.io.*;
import java.util.concurrent.*;
public class thread {
public static void main(String[] args) throws IOException, ExecutionException, InterruptedException {
myThread myThread = new myThread();
//创建固态线程池
// ExecutorService executorService = Executors.newFixedThreadPool(2);
//创建动态线程池
ExecutorService executorService = Executors.newCachedThreadPool();
executorService.submit(myThread);
executorService.submit(myThread);
executorService.shutdown();
}
}
package rrr;
import java.util.concurrent.Callable;
public class myThread implements Runnable {
int d=0;
@Override
public void run() {
for (int a=0;a<100;a++){
if (a%2==0) {
System.out.println(Thread.currentThread().getName()+" " + a);
}
}
}
}
四.自定义类,实现callable接口重写call()
特点:他可以返回线程运行的结果
package rrr;
import java.io.*;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class thread {
public static void main(String[] args) throws IOException, ExecutionException, InterruptedException {
myThread myThread = new myThread();
//创建FutureTask结果集对象
FutureTask<Integer> integerFutureTask = new FutureTask<>(myThread);
//创建线程`
Thread thread = new Thread(integerFutureTask);
// 修改名字
thread.setName("线程一:");
//启动线程
thread.start();
//获取线程的结果集
Integer integer = integerFutureTask.get();
System.out.println(integer);
}
}
package rrr;
import java.util.concurrent.Callable;
public class myThread implements Callable<Integer> {
int d=0;
@Override
public Integer call() {
for (int a=0;a<100;a++){
if (a%2==0) {
d=d+a;
// 获取当前线程名字并输出
System.out.println(Thread.currentThread().getName() + d);
}
}
return d;
}
}
例题:
1.卖票程序1,使用乐观锁,100张,票三个窗口卖
package rrr;
import java.io.*;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.Socket;
import java.util.concurrent.*;
public class thread {
public static void main(String[] args) {
myThread myThread=new myThread();
myThread myThread1=new myThread();
myThread myThread2=new myThread();
myThread.setName("窗口一");
myThread1.setName("窗口二");
myThread2.setName("窗口三");
myThread.start();
myThread1.start();
myThread2.start();
}
}
package rrr;
public class myThread extends Thread{
// 表示这个类所有对象都共享a
public static int a=0;
//同步锁对象一定要唯一
// static Object object=new Object();
@Override
public void run() {
//同步代码块
while(true){
//可以是类名class,字节码文件对象,文件是唯一的
synchronized (myThread.class){
// synchronized (object){
if (a<100){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
a++;
System.out.println(Thread.currentThread().getName()+"卖了"+a);
}else {
break;
}
}
}}}
1.卖票程序1,使用悲观锁,100张,票三个窗口卖
package rrr;
import java.io.*;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.Socket;
import java.util.concurrent.*;
public class thread {
public static void main(String[] args) {
myThread myThread=new myThread();
myThread myThread1=new myThread();
myThread myThread2=new myThread();
myThread.setName("窗口一");
myThread1.setName("窗口二");
myThread2.setName("窗口三");
myThread.start();
myThread1.start();
myThread2.start();
}
}
package rrr;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class myThread extends Thread{
// 表示这个类所有对象都共享a
static int a1=0;
//同步锁对象一定要唯一
// static Object object=new Object();
//创建悲观锁对象
static Lock d1= new ReentrantLock();
@Override
public void run() {
//同步代码块
while(true){
//上锁
d1.lock();
try {
//等于100退出循环
if (a1==100){
break;
}else {
a1++;
System.out.println(Thread.currentThread().getName()+"卖了"+a1);
}
} catch (Exception e) {
e.printStackTrace();
}
finally {
//无论如何也要开锁
d1.unlock();
//开完锁后睡0.01秒
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}}}
2.生产者消费者,生产者进行生产奶,消费者消费奶,利用线程进行等待唤醒(因为篇幅长,增加可观性,就不进行导包了)
实现方式一:
public class text {
public static void main(String[] args) {
box box = new box();
milkWork milkWork = new milkWork(box);
perSon perSon = new perSon(box);
Thread a=new Thread(milkWork);
Thread d=new Thread(perSon);
a.start();
d.start();
}
}
//生产者
public class milkWork implements Runnable {
private box box;
public milkWork(box box) {
this.box=box;
}
@Override
public void run() {
for (int a=1;a<10;a++){
box.setMilk(a);;
}
}
}
//消费者
public class perSon implements Runnable{
private box box;
public perSon(box box) {
this.box=box;
}
@Override
public void run() {
while (true){
box.getMilk();
}
}
}
//工厂
public class box {
private int milk;
//先定义没有奶
private boolean fye=false;
public synchronized void setMilk(int milk) {
//有奶就去等待
if (fye){
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//把奶装入
this.milk = milk;
System.out.println("送奶工放置了第"+ milk+"奶中");
// 唤醒所有
notifyAll();
// 把奶放置完后修改为有奶
fye=true;
}
public synchronized void getMilk() {
// 如果fye=true 那么!fye就是false
//如果没有了奶就去等待送奶
if (!fye){
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("用户拿到了了第"+milk+"奶中");
fye=false;
notifyAll();
}
}
实现方式二lock锁实现
package day02;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.Queue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* 线程间的通信: synchronized(obj) obj.wait()/notify()
* 生产者和消费者的设计模式:
* 生产者线程: 存储是否最大化,最大时,等待, 非最大时,可以生产并存储数据同时,唤醒消费者的线程
* 消费者线程: 取出数据时,验证是否为空,为空时则等待,非空时,取出并通知或唤醒生产线程
*/
class GoodsKC {
private Queue<Integer> queue; // 存储数据的集合(队列, 先进先出)
private int max_size ;
private Lock lock;
private Condition condition; // 锁的条件
public GoodsKC(int max_size) {
this.max_size = max_size;
queue = new LinkedList<>();
lock = new ReentrantLock(); // 重入锁类,创建线程同步锁对象
condition = lock.newCondition();
}
public void push(int data) throws InterruptedException { // 存入
lock.lock();
while (queue.size() == max_size){
System.out.println(Thread.currentThread().getName() +" 当前仓库已满");
condition.await(); // 锁条件的等待
}
queue.offer(data); // 压入队列(从右侧)
System.out.println(Thread.currentThread().getName()+" 存入"+ data +"成功");
// 通知消费线程可以取数据
condition.signalAll();
lock.unlock();
}
public int poll() throws InterruptedException { // 取,弹出
lock.lock();
while (queue.isEmpty()){
System.out.println(Thread.currentThread().getName() +" 当前仓库已空");
boolean await = condition.await(1, TimeUnit.SECONDS); // 如果超时,则返回false
if(!await) {
lock.unlock(); // 释放锁,让其它的消费者线程进入
throw new InterruptedException();
}
}
Integer data = queue.poll(); // 弹出, peek()获取队列最左边的元素
System.out.println(Thread.currentThread().getName() +" 消费了"+data);
condition.signalAll();
lock.unlock();
return data;
}
}
class ProducerTask implements Runnable{
private GoodsKC kc;
private int start_, end_;
public ProducerTask(GoodsKC kc, int start_, int end_) {
this.kc = kc;
this.start_ = start_;
this.end_ = end_;
}
@Override
public void run() {
for (int i = start_; i <= end_; i++) {
try {
kc.push(i);
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName()+" 停止生产");
}
}
class ConsumerTask implements Runnable{
private GoodsKC kc;
public ConsumerTask(GoodsKC kc) {
this.kc = kc;
}
@Override
public void run() {
while (true){
try {
int data = kc.poll();
} catch (InterruptedException e) {
break;
}
try {
Thread.sleep(400);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName()+" 停止消费");
}
}
public class TestThread1 {
public static void main(String[] args) {
GoodsKC goodsKC = new GoodsKC(10);
// 生产线程
Thread t1 = new Thread(new ProducerTask(goodsKC,1, 10));
Thread t2 = new Thread(new ProducerTask(goodsKC,11, 20));
// 消费者线程
Thread t3 = new Thread(new ConsumerTask(goodsKC));
Thread t4 = new Thread(new ConsumerTask(goodsKC));
Thread t5 = new Thread(new ConsumerTask(goodsKC));
for (Thread t: Arrays.asList(t1, t2, t3,t4, t5)) {
t.start();
}
}
}
2.实现方式二:synchronized关键字实现
package day01;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.Queue;
/**
* 线程间的通信: synchronized(obj) obj.wait()/notify()
* 生产者和消费者的设计模式:
* 生产者线程: 存储是否最大化,最大时,等待, 非最大时,可以生产并存储数据同时,唤醒消费者的线程
* 消费者线程: 取出数据时,验证是否为空,为空时则等待,非空时,取出并通知或唤醒生产线程
*/
class GoodsKC {
private Queue<Integer> queue; // 存储数据的集合(队列, 先进先出)
private int max_size ;
private Object lock; // = new Object(); 饿汉式
public GoodsKC(int max_size) {
this.max_size = max_size;
queue = new LinkedList<>();
lock = new Object(); // 赖汉式
}
public void push(int data) throws InterruptedException { // 存入
synchronized (lock){
while (queue.size() == max_size){
System.out.println(Thread.currentThread().getName() +" 当前仓库已满");
lock.wait();
}
queue.offer(data); // 压入队列(从右侧)
System.out.println(Thread.currentThread().getName()+" 存入"+ data +"成功");
// 通知消费线程可以取数据
lock.notifyAll();
}
}
public int poll() throws InterruptedException { // 取,弹出
synchronized (lock){
while (queue.isEmpty()){
System.out.println(Thread.currentThread().getName() +" 当前仓库已空");
lock.wait();
}
Integer data = queue.poll(); // 弹出, peek()获取队列最左边的元素
System.out.println(Thread.currentThread().getName() +" 消费了"+data);
lock.notifyAll(); // 通知生产线程,可以存数据
return data;
}
}
}
class ProducerTask implements Runnable{
private GoodsKC kc;
private int start_, end_;
public ProducerTask(GoodsKC kc, int start_, int end_) {
this.kc = kc;
this.start_ = start_;
this.end_ = end_;
}
@Override
public void run() {
for (int i = start_; i <= end_; i++) {
try {
kc.push(i);
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class ConsumerTask implements Runnable{
private GoodsKC kc;
public ConsumerTask(GoodsKC kc) {
this.kc = kc;
}
@Override
public void run() {
while (true){
try {
int data = kc.poll();
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
Thread.sleep(400);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class TestThread7 {
public static void main(String[] args) {
GoodsKC goodsKC = new GoodsKC(10);
// 生产线程
Thread t1 = new Thread(new ProducerTask(goodsKC,1, 20));
Thread t2 = new Thread(new ProducerTask(goodsKC,21, 50));
// 消费者线程
Thread t3 = new Thread(new ConsumerTask(goodsKC));
Thread t4 = new Thread(new ConsumerTask(goodsKC));
Thread t5 = new Thread(new ConsumerTask(goodsKC));
for (Thread t: Arrays.asList(t1, t2, t3,t4, t5)) {
t.start();
}
}
}
Lock接口
说明:说明:JDK1.5提供的同步锁操作接口
Condition newCondition() 获取锁的情况,便于操作线程的状态
使用 Condition condition = lock.newCondition();
1) lock() 获取锁 如锁被占用,则等待。
2)boolean tryLock() 尝试获取锁 成功返回true,失败返回false,不阻塞
Conditon接口
1)await() 等待
2)signal() 唤醒
3)signalAll() 唤醒所有
ReentrantLock类
使用 Lock lock = new ReentrantLock();
扩展-同步的集合类
1.Collections工具类
1)public static <T> Collection<T> synchronizedCollection(Collection<T> c)
2)public static <T> List<T> synchronizedList(List<T> list)
3)public static <T> Set<T> synchronizedSet(Set<T> s)
4)public static <K,V> Map<K,V> synchronizedMap(Map<K,V> m)
5)public static <T> SortedSet<T> synchronizedSortedSet(SortedSet<T> s)
6)public static <K,V> SortedMap<K,V> synchronizedSortedMap(SortedMap<K,V> m)
说明:JDK1.2提供,接口统一、维护性高,但性能没有提升,均以synchonized实现。
2.3.CopyOnWriteArrayList
1)线程安全的ArrayList,加强版读写分离
2)写有锁,读无锁,读写之间不阻塞,优于读写锁。
3)写入时,先copy一个容器副本、再添加新元素,最后替换引用
4)使用方式与ArrayList无异
3.CopyOnWriteArraySet
1)线程安全的Set,底层使CopyOnWriteArrayList实现
2)唯一不同在于,使用addIfAbsent()添加元素,会遍历数组
3)如存在元素,则不添加(扔掉副本)
4.ConcurrentHashMap
1)初始容量默认为16段(Segment),使用分段锁设计
2) 不对整个Map加锁,而是为每个Segment加锁。
3)最理想状态为16个对象分别存入16个Segment,并行数量16。
4)当多个对象存入同一个Segment时,才需要互斥。
5)使用方式与HashMap无异。
6)JDK1.8改为CAS无锁算法
5.ConcurrentLinkedQueue
1)线程安全、可高效读写的队列,高并发下性能最好的队列
2)无锁、CAS比较交换算法,修改的方法包含三个核心参数(V,E,N)
V:要更新的变量、E:预期值、N:新值。
只有当V==E时,V=N;否则表示已被更新过,则取消当前操作
3)核心方法 offer(v) 插入 poll() 删除 peek() 获取
6.BlockingQueue接口
1)void put(E e) //将指定元素插入此队列中,如果没有可用空间,则等待
2)E take() //获取并移除此队列头部元素,如果没有可用元素,则等待
3)可用于解决生产生、消费者问题
4)实现类
ArrayBlockingQueue 数组结构实现,有界队列(手工固定上限)
LinkedBlockingQueue 链表结构实现,无界队列。(默认上限Integer.MAX_VALUE)
例子
package day02;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
class TestBlockingQueue {
public static void main(String[] args) {
// 将普通的List转化为线程安全的List
final BlockingQueue<String> data = new ArrayBlockingQueue<String>(10);
// 创建三个线程产出数据
Runnable task1 = new Runnable() {
@Override
public void run() {
String item = UUID.randomUUID().toString();
System.out.println(Thread.currentThread().getName()+"产出"+item);
data.add(item);
}
};
ExecutorService service1 = Executors.newFixedThreadPool(3);
service1.submit(task1);
service1.submit(task1);
service1.submit(task1);
service1.shutdown();
// 创建两个线程 删除数据
Runnable task2 = new Runnable() {
@Override
public void run() {
try {
String item = data.take(); // 阻塞
System.out.println(Thread.currentThread().getName()+" 消费了"+item);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
ExecutorService service2 = Executors.newFixedThreadPool(2);
service2.execute(task2);
service2.execute(task2);
service2.shutdown();
}
}
方法 | 释义 |
Thread.currentThread().getName() | 获取当前线程的名字 |
Thread.yield(); | 尽可能让出cpu的执行权 |
wait(); | 等待 |
notifyAll(); | 唤醒 |
thread.join(); | 插队线程,我先执行,其他人往后 |
thread.setPriority(1); | 设置线程优先级为1-10 |
thread.setDaemon(true); | 设置守护线程,守护其他线程执行完后自己慢慢关闭 |
executorService.submit(myThread); | 线程池提交任务 |
executorService.shutdown(); | 关闭线程池 |