并发与多线程

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 线程获得锁,继续执行同步方法

原文地址:https://www.cnblogs.com/BonnieWss/p/10439888.html