多线程编程
目录
文章目录
work1
package cn.tedu.work;
public class ThreadWork1 {
public static void main(String[] args) {
RunnableThread1 target_a=new RunnableThread1();
Thread thread_a = new Thread(target_a, "a");
Thread thread_b = new Thread(target_a, "b");
Thread thread_c = new Thread(target_a, "c");
try {
thread_a.start();
thread_a.join();
thread_b.start();
thread_b.join();
thread_c.start();
thread_c.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class RunnableThread1 implements Runnable {
public void run() {
Thread thread=Thread.currentThread();
System.out.println("当前线程为"+thread.getName());
System.out.println("线程"+thread.getName()+"休眠2000ms");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程"+thread.getName()+"执行结束\n");
}
}
work2
package cn.tedu.work;
public class ThreadWork2 {
/**
* 使用
* */
public static void main(String[] args) {
RunnableThread2 target1 = new RunnableThread2();
Thread threada = new Thread(target1, "a");
Thread threadb = new Thread(target1, "b");
threada.start();
threadb.start();
}
}
class RunnableThread2 implements Runnable {
public static int nub = 0;
public static long th_id = 0;
Object ob = new Object();
public void run() {
while (true) {
synchronized (ob) {
long cunnt_id = Thread.currentThread().getId();
if (cunnt_id != th_id && nub <= 100) {
System.out.println(Thread.currentThread().getName() + ": " + (nub));
th_id = cunnt_id;
nub++;
} else if (nub > 100) {
break;
}
}
}
}
}
work3
package cn.tedu.work;
import java.security.Principal;
public class ThreadWork3 {
/**
* 写两个线程,一个线程打印1~ 52,另一个线程打印A~Z,打印顺序是12A34B...5152Z
* */
public static void main(String[] args) {
Thread thread1=new Thread(new PrintNub(),"nub");
Thread thread2 = new Thread(new PrintChar(), "char");
thread1.start();
thread2.start();
}
}
class PrintNub implements Runnable{
public void run() {
int nub=1;
while (true){
synchronized (ThreadWork3.class){
if(nub<52){
System.out.println(nub+" "+(nub+1));
nub+=2;
}else if(nub>=52){
ThreadWork3.class.notifyAll();
break;
}
ThreadWork3.class.notifyAll();
try {
ThreadWork3.class.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
class PrintChar implements Runnable{
public static Object ob=new Object();
public void run() {
char a='A';
while (true){
synchronized (ThreadWork3.class){
if(a<='Z'){
System.out.println(a);
a+=1;
}else if(a>'Z'){
ThreadWork3.class.notify();
break;
}
ThreadWork3.class.notifyAll();
try {
ThreadWork3.class.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
work4
package cn.tedu.work;
public class ThreadWork4 {
/**
* 编写一个程序,启动三个线程,三个线程的ID分别是A,B,C;,每个线程将自己的ID值在屏幕上打印5遍,打印顺序是ABCABC...
* */
public static void main(String[] args) {
ThreadA target=new ThreadA();
Thread thread1 = new Thread(target, "A");
Thread thread2 = new Thread(target, "C");
Thread thread3 = new Thread(target, "B");
thread1.start();
thread2.start();
thread3.start();
}
}
class ThreadA implements Runnable {
public volatile char t_char='A';
public void run() {
for (int i = 0; i < 5; ) {
Thread thread = Thread.currentThread();
char ch = thread.getName().charAt(0);
if (ch==t_char) {
System.out.printf(Thread.currentThread().getName());
i++;
t_char= (char) ('A'+(t_char-'A'+1)%3);
}
}
}
}
work5
package cn.tedu.work;
public class ThreadWork5 {
/**
* 编写10个线程,第一个线程从1加到10,
* 第二个线程从11加20…第十个线程从91加到100,
* 最后再把10个线程结果相加。
* */
public static void main(String[] args) {
for(int i=0;i<10;i++){
ThreadAdd target = new ThreadAdd(i);
new Thread(target).start();
}
}
}
class ThreadAdd implements Runnable{
// WriteLock lock = new WriteLock();
public static int sum;
public int time;
public ThreadAdd(int time) {
this.time = time;
}
public void run() {
int t_sum=0;
for(int i=1;i<=10;i++) {
t_sum += (time * 10)+i;
}
// System.out.println(t_sum);
sum+=t_sum;
if(time==9){
System.out.println(sum);
}
}
}
work6
package cn.tedu.work;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class ThreadWork6 {
/**
* 三个窗口同时卖票
*/
public static void main(String[] args) {
TicketThread target = new TicketThread();
Thread thread1 = new Thread(target, "1");
Thread thread2 = new Thread(target, "2");
Thread thread3 = new Thread(target, "3");
thread1.start();
thread2.start();
thread3.start();
}
}
class TicketThread implements Runnable {
public int ticket_nub = 100;
static ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
static Lock lock = rwl.writeLock();
public void run() {
while (true) {
lock.lock();
try {
if (ticket_nub > 0) {
System.out.println("您的票号为:" + ticket_nub + "");
System.out.println("窗口: " + Thread.currentThread().getName());
Thread.sleep(100);
ticket_nub--;
} else if (ticket_nub <= 0) {
break;
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
System.out.println("结束线程:" + Thread.currentThread().getName());
}
}
work7
https://zhuanlan.zhihu.com/p/65229222
package cn.tedu.work;
import java.awt.*;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.BlockingDeque;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class ThreadWork7 {
/**
* 7、 生产者消费者
* 7.1 synchronized方式
* 7.2 ReentrantLock方式 (可以保证顺序)
* 7.3 BlockingQueue方式
*/
public static void main(String[] args) {
// new BufferShelf().synchronizedMode();
new BufferShelf().ReentrantLockMode();
// new BufferShelf().BlockingQueueMode();
}
}
enum Goodstype {
烤肉,
茄子,
火锅,
油条
}
class BufferShelf {
// private static final Object L = ;
public static LinkedList<Map<String, String>> goodsBuffer = new LinkedList<>();
static int bufferNub = 2;
static long starttinme;
public void push() {
Random rand = new Random();
if (goodsBuffer.size() < bufferNub) {
HashMap<String, String> map = new HashMap<String, String>();
int goods_ind = rand.nextInt(Goodstype.values().length);
map.put("name", Goodstype.values()[goods_ind].toString());
goodsBuffer.push(map);
System.out.println(Thread.currentThread().getName() + "生产: " +map.get("name")+" buffer"+ goodsBuffer);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public void pop() {
if (goodsBuffer.size() > 0) {
Map<String, String> pop = goodsBuffer.pop();
// System.out.println("消费:" + pop);
System.out.println(Thread.currentThread().getName() + "消费:"+pop.get("name") +" buffer"+ goodsBuffer);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public void push_s(){
synchronized (BufferShelf.class){
push();
}
}
public void pop_s(){
synchronized (BufferShelf.class){
pop();
}
}
public void push_l(){
lock.lock();
try {
push();
} finally {
lock.unlock();
}
}
public void pop_l(){
lock.lock();
try {
pop();
} finally {
lock.unlock();
}
}
public void run(Runnable producer_t, Runnable consumer_t) {
Thread producer = new Thread(producer_t, "P1");
Thread producer2 = new Thread(producer_t, "P2");
Thread producer3 = new Thread(producer_t, "P3");
Thread consumer = new Thread(consumer_t, "C1");
Thread consumer2 = new Thread(consumer_t, "C2");
Thread consumer3 = new Thread(consumer_t, "C3");
producer.start();
producer2.start();
producer3.start();
consumer.start();
consumer2.start();
consumer3.start();
}
public void synchronizedMode() {
starttinme = System.currentTimeMillis();
Runnable producer_t = new Runnable() {
@Override
public void run() {
while (true) {
push_s();
if (System.currentTimeMillis() - starttinme > 10000) {
break;
}
}
}
};
Runnable consumer_t = new Runnable() {
@Override
public void run() {
while (true) {
pop_s();
if (System.currentTimeMillis() - starttinme > 10000) {
// System.out.println(goodsBuffer);
break;
}
}
}
};
run(producer_t, consumer_t);
}
static Lock lock = new ReentrantLock();
public void ReentrantLockMode() {
starttinme = System.currentTimeMillis();
Runnable producer_t = new Runnable() {
@Override
public void run() {
while (true) {
push_l();
if (System.currentTimeMillis() - starttinme > 2000) {
// System.out.println(goodsBuffer);
break;
}
}
}
};
Runnable consumer_t = new Runnable() {
@Override
public void run() {
while (true) {
pop_l();
if (System.currentTimeMillis() - starttinme > 10000) {
break;
}
}
}
};
run(producer_t, consumer_t);
}
// static BlockingQueue<Map<String, String>> buff = new LinkedBlockingDeque<>(bufferNub);
static BlockingQueue< String> buff = new LinkedBlockingDeque<>(bufferNub);
/**
* buff BlockingQueueMode 下的缓冲区 缓冲器的大小为bufferNub
*
*
* */
public void BlockingQueueMode() {
starttinme = System.currentTimeMillis();
Runnable producer_t = new Runnable() {
@Override
public void run() {
while (true) {
Random rand = new Random();
HashMap<String, String> map = new HashMap<String, String>();
int goods_ind = rand.nextInt(Goodstype.values().length);
map.put("name", Goodstype.values()[goods_ind].toString());
try {
// buff.put(map);
String aput=Goodstype.values()[goods_ind].toString();
buff.put(aput);
boolean bl = true;
// bl = buff.offer(map, 10, TimeUnit.MILLISECONDS);
if (bl) {
// System.out.println(Thread.currentThread().getName() + "生产:" + aput+ " buff="+buff);
System.out.println(Thread.currentThread().getName() + "生产:" + aput);
}
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (System.currentTimeMillis() - starttinme > 2000) {
break;
}
}
}
};
Runnable consumer_t = new Runnable() {
@Override
public void run() {
while (true) {
try {
// Map<String, String> pop = buff.take();
// Map<String, String> pop = buff.poll(10, TimeUnit.MILLISECONDS);
String pop = buff.poll(10, TimeUnit.MILLISECONDS);
// System.out.println(Thread.currentThread().getName() + "消费 " + pop+" buff="+ buff);
System.out.println(Thread.currentThread().getName() + "消费 " + pop);
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (System.currentTimeMillis() - starttinme > 2000) {
// System.out.println(goodsBuffer);
break;
}
}
}
};
run(producer_t, consumer_t);
}
}
work8
package cn.tedu.work;
import java.util.concurrent.locks.LockSupport;
public class ThreadWork8 {
static Thread thread1;
static Thread thread2;
/**
* 交替打印两个数组
* */
public static void main(String[] args) {
int[] nubs=new int[10];
char[] chars=new char[10];
for(int i=0;i<10;i++){
nubs[i]=i;
chars[i]= (char) (i+'a');
}
thread1=new Thread( ()->{
for(char ach:chars){
System.out.printf(ach+" ");
LockSupport.unpark(thread2);
LockSupport.park();
}
});
thread2=new Thread(()->{
for(int nub:nubs){
System.out.printf(nub+" ");
LockSupport.unpark(thread1);
LockSupport.park();
}
});
thread1.start();
thread2.start();
}
}
锁的实现
-
synchronized
锁对象为 this 对象 或者传入参数对象
-
ReentrantLock
锁对象为Lock
ReentrantLock lock = new ReentrantLock(); lock.lock(); lock.unlock;
-
wirterLock / readerLock
ReadWriteLock ReentrantReadWriteLock rwl = new ReentrantReadWriteLock(); Lock lock=rwl.readLock(); Lock lock=rwl.writeLock(); lock.lock(); lock.unlock;
-
信号量Semaphore
通常用于限制同时访问资源的线程数。例如,某段代码只允许最多三个线程进入执行,那么就可以初始化Semaphore为3,每次进入时使用acquire进行减一,退出这段代码时使用release加1,就可以达到控制的目的。
semaphore针对的是资源的可进入次数,如果可进入次数为1,那么此时可以类比lock的实现,遍又想到了使用reentrantlock的condition实现的另一个变种,一个lock的两个condition,类似一个资源的两个semaphore。代码的写法几乎一样,就不再在这贴出来了。
// s1.acquire(); 获取信号量 相当于上锁 // s1.release(); 释放锁 private static Semaphore s1 = new Semaphore(1); private static Semaphore s2 = new Semaphore(0); // s1.acquire() //获取锁 // s2.acquire(); //得到阻塞 等待G中 // s1.acquire() -> s1.run->s2.release()->s1. static class G extends Thread{ @Override public void run() { while (true){ try { s1.acquire(); //只能有一个线程能够执行下面代码 System.out.println("G"); s2.release(); //必须在s1打开请时等到G线程释放 s2.才能进行执行 } catch (InterruptedException e) { e.printStackTrace(); } } } } static class H extends Thread{ @Override public void run() { while (true){ try { s2.acquire(); System.out.println("H"); s1.release(); } catch (InterruptedException e) { e.printStackTrace(); } } } }
-
锁的实现
-
阻塞呢
-
线程间同步
-
volatile 关键字
static volatile boolean a = true;
volatile能够保证可见性和有序性。
可见性,被volatile修饰的变量的修改能立刻同步到主存,由于处理器缓存一致性机制,会使其他线程缓存中的数据失效,从而能够保证线程读到的都是最新数据。
有序性,防止指令重排序,指令重排序在单线程下是没有问题的,因为不会影响最终的结果,只是处理顺序上有不同,但是在多线程情况下,一旦指令进行重排序,会出现一些意料之外的结果。例如单例模式下,单例对象实例化完成之前引用提前暴露。当变量被声明为volatile之后,会在读写间建立happens-before关系,通过添加内存屏障,来禁止指令重排序。
-
使用静态变量
唤醒 阻塞
-
LockSupport 工具类
LockSupport是一个线程工具类,所有的方法都是静态方法,可以让线程在任意位置阻塞,也可以在任意位置唤醒。
它的内部其实两类主要的方法:park(停车阻塞线程)和unpark(启动唤醒线程)。
LockSupport.unpark(a); LockSupport.park();
-
wait()/notify Object
-
Reentrantlock Condition await/single
当调用condition.await()方法后会使得当前获取lock的线程进入到等待队列,如果该线程能够从await()方法返回的话一定是该线程获取了与condition相关联的lock。
调用condition的signal或者signalAll方法可以将等待队列中等待时间最长的节点移动到同步队列中。
static ReentrantLock lock = new ReentrantLock(); static Condition condition = lock.newCondition(); condition.signal(); //唤醒最早进入的 condition.await(); //加入等待队列
交替打印方式
-
方式一:
- 只有一个线程 进行打印 使用独占锁 或者使用
- 完成线程的切换
- 使用静态变量
- 使用线程阻塞 唤醒
-
方式二
- 使用线程同步的变量
-
只有一个语句或者一个变量 比如基数
- 使用原子类型
相关blog
Lambda
Lambda 即是匿名函数
匿名类
原子类型
java里的运算(比如自增)并不是原子性的。
理论锁模型
- 乐观锁/悲观锁
乐观锁:顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。
//怎么判断需要更新数据,
悲观锁:总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁。比如Java里面的同步原语synchronized关键字的实现就是悲观锁。
- 独享锁/共享锁
独享锁是指该锁一次只能被一个线程所持有。
只能被一个
共享锁是指该锁可被多个线程所持有。
对于Java ReentrantLock而言,其是独享锁。但是对于Lock的另一个实现类ReadWriteLock,其读锁是共享锁,其写锁是独享锁。
读锁的共享锁可保证并发读是非常高效的,读写,写读,写写的过程是互斥的。
独享锁与共享锁也是通过AQS来实现的,通过实现不同的方法,来实现独享或者共享。
对于Synchronized而言,当然是独享锁。
-
互斥锁/读写锁
上面讲的独享锁/共享锁就是一种广义的说法,互斥锁/读写锁就是具体的实现。
互斥锁在Java中的具体实现就是ReentrantLock。
读写锁在Java中的具体实现就是ReadWriteLock。
-
可重入锁
可重入锁又名递归锁,是指在同一个线程在外层方法获取锁的时候,在进入内层方法会自动获取锁。说的有点抽象,下面会有一个代码的示例。
对于Java ReetrantLock而言,从名字就可以看出是一个重入锁,其名字是Re entrant Lock 重新进入锁。
对于Synchronized而言,也是一个可重入锁。可重入锁的一个好处是可一定程度避免死锁。
1 synchronized void setA() throws Exception{
2 Thread.sleep(1000);
3 setB();
4 }
5
6 synchronized void setB() throws Exception{
7 Thread.sleep(1000);
8 }
上面的代码就是一个可重入锁的一个特点。如果不是可重入锁的话,setB可能不会被当前线程执行,可能造成死锁。
- 公平锁/非公平锁
公平锁是指多个线程按照申请锁的顺序来获取锁。
非公平锁是指多个线程获取锁的顺序并不是按照申请锁的顺序,有可能后申请的线程比先申请的线程优先获取锁。有可能,会造成优先级反转或者饥饿现象。
对于Java ReetrantLock而言,通过构造函数指定该锁是否是公平锁,默认是非公平锁。非公平锁的优点在于吞吐量比公平锁大。
对于Synchronized而言,也是一种非公平锁。由于其并不像ReentrantLock是通过AQS的来实现线程调度,所以并没有任何办法使其变成公平锁。
- 分段锁
分段锁其实是一种锁的设计,并不是具体的一种锁,对于ConcurrentHashMap而言,其并发的实现就是通过分段锁的形式来实现高效的并发操作。
数组插入的时候判断插入分段,锁掉那个分段,可以实现并行插入
我们以ConcurrentHashMap来说一下分段锁的含义以及设计思想,ConcurrentHashMap中的分段锁称为Segment,它即类似于HashMap(JDK7和JDK8中HashMap的实现)的结构,即内部拥有一个Entry数组,数组中的每个元素又是一个链表;同时又是一个ReentrantLock(Segment继承了ReentrantLock)。
当需要put元素的时候,并不是对整个hashmap进行加锁,而是先通过hashcode来知道他要放在哪一个分段中,然后对这个分段进行加锁,所以当多线程put的时候,只要不是放在一个分段中,就实现了真正的并行的插入。
但是,在统计size的时候,可就是获取hashmap全局信息的时候,就需要获取所有的分段锁才能统计。
分段锁的设计目的是细化锁的粒度,当操作不需要更新整个数组的时候,就仅仅针对数组中的一项进行加锁操作。
- 偏向锁/轻量级锁/重量级锁
这三种锁是指锁的状态,并且是针对Synchronized。在Java 5通过引入锁升级的机制来实现高效Synchronized。这三种锁的状态是通过对象监视器在对象头中的字段来表明的。
偏向锁是指一段同步代码一直被一个线程所访问,那么该线程会自动获取锁。降低获取锁的代价。
轻量级锁是指当锁是偏向锁的时候,被另一个线程所访问,偏向锁就会升级为轻量级锁,其他线程会通过自旋的形式尝试获取锁,不会阻塞,提高性能。
重量级锁是指当锁为轻量级锁的时候,另一个线程虽然是自旋,但自旋不会一直持续下去,当自旋一定次数的时候,还没有获取到锁,就会进入阻塞,该锁膨胀为重量级锁。重量级锁会让他申请的线程进入阻塞,性能降低。
Java锁底层实现
CAS(Compare and Swap 比较并交换),当多个线程尝试使用CAS同时更新同一个变量时,只有其中一个线程能更新变量的值,而其它线程都失败,失败的线程并不会被挂起,而是被告知这次竞争中失败,并可以再次尝试。