线程之间通信
- 概念:
线程是操作系统中独立的个体,但这些个体如果不经过特殊的处理就不能成为一个整体,线程间的通信就是成为整体的比用方式之一。当线程存在通信指挥,系统间的交互性会更强,在提高CPU利用率的同时还会使开发人员对线程任务处理的过程中进行有效的把控和监督。
使用wait/notify方法实现线程间的通信(这两个方法都是Ojbect的类方法,也就是说java为所有的对象都提供了这两个方法)。
- wait和notify必须配合synchronized关键字使用。
- wait方法释放锁,notify方法不释放锁。
public class ListAdd {
private volatile List<String> list = new ArrayList<>();
public void add(){
list.add("add to list");
}
public int size(){
return list.size();
}
public static void main(String[] args) {
final ListAdd listAdd =new ListAdd();
//声明一个锁
//使用wait和notify时,必须配合synchronized使用
final Object lock = new Object();
Thread t1 = new Thread(new Runnable(){
@Override
public void run() {
try{
synchronized (lock) {
for(int i = 0; i < 10; i++){
listAdd.add();
System.out.println("当前线程:" + Thread.currentThread().getName() + "添加一个元素");
Thread.sleep(500);
if(listAdd.size() == 5){
System.out.println("通知发出!");
lock.notify(); //通知t2,不释放锁,直到线程执行完毕才释放锁。
}
}
}
} catch(InterruptedException e){
e.printStackTrace();
}
}},"t1");
Thread t2 = new Thread(new Runnable(){
@Override
public void run() {
try{
synchronized (lock) {
if(listAdd.size() != 5){
lock.wait(); //释放锁
}
}
} catch (InterruptedException e){
e.printStackTrace();
}
System.out.println("当前线程:" + Thread.currentThread().getName() + "添收到通知线程停止");
throw new RuntimeException();
}},"t2");
t2.start();
t1.start();
}
}
/*
打印结果:
当前线程:t1添加一个元素
当前线程:t1添加一个元素
当前线程:t1添加一个元素
当前线程:t1添加一个元素
当前线程:t1添加一个元素
通知发出!
当前线程:t1添加一个元素
当前线程:t1添加一个元素
当前线程:t1添加一个元素
当前线程:t1添加一个元素
当前线程:t1添加一个元素
当前线程:t2添收到通知线程停止
Exception in thread "t2" java.lang.RuntimeException
at com.jt.thread02.ListAdd$2.run(ListAdd.java:59)
at java.lang.Thread.run(Unknown Source)
*/
ThreadLocal
- 概念:
线程局部变量,是一种多线程间并发访问变量的解决方案。与synchronized等加锁的方式不同,ThreadLocal完全不提供锁,而使用空间换时间的手段,为每个变量提供变量的独立副本,以保证线程安全。从性能上来说,ThreadLocal不具备绝对的优势,在并发不是很高的时候,加锁的性能会更好,但作为一套与锁安全无关的线程安全解决方案,在高并发量或者竞争激烈的场景,使用ThreadLocal可以在一定程度上减少锁竞争。
public class ConnThreadLocal {
final ThreadLocal<String> str = new ThreadLocal<>();
public void setStr(String value){
str.set(value);
}
public void getStr(){
System.out.println(Thread.currentThread().getName()+":"+this.str.get());
}
public static void main(String[] args) {
final ConnThreadLocal ctl = new ConnThreadLocal();
Thread t1 = new Thread(new Runnable(){
@Override
public void run() {
ctl.setStr("zhangsan");
ctl.getStr();//当前线程获取值
}},"t1");
Thread t2 = new Thread(new Runnable(){
@Override
public void run() {
ctl.getStr();//当前线程获取值
}},"t2");
t1.start();
t2.start();
}
}
/**
* 打印结果:
* t1:zhangsan
* t2:null
* */
单例&多线程
单例模式,最常见的就是饥饿模式和懒汉模式,一个直接实例化对象, 一个在调用方法时进行实例化对象。在多线程模式中,考虑到性能和线程安全问题,我们一般选择两种比较经典的单例模式,在性能提高的同时,有保证了线程安全。
- dubble check instance.
- static inner class.
public class DubboCheck {
private static DubboCheck dc;
public static DubboCheck getDubboCheck(){
if(null == dc){
try{
Thread.sleep(1000); //三个线程都阻塞到了这里
} catch (InterruptedException e){
e.printStackTrace();
}
synchronized (DubboCheck.class) {
if(null == dc){
dc = new DubboCheck();
}
}
}
return dc;
}
public static void main(String[] args) {
Thread t1 = new Thread(new Runnable(){
@Override
public void run() {
System.out.println(DubboCheck.getDubboCheck().hashCode());
}},"t1");
Thread t2 = new Thread(new Runnable(){
@Override
public void run() {
System.out.println(DubboCheck.getDubboCheck().hashCode());
}},"t2");
Thread t3 = new Thread(new Runnable(){
@Override
public void run() {
System.out.println(DubboCheck.getDubboCheck().hashCode());
}},"t3");
t1.start();
t2.start();
t3.start();
}
}
public class InnerClass {
private static class Singletion{
private static Singletion single = new Singletion();
}
public static Singletion getSingletion(){
return Singletion.single;
}
}