Open chengfengjie opened 6 years ago
多线程: 指的是一个程序运行时产生了不止一个线程
经常用来描述一段代码,在并发的情况之下,该代码经过多线程使用,线程的调度顺序不影响任何结果。这个时候使用多线程,我们只需要关注系统的内存、CPU是否够用即可。反过来,线程不安全就意味着线程的调度顺序会影响最终结果。
Java中的同步指的是人为的控制和调度,保证共享资源的对线程访问成为线程安全,来保证结果的准确。
在Java中可以用wait
、notify
、notifyAll
来实现线程间的通信。 我们可以用wait()
来让一个线程在某些条件下暂停运行。
在调用wait()
、notify()
、notifyAll()
的时候,必须先获得锁,且状态变量须有锁保护,而固有锁对象与固有条件队列对象又是同一个对象。也就是说,要在某个对象上执行wait
,notify
,必须先锁定该对象,而对应的状态变量也是由该对象锁保护的
执行wait
、notify
不获取锁会抛出异常 java.lang.IllegalMonitorStateException
典型的生产者和消费者的代码
package huijinhang.hms.console;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Random;
public class ProducerConsumerInJava {
public static void main(String args[]) {
ProducerConsumerInJava app = new ProducerConsumerInJava();
app.runTest();
}
void runTest() {
Queue<Integer> buffer = new LinkedList<>();
int maxSize = 10;
Thread producer = new Producer(buffer, maxSize, "PRODUCER");
Thread consumer = new Consumer(buffer, maxSize, "CONSUMER");
producer.start();
consumer.start();
}
class Producer extends Thread {
private Queue<Integer> queue;
private int maxSize;
public Producer(Queue<Integer> queue, int maxSize, String name) {
super(name);
this.queue = queue;
this.maxSize = maxSize;
}
@Override
public void run() {
while (true) {
synchronized (queue) {
while (queue.size() == maxSize) {
try {
System.out.println("Queue is full");
queue.wait();
} catch (Exception e) {
e.printStackTrace();
}
}
Random random = new Random();
int i = random.nextInt();
System.out.println("Producing value : " + i);
queue.add(i);
queue.notifyAll();
}
}
}
}
class Consumer extends Thread {
private Queue<Integer> queue;
private int maxSize;
public Consumer(Queue<Integer> queue, int maxSize, String name) {
super(name);
this.queue = queue;
this.maxSize = maxSize;
}
@Override
public void run() {
while (true) {
synchronized (queue) {
while (queue.isEmpty()) {
System.out.println("Queue is empty, Consumer thread is wait");
try {
queue.wait();
} catch (Exception e) {
e.printStackTrace();
}
}
System.out.println("Consuming value: " + queue.remove());
queue.notifyAll();
}
}
}
}
}
如果当多个线程访问同一个可变的状态变量时没有使用合适的同步,那么程序就会出现错误。有三种方式可以修复这个问题:
当设计线程安全的类时,良好的面向对象技术、不可修改性,以及明确的不可变规范都能起到一定的帮助作用。
无状态对象一定是线程安全的
大多数的Servlet都是无状态的,从而极大低降低了在实现servlet线程安全性时的复杂性。当只有Servlet处理请求时需要保存一些信息,线程安全才会成为一个问题。
在并发编程中,由于不恰当的执行时序而出现不正确的结果是一种非常重要的情况,它的正式名字就叫做:静态条件
假定有两个操作A和B,如果从执行线程A的线程来看,当另一个线程B执行时,要么将B全部执行完毕,要么完全不执行B,那么A和B对彼此来说是原子的。原子操作是指,对于访问同一个状态的所有操作(包括操作本身)来说,这个操作是一个以原子方式执行的操作
class Counter {
private long count = 0;
void increment() {
this.count ++;
}
long getCount() {
return this.count;
}
}
class CounterThread extends Thread {
private Counter counter;
public CounterThread(Counter counter) {
super();
this.counter = counter;
}
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
this.counter.increment();
}
System.out.println(counter.getCount());
}
}
public class AtomicMain {
public static void main(String args[]) {
Counter counter = new Counter();
CounterThread thread1 = new CounterThread(counter);
CounterThread thread2 = new CounterThread(counter);
thread1.start();
thread2.start();
}
}
这段程序的执行结果完全不可控
当某个线程请求一个由其他线程持有的锁时,发出请求的线程就会阻塞。由于内置锁时可重入的,如果某个线程试图获得一个由它自己持有的锁,那么这个请求就会成功。
public class Widget {
public synchronized void doSomething() {
}
}
public class LoggingWidget extend Widget {
public synchronized void doSomething() {
super.doSomething();
}
}
对于可能被多个线程同时访问的可变状态变量,在访问他时都需要持有一个锁,在这种情况下,我们称状态变量是由这个锁保护的
对于每个包含多个变量的不变性条件,其中涉及的所有变量都需要由同一个锁来保护
多线程编程中的三个核心概念
1、原子性: 一个操作要么全部执行,要么全部不执行。 2、可见性: 当多个线程并发访问共享变量时,一个线程对共享变量的修改,其他线程能够立即看到。 解释: CPU从主内存中读取数据的效率相对来说并不高,现在主流的计算机都有几级缓存。每个线程读取共享变量时,都会将变量加载进对应CPU的高速缓存里,修改变量后,CPU会立即更新该缓存,但是并不一定会立即将其写入主内存(实际上写会主内存的时间不可预期)。此时其他线程(尤其是不在同一CPU上执行的线程)访问变量时,从主内存中读到的就是旧的数据,而非第一线程更新后的数据。 3、顺序性: 程序执行的顺序按照代码的先后顺序执行