1. 多线程编程

2. Thread和Runnable
java中实现多线程的方式有两种,继承Thread类、实现Runnable接口
2.1 Thread
开发人员可以编写一个类继承Thread,并重写run方法,在run方法里面编写线程将要执行的代码。
创建线程对象后,只需要调用start()方法即可让线程进入就绪队列,等待操作系统调度。
需要特别注意的是调度具有随机性和随时性。也就是说无法确定下一次调度哪个线程,也无法确定什么时刻进行调度
public class MyThreadTest {
public static void main(String[] args) {
MyThread myThread = new MyThread();
myThread.start();//把myThread加入到就绪队列里面
}
}
class MyThread extends Thread {
@Override
public void run() {
System.out.println("MyThread执行了");
}
}
2.2 Runnable
除了继承Thread重写run方法外,在简单的情况下,还可通过实现Runnable接口的方式编写线程执行的代码
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Runnable接口方式实现多线程");
}
});
3.线程安全问题
一个数据,如一个对象或对象中的某个字段,如果有多个线程可以同时访问它,就可能会出现线程安全问题:数据错乱、程序出错或其他无法预知的问题
比如线程1要遍历一个list,线程2要把这个list清空,如果这两个线程同时执行就可能会出现线程安全问题
public class ThreadQuestionTest {
public static void main(String[] args) {
List<Integer> list = new ArrayList<Integer>();
for (int i = 0; i < 100000; i++) {
list.add(i);
}
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
}
});
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
list.clear();
}
});
thread1.start();
thread2.start();
}
}
输出:
0
null
null
null
null
...
4. 线程同步
线程同步控制,即使用某种方式使得一个线程在操作完某个数据前,别的线程无法操作这个数据,从而避免多个线程同时操作一个数据,进而避免线程安全问题
线程同步控制的方式有同步锁机制、等待/通知机制、信号量机制等,它们应用在不同复杂度的场景下
4.1同步代码块
synchronized同步锁机制
Java中每个对象都有一把锁,同一时刻只能有一个线程持有这把锁。线程可以使用synchronized关键字向系统申请某个对象的锁,得到锁之后,别的线程再申请该锁时,就只能等待。持有锁的线程在这次操作完成后,可以释放锁,以便其他线程可以获得锁
synchronized有两种形式,synchronized代码块和synchronized方法
synchronized代码块,又称同步代码块:
public class SynchronizedBlockTest {
public static void main(String[] args) {
List<Integer> list = new ArrayList<Integer>();
for (int i = 0; i < 100000; i++) {
list.add(i);
}
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
synchronized (list) {
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
}
}
});
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
synchronized (list) {
list.clear();
}
}
});
thread1.start();
thread2.start();
}
}
View Code
4.2 同步方法
public class SynchronizedMethodTest {
public static void main(String[] args) {
Data data = new Data();
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
data.bianliList();
}
});
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
data.clearList();
}
});
thread1.start();
thread2.start();
}
}
class Data {
private List<Integer> list;
public Data() {
list = new ArrayList<Integer>();
for (int i = 0; i < 100000; i++) {
list.add(i);
}
}
public synchronized void bianliList() {
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
}
public synchronized void clearList() {
list.clear();
}
}
View Code
非静态同步方法申请的锁是类的当前对象的锁,静态同步方法申请的锁是类的Class对象的锁。同步方法执行完后即向系统归还锁
synchronized代码块和synchronized方法的效果一样,可根据具体场景灵活选用
对于简单的需要线程同步控制的应用场景,synchronized基本够用
但需要注意,所有需要同步的线程必须都申请同一个对象的锁,当申请不同的锁或者有的线程没有使用synchronized时,同步锁机制就会失效
5. wait/notify 等待/通知机制
对于稍复杂的情况,比如多个线程需要相互合作有规律的访问共享数据,就可以使用wait/notify机制,即等待/通知机制,也称等待/唤醒机制
等待/通知机制建立在synchronized同步锁机制的基础上,即在同步代码块(或同步方法)内,如果当前线程执行了lockObject.wait()(lockObject表示提供锁的对象),则当前线程立即暂停执行,并被放入阻塞队列,并向系统归还所持有的锁,并在lockObject上等待,直到别的线程调用lockObject.notify()
如果有多个线程在同一个对象上等待,notify()方法只会随机通知一个等待的线程,也可以使用notifyAll()方法通知所有等待的线程。被通知的线程获得锁后会进入就绪队列
public class WaitNotifyTest {
public static void main(String[] args) {
Object lockObject = new Object();
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
synchronized (lockObject) {
try {
System.out.println("线程1即将开始在lockObject上等待");
lockObject.wait();
System.out.println("线程1收到通知并获得锁,开始继续执行");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
synchronized (lockObject) {
System.out.println("线程2将随机通知在lockObject上等待的线程");
lockObject.notify();
}
}
});
thread2.start();
thread1.start();
}
}
View Code
5.1 wait/notify-生产者消费者实例
一个很典型的生产者消费者例子:现有一个生产者、一个消费者、10个盘子(缓冲区),生产者把生产的产品放入空盘子中,当没有空盘子时就停止生产;消费者消费盘子中的产品,当所有的盘子都是空盘子时就停止消费
public class ProducerConsumerTest{
public static void main(String[] args) {
List<Integer> buffer = new LinkedList<Integer>();
int maxSize = 10;
Producer producer = new Producer(buffer, maxSize);
Consumer consumer = new Consumer(buffer);
producer.start();
consumer.start();
}
}
//模拟生产者
class Producer extends Thread {
private List<Integer> buffer; //缓冲区,表示多个盘子
private int maxSize; //表示盘子个数
public Producer(List<Integer> buffer, int maxSize) {
this.buffer = buffer;
this.maxSize = maxSize;
}
@Override
public void run() {
int id = 0;
while (true) {
synchronized (buffer) {
if (buffer.size() < maxSize) {//有空盘子则继续生产
id++;//表示生产了一个产品
buffer.add(id); //表示把产品放入一个空盘子中
System.out.println("生产产品" + id + "并通知消费者可以消费了");
buffer.notify(); //通知消费者有产品可以消费了
} else {
//如果没有空盘子则等待
System.out.println("没有空盘子了,生产者停止生产产品");
try {
buffer.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}
//模拟消费者
class Consumer extends Thread {
private List<Integer> buffer; //缓冲区,表示多个盘子
public Consumer(List<Integer> buffer) {
this.buffer = buffer;
}
@Override
public void run() {
while (true) {
synchronized (buffer) {
if (buffer.size() > 0) { //有不空的盘子
int id = buffer.remove(0); //表示消费了一个产品
System.out.println("消费产品" + id + "并通知生产者有空盘了");
buffer.notify();
} else {
//全部都是空盘子则等待
System.out.println("全部都是空盘子,消费者停止消费产品");
try {
buffer.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}
View Code
转载于:https://www.cnblogs.com/renjing/p/thread.html