• 多线程


    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类中的原因。
  • 相关阅读:
    文件的上传
    自定义EL表达式的函数
    JSTL 自定义标签
    Java c3p0连接池之二
    Java c3p0连接池
    JSP 登录与注册的小案例
    Java jdbc 连接oracle之三(封装工具类)
    Java jdbc 连接oracle之二(使用properties文件)
    Swift中Notification.Name自定义枚举
    swift UITableViewCell 策划删除,iOS11之后 设置侧滑不到最左边
  • 原文地址:https://www.cnblogs.com/yaowen/p/9313327.html
Copyright © 2020-2023  润新知