首页 > 代码库 > 多线程同步与通信
多线程同步与通信
多线程:
一、同步的方法
1、synchronized
合理利用资源,提高效率最有效的方法。带来这些有利的一面的同时,也为开发者带来了一些烦扰,如数据不一致,会导致严重的后果,目前使用最多的就是通过synchronized来实现数据的同步,从以下几个方面介绍synchronized:
要解决多线程并发的问题,就是通过排队的方式,一个一个来,如果方法或代码块加上了synchronized,就等于获取了锁,其他线程只能等待方法或代码块被执行后锁被释放,等待的线程获取到锁,才能执行方法或代码块里面的代码。
与synchronized密不可分的是锁,锁有类锁和对象锁,对象锁与类锁各自独立,互不干扰,会出现下面几种情况
1.1、在static上面的synchronized可以获取到类锁,当有一个方法获取到类锁时,其他加了static synchronized的方法必须要等待类锁被释放才能获取到类锁,并执行里面的方法
private static synchronized void doA() throws Exception {
Thread.sleep(1000 * 3);// 休眠3秒
System.out.println("A sleep finish");
}
private static synchronized void doB() {
System.out.println("B sleep finish");
}
private static void report(String name) {
System.out.println(name + " start sleep");
}
public static void main(String[] args) throws InterruptedException {
new Thread(new Runnable() {
public void run() {
try {
report("A");
doA();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
new Thread(new Runnable() {
public void run() {
report("B");
doB();
}
}).start();
}
结果:
A start sleep
B start sleep
A sleep finish
B sleep finish
1.2、获取这个对象锁,其他方法或代码块必须要等到这个对象锁释放后才能获取这个对象锁,执行后面的代码。
1.3、获取同一个对象锁时,不同的线程需要等待,但不同对象获取不同的对象锁时,互不影响
final Object p1 = new Object();
final Object p2 = new Object();
new Thread(new Runnable() {
public void run() {
synchronized (p1) {
System.out.println(Thread.currentThread().getName() + "获取P1对象锁");
try {
Thread.sleep(1000 * 3);
System.out.println(Thread.currentThread().getName() + "释放P1对象锁");
} catch (Exception e) {
e.printStackTrace();
}
}
}
}).start();
new Thread(new Runnable() {
public void run() {
synchronized (p1) {
System.out.println(Thread.currentThread().getName() + "获取P1对象锁");
}
}
}).start();
输出:
Thread-0获取P1对象锁
Thread-0释放P1对象锁
Thread-1获取P1对象锁
将下面的线程中的p1改成p2时,输出如下
Thread-0获取P1对象锁
Thread-1获取P2对象锁
Thread-0释放P1对象锁
2.原子变量
脏读:数据不一致
volatile关键字:新的线程会拷贝一份副本到自己的内存中,修改的与主线程公用的变量并不会改变主线程中的变量的值,只有在变量前面加上volatile关键字,迫使线程去主线程中的内存中获取或修改变量的值,可以保证变量的可见性,但它并不具备原子性,也是不安全的
同步方法
2.1.加锁
2.2.使用原子变量
代码:
public volatile static int volatileCount = 0;
public static int syncCount = 0;
public static AtomicInteger autoCount = new AtomicInteger(0);
public static void inc() {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
volatileCount++;
syncInc();
autoInc();
}
private static void autoInc() {
autoCount.incrementAndGet();
}
private static synchronized void syncInc() {
syncCount++;
}
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 50; i++) {
Thread t = new Thread(new Runnable() {
public void run() {
VolatileTest.inc();
}
});
t.start();
}
Thread.sleep(1000);
System.out.println("运行结果:Counter.volatileCount=" + VolatileTest.volatileCount);
System.out.println("运行结果:Counter.syncCount=" + VolatileTest.syncCount);
System.out.println("运行结果:Counter.autoCount=" + VolatileTest.autoCount);
}
输出结果:
运行结果:Counter.volatileCount=45
运行结果:Counter.syncCount=50
运行结果:Counter.autoCount=50
3.ThreadLocal
ThreadLocal为并发提供另一种解决方案,通过空间换取时间,为每一个线程提供一个单独空间
代码:
public class ThreadLocalTest {
private static ThreadLocal<String> threadLocal = new ThreadLocal<String>();
public static void setValue(String value) {
threadLocal.set(value);
}
public static String getValue() {
return threadLocal.get();
}
public static void main(String[] args) {
new Thread(new Runnable() {
public void run() {
setValue("fred");
System.out.println("threadLocal=" + getValue());
}
}, "t1").start();
new Thread(new Runnable() {
public void run() {
System.out.println("threadLocal=" + getValue());
}
}, "t2").start();
}
}
结果:
threadLocal=fred
threadLocal=null
二、线程之间的通信
wait/notify
1、必须用同步关键字
2、wait释放锁 notify不释放锁,notify后不会马上执行wait线程的代码,只有notify所在线程执行完后,才会执行wait线程的代码
代码(模拟阻塞队列):
public final static LinkedList<String> quene = new LinkedList<String>();
public final static Object lock = new Object();
public final static int QUENE_SIZE = 5;
public final static int QUENE_MIN = 0;
public final static AtomicInteger count = new AtomicInteger(0);
public static void main(String[] arg) {
final MyQueue quene = new MyQueue();
for (int i = 0; i < 10; i++) {
new Thread(new Runnable() {
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
quene.put(count.getAndIncrement() + "");
}
}).start();
new Thread(new Runnable() {
public void run() {
quene.get();
}
}).start();
}
}
public int getSize() {
return quene.size();
}
public String get() {
synchronized (lock) {
while (QUENE_MIN == getSize()) {
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
String value = http://www.mamicode.com/quene.get(QUENE_MIN);
quene.remove(QUENE_MIN);
System.out.println("移除" + value);
lock.notify();
return value;
}
}
public void put(String value) {
synchronized (lock) {
while (QUENE_SIZE == getSize()) {
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
quene.add(getSize(), value);
System.out.println("增加" + value);
lock.notify();
}
}
结果:
增加1
移除1
增加0
增加2
增加3
移除0
移除2
移除3
增加5
增加6
增加7
增加9
移除5
移除6
增加8
移除7
移除9
移除8
增加4
移除4
多线程同步与通信