博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
多线程同步与通信
阅读量:4495 次
发布时间:2019-06-08

本文共 5024 字,大约阅读时间需要 16 分钟。

多线程:

 

 

一、同步的方法

 

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 = 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

转载于:https://www.cnblogs.com/fred1900/p/7183574.html

你可能感兴趣的文章
MySQL启动出现The server quit without updating PID file错误解决办法
查看>>
什么是多租户
查看>>
jQuery的效果
查看>>
express node 框架介绍
查看>>
adt-bundle-windows-x86-20131030
查看>>
Socket
查看>>
正则表达式之 数据验证 与 文本替换
查看>>
RPC是什么?
查看>>
CLR via C#:CLR的执行模型
查看>>
JS获取服务器时间
查看>>
如何对数据排序和拆分文件
查看>>
数据解析01-15
查看>>
linux 安装mysql数据库——yum安装法
查看>>
Several ports (8005, 80, 8009) required by Tomcat v6.0 Server at localhost are already in use
查看>>
事件监听器
查看>>
设计模式之单例设计模式
查看>>
异常的基本概念
查看>>
vue 在发送axios请求时数据渲染问题
查看>>
动态链接库dll
查看>>
2018 Multi-University Training Contest 3 - HDU Contest
查看>>