一篇文章搞定java线程

3 篇文章 0 订阅

[TOC]
线程状态转换

既然想了解多线程,线程的概念和什么是多线程想必不用多说,那首先说说线程的几个状态以及状态直接的转换,下边这张图展示了线程的几个状态,详细说说状态直接是如何转换的。
这里写图片描述
当new 一个线程它只是初始态,如果你不调用start()方法那这个线程永远都不会被执行,当调用start()方法后这个线程就变成了可运行状态,等待调度运行,调用start()不会马上进入运行状态,只是可运行,具体什么时候运行要看虚拟机的调度。
当线程被选中进入运行状态就是执行run方法所写的内容,这里说说线程同步的概念,同步就是协同步调,按预定的先后次序进行运行,由于多线程要进行线程之间的通信和其他种种原因,代码可能需要让线程在不同状态直接来回转换,下面详细介绍状态之间是如何转换的。

1.Thread.yield()方法,调用该方法会让线程之间由运行态变成就绪态,也就是主动让一下cpu资源,有可能让完之后马上又被调起,因此它不会让线程释放持有的锁。
2.Thread.sleep()方法 ,让线程进入阻塞状态,让出CPU给其他线程执行,当超出指定时间,线程进入就绪态,可以被调度运行,因此该方法不会释放持有的锁。
3.其他线程.join()方法,让其他线程加入,让本线程进入阻塞状态,在加入线程执行完成之前本线程是不会被执行的,加入线程执行完之后本线程会进入就绪态,可以被调度运行,因此该方法也不释放锁。
4.对象.wait()方法,释放锁并进入阻塞状态,调用此方法进入阻塞状态的线程会被,对象.notify(),或者notifyAll(),唤醒,唤醒的线程需要去抢占锁,只有抢占锁成功了才会进入就绪态,此方法一定要在synchronized方法或者synchronized代码块中调用。
5.notify(),notifyAll(),上面说过这两个方法就是唤醒调用wait()方法阻塞的线程。
6.当程序执行到synchronized标记的方法或代码块没有拿到锁时进入到阻塞状态,只有成功拿到锁才会转入就绪态。
7.当线程正常执行完成或异常退出则进入死亡态,这个状态的线程无法再被执行。

顺序执行问题

先来看一个实际问题 现在有T1、T2、T3三个线程,你怎样保证T2在T1执行完后执行,T3在T2执行完后执行。
看上边解释,join()会等待其他线程执行完再执行,这个可以使用,wait等待其他线程唤醒再执行,这个也可以使用。
先看join()方法:

public class MyThread implements Runnable{

	Service1 sercice1;
	public MyThread(Service1 i) {
		// TODO Auto-generated constructor stub
		this.sercice1=i;
	}
	@Override
	public void run() {
		sercice1.m1();
	}

	public static void main(String[] args) throws InterruptedException {
		Service1 service2=new Service1();
		for(int i=0;i<10;i++){
			MyThread thread=new MyThread(service2);
			MyThread2 thread2=new MyThread2(service2);
			MyThread3 thread3=new MyThread3(service2);
			Thread thread4 =new Thread(thread);
			Thread thread5=new Thread(thread2);
			Thread thread6 = new Thread(thread3);
			thread4.start();
			thread4.join();
			thread5.start();
			thread5.join();
			thread6.start();
			thread6.join();
			for(int n=0;n<100;n++){
				System.out.println("dddd");
			}
			
		}
		System.out.println("执行结束");
	}
}

public class MyThread2 implements Runnable{
	Service1 sercice1;
	public MyThread2(Service1 i) {
		// TODO Auto-generated constructor stub
		this.sercice1=i;
	}
	@Override
	public void run() {
		sercice1.m2();
	}
	
}

public class MyThread3 implements Runnable{

	Service1 sercice1;
		public MyThread3(Service1 i) {
			// TODO Auto-generated constructor stub
			this.sercice1=i;
		}
		@Override
		public void run() {
			sercice1.m3();
		}

}

public class Service1 {
	String lock="";
public void m1() {
	for(int i=0;i<100;i++)
		System.out.println("1");	
}
public void m2() {
	for(int i=0;i<100;i++)
		System.out.println("2");	
}
public void m3() {
	for(int i=0;i<100;i++)
		System.out.println("3");	
}
}

第二种wait()

public class ThreadCC extends Thread {  
	  
    private MyService service;  
  
    public ThreadCC(MyService dbtools) {  
        this.service = dbtools;  
    }  
  
    @Override  
    public void run() {  
        try {
			service.methodC();
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}  
    }  
  
    public static void main(String[] args) {  
        MyService myService = new MyService();  
       for (int i = 0; i < 2; i++) {  
            ThreadAA aa = new ThreadAA(myService);  
            ThreadBB bb = new ThreadBB(myService);  
            ThreadCC cc = new ThreadCC(myService);  
            cc.start();  
            bb.start(); 
            aa.start();
        }  
    } 
} 

public class ThreadBB extends Thread {  
	  
    private MyService service;  
  
    public ThreadBB(MyService dbtools) {  
        super();  
        this.service = dbtools;  
    }  
  
    @Override  
    public void run() {  
        try {
			service.methodB();
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}  
    }  
  
}

public class ThreadAA extends Thread {  
	  
    private MyService service;  
  
    public ThreadAA(MyService dbtools) {  
        super();  
        this.service = dbtools;  
    }  
  
    @Override  
    public void run() {  
        try {
			service.methodA();
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}  
    }  
}

public class MyService {
	private volatile int i=1;
	public  synchronized  void methodA() throws InterruptedException {
		while (i!=1) {
			wait();
		}
		for(int j=0;j<10;j++)
			System.out.println("AAA");
		i=2;
		notifyAll();
	}
	public synchronized  void methodB() throws InterruptedException {
		while (i!=2) {
			wait();
		}
		for(int j=0;j<10;j++)
			System.out.println("bbb");
		i=3;
		notifyAll();
	}
	public synchronized  void methodC() throws InterruptedException {
		while (i!=3) {
			wait();
		}
		for(int j=0;j<10;j++)
			System.out.println("ccc");
		i=1;
		notifyAll();
	}
}

还有一种在子线程内部实现join 可以自己动手试试

线程安全

当两个或以上的线程对同一个数据进行操作的时候,可能会产生“竞争条件”的现象,引发的问题就是线程安全问题,至于什么是线程安全,以及如何保证线程安全看完下边的例子就知道了  

先看一个线程不安全的例子

public class ThreadSafe implements Runnable{

	int kucun=10;
	
	int fafang=0;

	@Override
	public void run() {
		t1();
		
	}
	
	public void t1() {
		if(kucun>0){//1
			try {
				Thread.sleep(200);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			kucun--;
		    fafang++;
		}
		System.out.println("库存"+kucun);
		System.out.println("发放"+fafang);
	}
	
	public static void main(String[] args) {
		ThreadSafe safe= new ThreadSafe();
		//单线程
		/*for(int i=0;i<13;i++){
			safe.run();
		}*/
		//多线程
		for(int i=0;i<13;i++){
			Thread t1=new Thread(safe);
			t1.start();
		}
		
	}
	
}
我们期望的是当kucun为0时不再减少,单线程模式下没有问题,稳定在0就不动了,但是运行多线程版本时就会出现负数,原因就是多个线程在库存为1的时候同时完成了代码1处的判断然后线程被切换,这里用睡眠模仿,睡眠结束后继续往下执行,导致多扣多发,这就是线程不安全

保证线程安全的方法

一、最常用的保证线程安全就是使用synchronized关键字 标注方法或代码块
使用synchronized关键字标注方法即可解决
	public synchronized void  t2() {
		if(kucun>0){
			try {
				Thread.sleep(200);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			kucun--;
		    fafang++;
		}
		System.out.println("库存"+kucun);
		System.out.println("发放"+fafang);
	}
	 String lock="";
	public  void  t3() {
		synchronized (lock) {
			if(kucun>0){
				try {
					Thread.sleep(200);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				kucun--;
				fafang++;
			}
			System.out.println("库存"+kucun);
			System.out.println("发放"+fafang);
		}
	}

t2或t3两种方式都可以

二、除了synchronized还有其他方式吗?有,可以使用锁,先看看使用锁的代码。

	ReentrantLock reentrantLock=new ReentrantLock();
	public void  t4() {
		reentrantLock.lock();
		try {
			if(kucun>0){
				try {
					Thread.sleep(200);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				kucun--;
				fafang++;
			}
			System.out.println("库存"+kucun);
			System.out.println("发放"+fafang);
		} catch (Exception e) {
			// TODO: handle exception
		}finally {
			reentrantLock.unlock();
		}
	}

锁和synchronized关键字区别

使用这种方式也没有问题,那么锁和synchronized关键字有什么区别呢?
1.首先synchronized是关键字锁是接口。
2.synchronized无法得知是否成功获取对象的锁,锁是可以的。
3.synchronized无需写代码释放锁,但使用锁必须手动释放锁一般在finally()块中释放。
4.synchronized无法响应中断,lock是可以响应中断的
5.Lock可以提高多线程进行读操作的效率。
说道这先看下锁的接口中常用的定义方法

public interface Lock {
    void lock();
    void lockInterruptibly() throws InterruptedException;
    boolean tryLock();
    boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
    void unlock();
    Condition newCondition();
}

先说说例子中用的也是最常用的lock()方法,这个方法尝试获取锁如果获取不到就阻塞。
tryLock()能够得知是否成功获取锁,成功返回true,失败返回false

public void t5() {
		if(reentrantLock.tryLock()){
			try {
				if(kucun>0){
					kucun--;
					fafang++;
				}
				System.out.println("库存"+kucun);
				System.out.println("发放"+fafang);
			} catch (Exception e) {
				// TODO: handle exception
			}finally {
				System.out.println("释放锁");
				reentrantLock.unlock();
			}
		}else {
			System.out.println(Thread.currentThread().getName()+"没获取到锁");
		}
	}

boolean tryLock(long time, TimeUnit unit) 这个方式跟上一个类似,区别是获取不到锁在指定时间内会去尝试获取,超过指定时间放弃获取

public void t6() {
		try {
			if(reentrantLock.tryLock(2000l,TimeUnit.MILLISECONDS)){
				try {
					if(kucun>0){
						kucun--;
						fafang++;
					}
					System.out.println("库存"+kucun);
					System.out.println("发放"+fafang);
				} catch (Exception e) {
					// TODO: handle exception
				}finally {
					System.out.println("释放锁");
					reentrantLock.unlock();
				}
			}else {
				System.out.println(Thread.currentThread().getName()+"没获取到锁");
			}
		} catch (Exception e) {
			// TODO: handle exception
		}
	}

lockInterruptibly()通过这个方法获取到锁后如果线程被阻塞,可以调用thread.interrupt()中断线程等待,只能中断阻塞过程中的线程。

	public void t7() {
		try {
			reentrantLock.lockInterruptibly();
			
			try {
				if(kucun>0){
					kucun--;
					fafang++;
				}
				System.out.println("库存"+kucun);
				System.out.println("发放"+fafang);
				Thread.sleep(2000);
			} catch (Exception e) {
				// TODO: handle exception
			}finally {
				System.out.println("释放锁");
				reentrantLock.unlock();
			}
			
		} catch (InterruptedException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
	}
	public static void main(String[] args) {
		ThreadSafe safe= new ThreadSafe();
		Thread t1=new Thread(safe);
		t1.start();
		Thread t2=new Thread(safe);
		t2.start();
		t2.interrupt();
	}

ReentrantLock,意思是“可重入锁”,是唯一实现了Lock接口的类,例子中使用的就是这个实现。
既然说起了锁不得不说ReadWriteLock接口

public interface ReadWriteLock {

    Lock readLock();
 
    Lock writeLock();
}

它分为读锁和写锁,把读写进行了分离
先看看不分离是什么样子

public void  t8() {
		reentrantLock.lock();
		try {
				System.out.println(Thread.currentThread().getName()+"读");
				try {
					System.out.println(System.currentTimeMillis());
					Thread.sleep(2000);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			
		} catch (Exception e) {
			// TODO: handle exception
		}finally {
			reentrantLock.unlock();
		}
	}

public static void main(String[] args) {
		ThreadSafe safe= new ThreadSafe();
		//多线程
		for(int i=0;i<13;i++){
			Thread t1=new Thread(safe);
			t1.start();
		}
	}

打印结果截取一段
Thread-0读
1511940110079
Thread-1读
1511940112079
Thread-2读
1511940114079
Thread-3读
1511940116079
Thread-4读
1511940118080
都是要上一个线程释放锁才执行下一个
换成读写锁试试

ReadWriteLock readWriteLock =new ReentrantReadWriteLock();
	public void  t9() {
		readWriteLock.readLock().lock();
		try {
				System.out.println(Thread.currentThread().getName()+"读");
				try {
					System.out.println(System.currentTimeMillis());
					Thread.sleep(2000);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			
		} catch (Exception e) {
			// TODO: handle exception
		}finally {
			readWriteLock.readLock().unlock();
		}
	}

打印结果截取一段
Thread-0读
Thread-2读
1511940233999
Thread-5读
1511940233999
Thread-6读
1511940233999
Thread-1读
1511940233999
Thread-7读
差别看出来了吧
可重入锁,可中断锁,公平锁
再介绍锁的几个概念
1.可重入锁
简单说就是已经获取指定对象的锁的方法中再去调用使用了此对象锁的方法无需重新申请,synchronized与reentrantLock都是可重入的。
2.可中断锁
就是可以响应中断的锁,synchronized不可中断lock是可中断的(lockInterruptibly()方法)
3.读写锁
就是上边说的
4.公平锁
简单说就是多线程环境下会不会某个线程存在永远都无法获取锁的可能。
synchronized是非公平的,ReadWriteLock和ReentrantLock默认是非公平的,使用ReentrantLock lock = new ReentrantLock(true);创建的就是公平锁。
到目前为止什么是线程安全如何保证线程安全以及锁的概念都都清楚了。

如何实现高效缓存

经常碰到面试题在Java中Lock接口比synchronized块的优势是什么?你需要实现一个高效的缓存,它允许多个用户读,但只允许一个用户写,以此来保持它的完整性,你会怎样去实现它。
通过上边的讲解知道lock能知道是否获取锁,能够中断阻塞,还分为读写锁,能够大大的提高读的效率,实现这个搞笑缓存使用读写锁,读的操作用读锁写的操作用写锁。

实现阻塞队列(线程的通信方式)

另一个面试问题用Java实现阻塞队列
首先要知道什么是阻塞队列,说白了就是长度固定当队列满了就无法再放入,线程阻塞,直到有位置继续放才被唤醒,读取操作没有内容就阻塞等有内容再读取。
这肯定需要两方一方读一方写,那么至少需要两个线程,一个线程读一个线程写,写完如何通知读呢?读完如何通知写呢?这就涉及到了线程通信,通过上边我们了解到 wait() notify() notifyAll()可以解决两个线程的通信问题,
下边看简单的代码(wait notify 方式)

public class Queue<T> {
	int captcal;//容量
	int take;去出位置
	int put;放入位置
	Object[] object;
	Object[] EMPTY_OBJECT = {};
	int waitFlag = 1;

	public Queue(int size) throws Exception {
		if (size <= 0) {
			throw new Exception();
		} else {
			captcal = 0;
			take = 0;
			put = 0;
			object = new Object[size];
		}
	}

	public synchronized void put(T t) {
		try {
			while (captcal == object.length) {
				wait();
			}
			this.object[put] = t;
			put++;
			if (put == object.length) {
				put = 0;
			}
			// put=(put%(object.length+1));
			captcal = captcal + 1;
			notifyAll();

		} catch (Exception e) {
			// TODO: handle exception
		}
	}

	public synchronized Object get() {
		try {
			while (captcal == 0) {
				wait();

			}
			Object object = this.object[take];
			take++;
			if (take == this.object.length) {
				take = 0;
			}
			// take=take%(this.object.length+1);
			captcal = captcal - 1;
			notifyAll();
			return object;
		} catch (Exception e) {
			// TODO: handle exception
			return null;
		}
	}

	public static void main(String[] args) throws Exception {
		Queue<String> queue = new Queue<>(10);
		BlocQueueTestService service = new BlocQueueTestService(queue);
		BlockQueueTestThreadGet get = new BlockQueueTestThreadGet(service);
		BlokeQueueTestThreadPut put = new BlokeQueueTestThreadPut(service);
		Thread t1 = new Thread(get);
		Thread tw = new Thread(put);
		t1.start();
		tw.start();

	}

}


public class BlocQueueTestService {
Queue<String> queue;
public BlocQueueTestService(Queue<String> queue) {
this.queue=queue;
}
public void put() {
	for (int i = 0; i < 30; i++) {
		queue.put(i+"");
	}
}
public void get() {
	for (int i = 0; i < 30; i++) {
		System.out.println(queue.get());
	}
}
}

public class BlokeQueueTestThreadPut implements Runnable{
	public BlokeQueueTestThreadPut(BlocQueueTestService serviceui) {
		this.serviceui=serviceui;
	}
	BlocQueueTestService serviceui;
	@Override
	public void run() {
		// TODO Auto-generated method stub
		serviceui.put();
	}
	
}

public class BlockQueueTestThreadGet implements Runnable{
	BlocQueueTestService serviceui;
	public BlockQueueTestThreadGet(BlocQueueTestService serviceui) {
		// TODO Auto-generated constructor stub
		this.serviceui=serviceui;
	}
	@Override
	public void run() {
		// TODO Auto-generated method stub
		serviceui.get();
	}
	
}

Java Condition

还有其他的线程通信方式吗,当然有,就是下边要介绍的Condition;
java.util.concurrent.locks.Condition接口
它用来替代传统的Object的wait()、notify()实现线程间的协作,
Condition的强大之处在于它可以为多个线程间建立不同的Condition
Condition依赖于Lock接口,生成一个Condition的基本代码是lock.newCondition()
调用Condition的await()和signal()方法,都必须在lock保护之内,就是说必须在lock.lock()和lock.unlock之间才可以使用
常用方法
void await() throws InterruptedException;
void signalAll();

public class Queue<T> {
	int captcal;
	int take;
	int put;
	Object[] object;
	Object[] EMPTY_OBJECT = {};
	int waitFlag = 1;

	public Queue(int size) throws Exception {
		if (size <= 0) {
			throw new Exception();
		} else {
			captcal = 0;
			take = 0;
			put = 0;
			object = new Object[size];
		}
		// TODO Auto-generated constructor stub
	}

	final Lock lock = new ReentrantLock();
	final Condition write = lock.newCondition(); // 写线程锁
	final Condition read = lock.newCondition();

	public void write(T t) {
		lock.lock();
		try {
			while (captcal == object.length) {
				write.await();
			}
			this.object[put] = t;
			put++;
			if (put == object.length) {
				put = 0;
			}
			captcal = captcal + 1;
			read.signalAll();

		} catch (Exception e) {
			// TODO: handle exception
		} finally {
			lock.unlock();
		}
	}

	public Object read() {
		lock.lock();
		try {
			while (captcal == 0) {
				read.await();
			}
			Object object = this.object[take];
			take = ((take % this.object.length) + 1);
			captcal = captcal - 1;
			write.signalAll();
			return object;
		} catch (Exception e) {
			// TODO: handle exception
			return null;
		} finally {
			lock.unlock();
		}
	}

	public static void main(String[] args) throws Exception {
		/*
		 * for(int i=0;i<30;i++){ System.out.println(i%10); }
		 */
		Queue<String> queue = new Queue<>(10);
		BlocQueueTestService service = new BlocQueueTestService(queue);
		BlockQueueTestThreadGet get = new BlockQueueTestThreadGet(service);
		BlokeQueueTestThreadPut put = new BlokeQueueTestThreadPut(service);
		Thread t1 = new Thread(get);
		Thread tw = new Thread(put);
		t1.start();
		tw.start();

	}

}


public class BlocQueueTestService {
Queue<String> queue;
public BlocQueueTestService(Queue<String> queue) {
this.queue=queue;
}
public void put() {
	for (int i = 0; i < 30; i++) {
		queue.write(i+"");
	}
}
public void get() {
	for (int i = 0; i < 30; i++) {
		System.out.println(queue.read());
	}
}
}

其他代码不变
不同的条件控制不同的操作,这就是它的强大之处;
还有其他的线程通信方式吗?可以使用共享变量。
另外上边的问题换个问法就是生产者-消费者问题。

死锁

既然上边谈到了锁,那就不得不说说死锁的问题,什么是死锁,简单来说就是两个线程都持有对方需要的锁在等待对方持有的锁,下边用代码演示一下死锁

public class DeadLock {
private ReentrantLock lock1=new ReentrantLock();
private ReentrantLock lock2 =new ReentrantLock();
public void method1() {
	lock1.lock();
	try {
		System.out.println("1成功获取lock1锁");
		Thread.sleep(5000);
		lock2.lock();
		System.out.println("1成功获取两把锁");
	} catch (Exception e) {
		// TODO: handle exception
	}finally {
		lock2.unlock();
		lock1.unlock();
	}
}
public void method2() {
	lock2.lock();
	try {
		System.out.println("2成功获取lock2锁");
		Thread.sleep(5000);
		lock1.lock();
		System.out.println("2成功获取两把锁");
	} catch (Exception e) {
		// TODO: handle exception
	}finally {
		lock2.unlock();
		lock1.unlock();
	}
}
public static void main(String[] args) {
	DeadLock lock=new DeadLock();
	DeadThread1 thread1=new DeadThread1(lock);
	DeadThread2 thread2 =new DeadThread2(lock);
	Thread t1=new Thread(thread1);
	Thread t2=new Thread(thread2);
	t1.start();
	t2.start();
	
}
}

输出结果
2成功获取lock2锁
1成功获取lock1锁

线程会卡主不动等待对方持有的锁释放

解决死锁

如何避免死锁问题,常用的方式是两种
第一,再来把锁,只有先持有了那把锁才能进入各自方法。
第二,按相同顺序加锁。
第三,超时自动释放锁。

乐观锁与悲观锁

什么是乐观锁与悲观锁
乐观锁就是态度乐观的锁,每次读取数据都认为其他线程不会修改数据,它在做完操作往回写数据时通过持有的数据旧值与主存的旧值进行对比只有两者一致才会把新值写入主存,这种锁适用于读多的操作。
悲观所就是态度悲观的锁,没次取值都认为别人会修改数据,每次它都给锁住,不让其他线程使用直到它用完放回,这种锁会降低并发性,典型的就是synchronized。

原子操作

多线程中经常被问到的问题是,什么是原子操作,物理上什么是原子,就是不能再拆分的的物质单元(虽然发现了夸克还有更小的,但书上就这么说的),所以原子操作也是不能再被拆分的操作(所谓原子操作是指不会被线程调度机制打断的操作;这种操作一旦开始,就一直运行到结束,中间不会切换到其他线程 )。

volatile关键字

说到这里也不得不提一下volatile关键字
这个要想理解这个关键字还要了解一下java虚拟机的内存模型
对于共享变量每个工作线程中都持有它的副本,其中某一线程对变量的修改不会立即写到主存中,即便同步到主存中缓存也是不知道它改变了的,因此其他线程再从缓存中读取时还是之前的旧值,在基于此值的操作都是错的,volatile关键字的作用就是当有线程对共享变量做出修改时会立即同步回主内存,并通知缓存变量过期(多核cpu有多个核心,每个方法可能在不同的核心上,线程使用共享变量优先从cpu高速缓存中获取,缓存过期的意思就是让使用变量的地方从主存中获取),这样再来读取的线程读取到的值就是修改后的值了。因此可以理解为volatile关键字是变量维度的同步。

不可变对象

多线程中另一个问题,什么是不可变对象,它对写并发应用有什么帮助?
从字面意思上就能看出,不可变就是不能变,创建完啥样就是啥样,在java中就是用final修饰的,常见的就是String
由于其状态无法改变,因此无法被线程的干扰损坏或者被视为不一致状态,不可变在多线程使用的好处当然是不用考虑多线程的并发问题了,一万个线程调用这个对象效果都是一样的。

CyclicBarrier和CountdownLatch

CyclicBarrier和CountdownLatch有什么区别
CountdownLatch就是先设置一个初始值,等待线程调用CountdownLatch.await(),被等待的每个线程执行完调用countdownLatch.countDown()进行数值减一,当数值减到零时,等待线程执行。简单说就是一个线程等其他线程执行完再再行。

public class CountDownLatchTest {
	public static void main(String[] args) {
		CountDownLatch latch = new CountDownLatch(5);
		// 先启动等待线程
		new Thread(new ThreadWait(latch)).start();
		
		//启动五个线程
		for (int i = 0; i < 5; i++) {
			new Thread(new CountDownlatchTestThread(latch,i)).start();
			
		}
	}

	
}
class CountDownlatchTestThread implements Runnable{
	CountDownLatch latch;
 int i;
	public CountDownlatchTestThread(CountDownLatch l,int i) {
		this.latch = l;
		this.i=i;
	}

	@Override
	public void run() {
		System.out.println("第"+i+"次");
		System.out.println("执行一次");
		latch.countDown();
	}
}

class ThreadWait implements Runnable{
	CountDownLatch latch;

	public ThreadWait(CountDownLatch l) {
		this.latch = l;
	}

	@Override
	public void run() {
		// TODO Auto-generated method stub
		try {
			latch.await();
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println(latch.getCount()+"等待线程执行");
	}
}

CyclicBarrier可以理解为一组线程的一个等待点,比如说固定开启5个线程,每个线程都执行到某一点后停下等待,知道这组的所有线程都运行到这点时再继续向下执行。

public class CyclicBarrierTest {
public static void main(String[] args) {
	CyclicBarrier barrier=new CyclicBarrier(5);
	for(int j =0;j<5;j++){
		
		for(int i =0;i<5;i++){
			new Thread(new Thread1(barrier,i)).start();
		}
		try {
			Thread.sleep(2000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}
}
class Thread1 implements Runnable{
	int i;
	CyclicBarrier b;
	public Thread1(CyclicBarrier b,int i) {
		this.b=b;		
		this.i=i;
	}
	@Override
	public void run() {
		System.out.println(Thread.currentThread().getName()+"将要等待");

		try {
			b.await();
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (BrokenBarrierException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println("等待完成继续执行");
		
	}
	
}

上边这两个其实是闭锁很好的实现,所谓的闭锁就是没达到先决条件锁是关闭的,达到某一条件锁才打开。

jvm线程调度策略

下面再聊聊线程调度策略,通常的调度策略有抢占式与协同式,抢占式就像搞抢购活动,一点资源大家一起抢,谁抢到归谁,协同式式我执行一段时间主动让给其他线程执行。
jvm两种都有借鉴,当你调用yeild wait sleep时及时主动让行,而就绪态的线程就是抢占式的,线程还有个权重算法,谁的优先级高总的运行次数就会多点,但并不是每次一定运行优先级高的线程。


更多精彩内容请关注微信公众号 IT农厂【ITFF01】

这里写图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值