多线程

 

class MyThread extends Thread {
    
    public MyThread(String name) {
        super(name);
    }

    @Override
    public void run() {
        synchronized(this) {
            try {  
                for (int i = 0; i < 5; i++) {
                    Thread.sleep(100); // 休眠100ms
                    System.out.println(Thread.currentThread().getName() + " loop " + i);  
                }
            } catch (InterruptedException ie) {  
            }
        }  
    }
}

public class Demo1_2 {

    public static void main(String[] args) {  
//2个线程处理同2个任务 Thread t1
= new MyThread("t1"); // 新建“线程t1” Thread t2 = new MyThread("t2"); // 新建“线程t2” t1.start(); // 启动“线程t1” t2.start(); // 启动“线程t2” } }

 

class MyRunable implements Runnable {
    
    @Override
    public void run() {
        synchronized(this) {
            try {  
                for (int i = 0; i < 5; i++) {
                    Thread.sleep(100); // 休眠100ms
                    System.out.println(Thread.currentThread().getName() + " loop " + i);  
                }
            } catch (InterruptedException ie) {  
            }
        }  
    }
}

public class Demo1_1 {

    public static void main(String[] args) {  
        Runnable demo = new MyRunable();     // 新建“Runnable对象”
        //2个线程处理同一个任务
        Thread t1 = new Thread(demo, "t1");  // 新建“线程t1”, t1是基于demo这个Runnable对象
        Thread t2 = new Thread(demo, "t2");  // 新建“线程t2”, t2是基于demo这个Runnable对象
        t1.start();                          // 启动“线程t1”
        t2.start();                          // 启动“线程t2” 
    } 
}

 

class Count {

    // 含有synchronized同步块的方法
    public void synMethod() {
        synchronized(this) {
            try {  
                for (int i = 0; i < 5; i++) {
                    Thread.sleep(100); // 休眠100ms
                    System.out.println(Thread.currentThread().getName() + " synMethod loop " + i);  
                }
            } catch (InterruptedException ie) {  
            }
        }  
    }

    // 非同步的方法
    public void nonSynMethod() {
        try {  
            for (int i = 0; i < 5; i++) {
                Thread.sleep(100);
                System.out.println(Thread.currentThread().getName() + " nonSynMethod loop " + i);  
            }
        } catch (InterruptedException ie) {  
        }
    }
}

public class Demo2 {

    public static void main(String[] args) {  
        final Count count = new Count();
        // 新建t1, t1会调用“count对象”的synMethod()方法
        Thread t1 = new Thread(
                new Runnable() {
                    @Override
                    public void run() {
                        count.synMethod();
                    }
                }, "t1");

        // 新建t2, t2会调用“count对象”的nonSynMethod()方法
        Thread t2 = new Thread(
                new Runnable() {
                    @Override
                    public void run() {
                        count.nonSynMethod();
                    }
                }, "t2");  


        t1.start();  // 启动t1
        t2.start();  // 启动t2
    } 
}

 

实例锁 -- 锁在某一个实例对象上。如果该类是单例,那么该锁也具有全局锁的概念。
               实例锁对应的就是synchronized关键字。
全局锁 -- 该锁针对的是类,无论实例多少个对象,那么线程都共享该锁。
               全局锁对应的就是static synchronized(或者是锁在该类的class或者classloader对象上)。

关于“实例锁”和“全局锁”有一个很形象的例子:

pulbic class Something {
    public synchronized void isSyncA(){}
    public synchronized void isSyncB(){}
    public static synchronized void cSyncA(){}
    public static synchronized void cSyncB(){}
}

假设,Something有两个实例x和y。分析下面4组表达式获取的锁的情况。
(01) x.isSyncA()与x.isSyncB() 
(02) x.isSyncA()与y.isSyncA()
(03) x.cSyncA()与y.cSyncB()
(04) x.isSyncA()与Something.cSyncA()

 (01) 不能被同时访问。因为isSyncA()和isSyncB()都是访问同一个对象(对象x)的同步锁!

(02) 可以同时被访问。因为访问的不是同一个对象的同步锁,x.isSyncA()访问的是x的同步锁,而y.isSyncA()访问的是y的同步锁。

(03) 不能被同时访问。因为cSyncA()和cSyncB()都是static类型,x.cSyncA()相当于Something.isSyncA(),y.cSyncB()相当于Something.isSyncB(),因此它们共用一个同步锁,不能被同时反问。

(04) 可以被同时访问。因为isSyncA()是实例方法,x.isSyncA()使用的是对象x的锁;而cSyncA()是静态方法,Something.cSyncA()可以理解对使用的是“类的锁”。因此,它们是可以被同时访问的。

 

在Object.java中,定义了wait(), notify()和notifyAll()等接口。wait()的作用是让当前线程进入等待状态,同时,wait()也会让当前线程释放它所持有的锁。而notify()和notifyAll()的作用,则是唤醒当前对象上的等待线程;notify()是唤醒单个线程,而notifyAll()是唤醒所有的线程。

Object类中关于等待/唤醒的API详细信息如下:
notify()                        -- 唤醒在此对象监视器上等待的单个线程。
notifyAll()                     -- 唤醒在此对象监视器上等待的所有线程。
wait()                          -- 让当前线程处于“等待(阻塞)状态”,“直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法”,当前线程被唤醒(进入“就绪状态”)。
wait(long timeout)              -- 让当前线程处于“等待(阻塞)状态”,“直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者超过指定的时间量”,当前线程被唤醒(进入“就绪状态”)。
wait(long timeout, int nanos)   -- 让当前线程处于“等待(阻塞)状态”,“直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者其他某个线程中断当前线程,或者已超过某个实际时间量”,当前线程被唤醒(进入“就绪状态”)。

 

package com.test;
// WaitTest.java的源码
class ThreadA extends Thread{

    public ThreadA(String name) {
        super(name);
    }

    public void run() {
        System.out.println("1111");
        synchronized (this) {
            System.out.println(Thread.currentThread().getName()+" call notify()");
            // 唤醒当前的wait线程
            notify();
            System.out.println("2222");
        }
    }
}

public class WaitTest {

    public static void main(String[] args) {

        ThreadA t1 = new ThreadA("t1");

        synchronized(t1) {
            try {
                System.out.println(Thread.currentThread().getName()+" start t1");
                t1.start();  //main线程和新线程用的是同一个锁t1,start方法调用后,run方法不能进去,因为锁被main线程占用。

                System.out.println(Thread.currentThread().getName()+" wait()");
                t1.wait();  //锁调用wait方法,让正在使用锁t1的main线程等待,wait方法让当前线程释放锁,run方法进去,notify()唤醒等待的main线程,synchronized执行完之后释放锁,main才可以继续走下去。

                System.out.println(Thread.currentThread().getName()+" continue");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

/*
 t1.wait()应该是让“线程t1”等待;但是,为什么却是让“主线程main”等待了呢?
 注意:jdk的解释中,说wait()的作用是让“当前线程”等待,而“当前线程”是指正在cpu上运行的线程!
这也意味着,虽然t1.wait()是通过“线程t1”调用的wait()方法,但是调用t1.wait()的地方是在“主线程main”中。而主线程必须是“当前线程”,也就是运行状态,才可以执行t1.wait()。
所以,此时的“当前线程”是“主线程main”!因此,t1.wait()是让“主线程”等待,而不是“线程t1”!
 */
package com.test;
public class WaitNotifyDemo1 {
    private int num; //输出数字
    private int runThreadNum; //当前运行线程编号
     
    public WaitNotifyDemo1(int num, int runThreadNum){
        this.num = num;
        this.runThreadNum = runThreadNum;
    }
     
     
    /**
     * 打印线程
     */
    static class PrintThread extends Thread{
        private int threadNum; //当前运行线程编号
        private WaitNotifyDemo1 demo; //锁对象
         
        public PrintThread(int threadNum, WaitNotifyDemo1 demo){
            this.threadNum = threadNum;
            this.demo = demo;
        }
         
        @Override
        public void run() {
            synchronized (demo) {  // 这里要同步,不然后面demo.wait()就会报错
                try{
                    for(int i=1; i<=5; i++){
                        while(true){
                            if(threadNum == demo.runThreadNum){
                                break;
                            }
                            else{
                                //如果当前线程不是接下来要运行的线程,进入等待池
                                demo.wait(); 
                            }
                        }
                         
                        for(int j=1; j<=5; j++){
                            System.out.println("线程"+threadNum+":"+(++demo.num));
                        }
                         
                        demo.runThreadNum = demo.runThreadNum%3 +1; //计算之后运行的线程编号
                        demo.notifyAll(); //唤醒所有等待池中的线程
                    }
                }
                catch(Exception e){
                    e.printStackTrace();
                }
            }
        }
    }
     
     
    public static void main(String[] args) {
        WaitNotifyDemo1 demo = new WaitNotifyDemo1(0,1);
         // 3个线程处理同一个任务
        new PrintThread(1,demo).start();
        new PrintThread(2,demo).start();
        new PrintThread(3,demo).start();
    }
}

启动3个线程打印递增的数字, 线程1先打印1,2,3,4,5, 然后是线程2打印6,7,8,9,10, 然后是线程3打印11,12,13,14,15. 接着再由线程1打印16,17,18,19,20....以此类推, 直到打印到75. 

public class Print {
 
//3个线程处理同一个任务,

    public static void main(String[] args) {
        new Thread(new PrintRunnable(1)).start();
        new Thread(new PrintRunnable(2)).start();
        new Thread(new PrintRunnable(3)).start();
    }
}
class PrintRunnable implements Runnable {
 
    private static volatile int printNum = 0;
    private int threadId;
 
    public PrintRunnable(int threadId){
        this.threadId = threadId;
    }
    @Override
    public void run() {
            while(printNum < 75){
                synchronized (Print.class){
                    if (printNum/5%3 + 1 == threadId){
                        for (int i = 0; i <5; i++) {
                            System.out.println("线程"+threadId+":"+(++printNum));
                        }
                        Print.class.notifyAll();
                    }else {
                        try {
                            Print.class.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
 
                }
        }
    }
}

 

package com.test;
public class NotifyAllTest {

    private static Object obj = new Object();
    public static void main(String[] args) {

        ThreadA t1 = new ThreadA("t1");
        ThreadA t2 = new ThreadA("t2");
        ThreadA t3 = new ThreadA("t3");
        t1.start();
        t2.start();
        t3.start();

        try {
            System.out.println(Thread.currentThread().getName()+" sleep(3000)");
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        synchronized(obj) {
            // 主线程等待唤醒。
            System.out.println(Thread.currentThread().getName()+" notifyAll()");
            obj.notifyAll(); //喚醒
        }
    }

    static class ThreadA extends Thread{

        public ThreadA(String name){
            super(name);
        }

        public void run() {
            synchronized (obj) {
                try {
                    // 打印输出结果
                    System.out.println(Thread.currentThread().getName() + " wait");

                    // 唤醒当前的wait线程
                    obj.wait();   //线程要执行,1要被唤醒,2要获取锁,不然仍然处于wait的阻塞状态
  
                    // 打印输出结果
                    System.out.println(Thread.currentThread().getName() + " continue");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

 

5. 为什么notify(), wait()等函数定义在Object中,而不是Thread中

Object中的wait(), notify()等函数,和synchronized一样,会对“对象的同步锁”进行操作。

wait()会使“当前线程”等待,因为线程进入等待状态,所以线程应该释放它锁持有的“同步锁”,否则其它线程获取不到该“同步锁”而无法运行!
OK,线程调用wait()之后,会释放它锁持有的“同步锁”;而且,根据前面的介绍,我们知道:等待线程可以被notify()或notifyAll()唤醒。现在,请思考一个问题:notify()是依据什么唤醒等待线程的?或者说,wait()等待线程和notify()之间是通过什么关联起来的?答案是:依据“对象的同步锁”。

负责唤醒等待线程的那个线程(我们称为“唤醒线程”),它只有在获取“该对象的同步锁”(这里的同步锁必须和等待线程的同步锁是同一个),并且调用notify()或notifyAll()方法之后,才能唤醒等待线程。虽然,等待线程被唤醒;但是,它不能立刻执行,因为唤醒线程还持有“该对象的同步锁”。必须等到唤醒线程释放了“对象的同步锁”之后,等待线程才能获取到“对象的同步锁”进而继续运行。

总之,notify(), wait()依赖于“同步锁”,而“同步锁”是对象锁持有,并且每个对象有且仅有一个!这就是为什么notify(), wait()等函数定义在Object类,而不是Thread类中的原因。

 

posted @ 2018-07-15 13:21  无天666  阅读(185)  评论(0编辑  收藏  举报