• java多线程系列(1)


    1,为什么需要线程?

    作用:提升cpu的利用率,如,早期的dos系统,执行2个命令时( command 1, command 2 ),如果command1【假如是磁盘遍历文件的IO操作】执行的时间比较长,那么command 2必须等待,这种方式就是同步阻塞,

    cpu就闲置了,为了提高cpu的利用率,我们就要使用多线程,如果一个任务时间比较长,cpu就暂时挂起他,去执行另外的线程,所以线程一般是异步的。

    2,每一个进程至少会有一个线程在运行

    public class Test {
    
        public static void main(String[] args) {
            //打印线程的名称
            System.out.println( Thread.currentThread().getName() );
            
        }
    
    }

    输出结果为 "main" ,注意这个main是线程的名字,跟main函数的名字相同而已。

    3,在java中实现多线程有2种方式

    >继承Thread类

    >实现Runnable接口

    在run方法中写线程要执行的任务

    class MyThread extends Thread{
        public void run(){
            System.out.println( "MyThread::run" );
        }
    }
    
    public class ThreadUse1 {
    
        public static void main(String[] args) {
            MyThread mt = new MyThread();
            mt.start();
            System.out.println( "运行结束" );
        }
    
    }

    从运行结果可知,run方法是在之后执行的,虽然start开启线程比  【System.out.println( "运行结束" );】 他早,这说明,CPU在调用线程的时候,是随机的

    4,再次验证cpu调用线程的随机性

    class MyThreadRand extends Thread{
        public void run(){
            try{
                for ( int i = 0; i < 10; i++ ) {
                    int time = ( int )( Math.random() * 1000 );
                    Thread.sleep( time );
                    System.out.println( "MyThread:" + Thread.currentThread().getName() );
                }
            }catch( InterruptedException e ){
                e.printStackTrace();
            }
        }
    }
    
    public class RandThread {
    
        public static void main(String[] args) {
            
            try{
                MyThreadRand mt = new MyThreadRand();
                mt.setName( "自定义线程" );
                mt.start();
                for ( int i = 0; i < 10; i++ ) {
                    int time = ( int )( Math.random() * 1000 );
                    Thread.sleep( time );
                    System.out.println( "MainThread:" + Thread.currentThread().getName() );
                }
            }catch( InterruptedException e ){
                e.printStackTrace();
            }
        }
    
    }

    从执行结果可知,线程的调度没有什么规律,是随机的, 这里补充一点,start方法作用是通知 “线程规划器”,这个线程已经准备好了,等待调用线程的run方法,就是让系统安排一个时间来调用run方法。如果直接调用run方法,线程就变成同步方式了,必须等待MyThreadRand的run方法执行完成之后,才会执行main函数中的线程

    5,start方法的顺序,不代表线程的启动顺序

    class MyThreadStart extends Thread{
        private int i;
        public MyThreadStart( int i ) {
            this.i = i;
        }
        public void run(){
            System.out.println( i );
        }
    }
    
    public class RandThread2 {
    
        public static void main(String[] args) {
            MyThreadStart s1 = new MyThreadStart( 1 );
            MyThreadStart s2 = new MyThreadStart( 2 );
            MyThreadStart s3 = new MyThreadStart( 3 );
            MyThreadStart s4 = new MyThreadStart( 4 );
            MyThreadStart s5 = new MyThreadStart( 5 );
            MyThreadStart s6 = new MyThreadStart( 6 );
            MyThreadStart s7 = new MyThreadStart( 7 );
            MyThreadStart s8 = new MyThreadStart( 8 );
            MyThreadStart s9 = new MyThreadStart( 9 );
            MyThreadStart s10 = new MyThreadStart( 10 );
            
            s1.start();
            s2.start();
            s3.start();
            s4.start();
            s5.start();
            s6.start();
            s7.start();
            s8.start();
            s9.start();
            s10.start();
        }
    
    }

    6,实现Runnable接口

    class MyThreadRunnable implements Runnable {
        public void run(){
            System.out.println( Thread.currentThread().getName() );
        }
    }
    
    public class ThreadRunnable {
    
        public static void main(String[] args) {
    
            MyThreadRunnable mt = new MyThreadRunnable();
            Thread t = new Thread( mt );
            t.setName( "自定义线程1" );
            t.start();
        }
    
    }

    那么两种多线程的实现方式,有什么不同呢?

    >继承Thread类

    >实现Runnable接口

    1,使用继承Thread类的方式,多线程之间的数据不共享

    class MyThreadShare extends Thread{
        private int count = 5;
        public MyThreadShare( String name ){
            this.setName( name );
        }
        public void run(){
            while( count-- > 0 ){
                System.out.println( Thread.currentThread().getName() + "->" + count );
            }
        }
    }
    
    public class ThreadShare {
        public static void main(String[] args) {        
            MyThreadShare mt1 = new MyThreadShare( "A" );
            MyThreadShare mt2 = new MyThreadShare( "B" );
            MyThreadShare mt3 = new MyThreadShare( "C" );
            
            mt1.start();
            mt2.start();
            mt3.start();
        }
    }

    2,而要想实现线程之间的数据共享,我们可以改一下

    备注:线程数据共享与不共享,都有对应的场景,比如火车站4个窗口卖票,很显然需要线程共享数据。如:总共用10张票,如果窗口卖了1张,其他窗口就指剩下9张,这才是比较贴近实际的,如果用第一种方式,相当于有40张余票了。

    class MyThreadShare2 extends Thread{
        private int count = 5;    
        public void run(){
            while( count-- > 0 ){
                System.out.println( Thread.currentThread().getName() + "->" + count );
            }
        }
    }
    
    public class ThreadShare2 {
        public static void main(String[] args) {        
            
            MyThreadShare2 mt = new MyThreadShare2();
            Thread ta = new Thread( mt, "A" );
            Thread tb = new Thread( mt, "B" );
            Thread tc = new Thread( mt, "C" );
            
            ta.start();
            tb.start();
            tc.start();
            
        }
    }

    从结果上看,好像实现了,数据共享,但是有点异常,B->3 很明显不对,这种现象,在多线程编程里面,叫“线程非安全”。现实生活中也有类似场景,比如4S店卖车,两个客户同时预订了这辆车。那估计少不了一番辩论。怎么解决这个问题呢?一般来说,在客户订车之前,销售员要先查看库存,如果客户下单,要把库存占用。表明有人预订,其他销售员看见了,就知道车被预订了。程序中也是类似。如果要访问这个变量,我们就给他加锁,类似于销售员占用库存。在方法前加上synchronized关键字。那么其他线程访问的时候,必须拿到这把锁,才能访问。synchronized可以在任意对象或者方法上加锁。

    class MyThreadShare2 extends Thread{
        private int count = 5;    
    //    public void run(){  //产生线程非安全问题
        synchronized public void run(){
            while( count-- > 0 ){
                System.out.println( Thread.currentThread().getName() + "->" + count );
            }
        }
    }
    
    public class ThreadShare2 {
        public static void main(String[] args) {        
            
            MyThreadShare2 mt = new MyThreadShare2();
            Thread ta = new Thread( mt, "A" );
            Thread tb = new Thread( mt, "B" );
            Thread tc = new Thread( mt, "C" );
            
            ta.start();
            tb.start();
            tc.start();
            
        }
    }

     3,模拟用户登录场景,如果有两个用户登录,我们让其中一个用户线程占时挂起。看下会出现什么情况

    class Login {
        private static String userName;
        private static String userPwd;
        
        public static void doPost( String _userName, String _userPwd ){
            try {
                userName = _userName;
                if( userName.equals( "ghostwu" ) ) {
                    Thread.sleep( 3000 );                
                }
                userPwd = _userPwd;
                System.out.println( userName + "---->" + userPwd );
            }catch( InterruptedException e ){
                e.printStackTrace();
            }
        }
    }
    
    class ThreadA extends Thread{
        public void run(){
            Login.doPost( "ghostwu", "abc123" );
        }
    }
    
    class ThreadB extends Thread{
        public void run(){
            Login.doPost( "ghostwuB", "abc1234" );
        }
    }
    
    public class UserLogin {
    
        public static void main(String[] args) {
            
            ThreadA ta = new ThreadA();
            ThreadB tb = new ThreadB();
            
            ta.start();
            tb.start();
        }
    
    }

    在A线程挂起的时候,他之前的赋值已经被B线程改变了,所以结果与预想的ghostwu  abc123不同。很明显,我们要上锁。

    synchronized public static void doPost( String _userName, String _userPwd ){
            try {
                userName = _userName;
                if( userName.equals( "ghostwu" ) ) {
                    Thread.sleep( 3000 );                
                }
                userPwd = _userPwd;
                System.out.println( userName + "---->" + userPwd );
            }catch( InterruptedException e ){
                e.printStackTrace();
            }
        }
  • 相关阅读:
    IDEA 使用镜像快速创建SpringBoot项目
    ajax基础学习笔记
    GitHub高效搜索
    MVC收藏的实现
    一个显示界面
    R-MS
    MS-API。AJAS
    MS-MVCAJAS 秒杀的添加功能吧
    真-API控制器AJAS
    真-API.DALBLL.AJAS/// 添加/// 绑定分类/// 显示,查询/// 删除//删除/// 反填/// 修改
  • 原文地址:https://www.cnblogs.com/ghostwu/p/9250722.html
Copyright © 2020-2023  润新知