并发与多线程
1、创建线程:
/* * 创建线程: * 方法一:(1)创建类Thread1继承Thread类,重写run()方法 * (2)在main线程中创建Thread1对象,并用start * */ public class Thread1 extends Thread{ @Override public void run() { for(int i=0;i<10;i++){ System.out.println("i is :"+i+" "+Thread.currentThread().getName()); } } } public class ThreadTest { public static void main(String[] args) throws Exception { //创建线程第一种方法: for(int i=0;i<5;i++){ Thread1 thread1=new Thread1(); thread1.start(); } } }
/*第二种方法:(1)创建Thread2实现Runnable接口,并实现run()方法 (2)在主线程main中创建线程,创建Thread对象Thread thread=new Thread(new Thread2()); 调用启动方法start() * */ public class Thread2 implements Runnable{ @Override public void run() { for(int i=0;i<5;i++){ System.out.println("i is :"+" "+Thread.currentThread().getName()); } } } //创建线程第二种方法: for (int i=0;i<5;i++){ Thread thread=new Thread(new Thread2()); thread.start(); }
//创建线程第三种方法:创建Runnable内部类 Thread thread = new Thread( new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName()); } } ); thread.start();
2、实现同步
public class ThreadNum extends Thread { private static int num = 50; //没有使用同步 @Override public void run() { while (num > 0) { try{ sleep(10l); }catch (InterruptedException e){ e.printStackTrace(); } System.out.println("num is " + num + " " + Thread.currentThread().getName()); num--; } } }
结果:
num is 50 Thread-1 ....... num is 1 Thread-4 num is 0 Thread-1 num is -1 Thread-3 num is -2 Thread-2 num is -3 Thread-0
由于没有实现同步,出现了num为负数的情况。
/* * 发现出现负数的情况,说明两个以上线程进入了while(num>0)中,所以要实现同步。 * 方法1:使用同步代码块 * 语法:synchronized(同步锁){ * //需要同步操作的代码 * } * 同步锁又叫同步监听对象、同步监听器、互斥锁; * 任何时候只有一个线程能拿到同步锁; * 同步锁可以是相对于线程不变化的对象,一般情况下把当前并发访问的共同资源作为同步监听对象。 * * 方法2:使用同步方法 * * 方法3:使用锁机制 * */ //方法1:使用同步代码块 @Override public void run() { //这里使用当前对象的字节码文件作为同步锁 for (int i = 0; i < 50; i++) { synchronized (this.getClass()) { if (num > 0) { try { Thread.sleep(10l); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("num is " + num + " " + Thread.currentThread().getName()); num--; } } } } //方法2:使用同步方法 @Override public void run() { for(int i=0;i<50;i++){ numDown(); } } private synchronized void numDown() { while (num > 0) { try { Thread.sleep(10l); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("num is " + num + " " + Thread.currentThread().getName()); --num; } }
/** * 使用锁的方式实现同步 */ public class NumRunnable implements Runnable{ private static int num=50; //创建一个锁 Lock lock=new ReentrantLock(); @Override public void run(){ for(int i=0;i<50;i++){ // 获取锁 lock.lock(); try{ if(num>0){ Thread.sleep(10l); System.out.println("num is "+num+","+Thread.currentThread().getName()); num--; } Thread.sleep(10l); }catch (InterruptedException e){ e.printStackTrace(); }finally { lock.unlock(); } } } }
3、生产者和消费者
public class Person { private String name; private int age; private boolean isEmpty = true;//实例变量(单例模式下是非线程安全的),资源对象是否为空,为空则为true,表示需要生产。如果为false,则不需要生产 public synchronized void push(String name, int age) { try { while (!isEmpty) {//如果不是空的(消费者还未消费) this.wait(); } //-----生产数据开始---- this.name = name; Thread.sleep(10l); this.age = age; //-----生产数据结束---- isEmpty = false;//表示不是空的,需要消费者消费 this.notifyAll();//生产完毕,唤醒所有消费者 } catch (InterruptedException e) { e.printStackTrace(); } } public synchronized void pop() { try { while (isEmpty) {//如果是空的,则先需要生产者生产,才能消费 this.wait(); } //---消费数据开始---- Thread.sleep(10l); System.out.println("name---" + name + "," + "age---" + age); //----消费数据结束---- isEmpty = true; this.notifyAll();//消费完毕,唤醒所有生产者 } catch (InterruptedException e) { e.printStackTrace(); } } }
生产者:
public class Producer implements Runnable { private Person person; public Producer(Person person) { this.person = person; } @Override public void run() { //生产对象 for (int i = 0; i < 10; i++) { if (i % 2 == 0) { person.push("Tom", 10); System.out.println("i is " + i + " Tom"); } else { person.push("Lily", 20); System.out.println("i is " + i + " Lily"); } } } }
消费者:
public class Consumer implements Runnable { private Person person; public Consumer(Person person) { this.person = person; } @Override public void run() { //消费对象 this.person.pop(); } }
测试:
//生产者与消费者模式:生产者生产一次数据,就暂停生产者线程,等待消费者消费;消费者消费完了,消费者线程暂停,等待生产者生产数据 //同步锁池:同步锁必须选择多个线程共同的资源对象,而一个线程获得锁的时候,其他线程都在同步锁池获取锁;当那个线程释放 //同步锁后,其他线程开始由CPU调度分配锁 Person person = new Person();//多个线程共享的资源 for (int i = 0; i < 50; i++) { Thread thread = new Thread(new Producer(person)); thread.start(); Thread thread1 = new Thread(new Consumer(person)); thread1.start(); }
wait():执行该方法的线程对象,释放同步锁,JVM会把该线程放到等待池中,等待其他线程唤醒该线程
notify():执行该方法的线程唤醒在等待池中等待的任意一个线程,把线程转到锁池中等待(注意锁池和等待池的区别)
notifyAll():执行该方法的线程唤醒在等待池中等待的所有线程,把线程转到锁池中等待。
假设 A 线程和 B 线程同时操作一个 X 对象,A,B 线程可以通过 X 对象的 wait() 和 notify() 方法来进行通信,流程如下:
①、当线程 A 执行 X 对象的同步方法时,A 线程持有 X 对象的 锁,B线程在 X 对象的锁池中等待
②、A线程在同步方法中执行 X.wait() 方法时,A线程释放 X 对象的锁,进入 X 对象的等待池中
③、在 X 对象的锁池中等待锁的 B 线程获得 X 对象的锁,执行 X 的另一个同步方法
④、B 线程在同步方法中执行 X.notify() 方法,JVM 把 A 线程从等待池中移动到 X 对象的锁池中,等待获取锁
⑤、B 线程执行完同步方法,释放锁,等待获取锁的 A 线程获得锁,继续执行同步方法