zoukankan      html  css  js  c++  java
  • java多线程

    多线程的实现

    可以通过继承Tread类和实现Runnable接口来实现多线程,通过卖票实现多线程代码如下:

    package Thread;
    // 继承Thread类
    class ConductorThread extends Thread{
    	private int ticketNum = 5;
    	@Override
    	public void run() {
    		for(int i=5;i>0;i--) {
    			if(this.ticketNum>0) {
    				this.ticketNum--;
    				System.out.println(Thread.currentThread().getName() + "卖了1张票剩余" + this.ticketNum+ "张票");
    			}
    		}
    	}
    }
    
    //实现Runnable接口
    class ConductorRunnable implements Runnable{
    	private int ticketNum = 5;
    	@Override
    	public void run() {
    		for(int i=5;i>0;i--) {
    			if(this.ticketNum>0) {
    				this.ticketNum--;
    				System.out.println(Thread.currentThread().getName()+"卖了1张票剩余"+this.ticketNum+"张票");
    			}
    		}
    	}
    }
    
    public class RailwayStation {
    	public static void main(String args[]) {
    		// 创建实例对象
    		ConductorThread conductorThread = new ConductorThread();
    		ConductorRunnable conductorRunnable = new ConductorRunnable();
    		
    		//为实例对象创建两个售票员线程
    		Thread conductor1 = new Thread(conductorThread,"售票员1") ;
    		Thread conductor2 = new Thread(conductorThread,"售票员2") ;
    
    		Thread conductor3 = new Thread(conductorRunnable,"售票员3");
    		Thread conductor4 = new Thread(conductorRunnable,"售票员4");
    		// 启动线程
    		conductor1.start();
    		conductor2.start();	
    		conductor3.start();
    		conductor4.start();
    	}
    }
    

    运行结果:

    售票员1卖了1张票剩余4张票
    售票员1卖了1张票剩余2张票
    售票员1卖了1张票剩余1张票
    售票员1卖了1张票剩余0张票
    售票员2卖了1张票剩余3张票
    售票员4卖了1张票剩余3张票
    售票员3卖了1张票剩余3张票
    售票员4卖了1张票剩余2张票
    售票员3卖了1张票剩余1张票
    售票员4卖了1张票剩余0张票
    

    如果创建了两个实例对象,则会卖两倍的票数,这是因为普通成员变量的ticketNum会被初始化两次。

    Thread和Runnable的区别

    阅读源码

    public interface Runnable {
        /**
         * When an object implementing interface <code>Runnable</code> is used
         * to create a thread, starting the thread causes the object's
         * <code>run</code> method to be called in that separately executing
         * thread.
         * <p>
         * The general contract of the method <code>run</code> is that it may
         * take any action whatsoever.
         *
         * @see     java.lang.Thread#run()
         */
        public abstract void run();
    }
    
    public
    class Thread implements Runnable {
        /* Make sure registerNatives is the first thing <clinit> does. */
        private static native void registerNatives();
        static {
            registerNatives();
        }
    
        private volatile String name;
        private int            priority;
        private Thread         threadQ;
        private long           eetop;
    
        /* Whether or not to single_step this thread. */
        private boolean     single_step;
        ......
    }
    
    • Thread类实现了Runnable接口,当一个类A继承了类B,类B没有继承Thread,由于java的单继承性,类B要想实现多线程,则不能继承Thread类,只能实现Runnable接口。
    • Runnable可以看做一个一个具体的task,在task下有多个线程共同来执行这个task,不同线程之间的资源是可以共享的。Thread作为线程的载体,不同Thread对象之间不可以进行资源共享。

    线程的状态

    • 新建状态

    new出新对象,调用start方法,线程进入就绪状态,已经启动的线程不能再次调用start方法

    • 就绪状态

    具备的运行条件,但是没有分到CPU,等待系统为其分配CPU

    • 运行状态

    运行状态的线程有可能变为阻塞状态
    注: 当发生如下情况是,线程会从运行状态变为阻塞状态:

     ①线程调用sleep方法主动放弃所占用的系统资源
     ②线程调用一个阻塞式IO方法,在该方法返回之前,该线程被阻塞
     ③线程试图获得一个同步监视器,但更改同步监视器正被其他线程所持有
     ④线程在等待某个通知(notify)
     ⑤程序调用了线程的suspend方法将线程挂起。不过该方法容易导致死锁,所以程序应该尽量避免使用该方法。
    
    • 阻塞状态

    线程执行了sleep方法或者等待I/O设备

    • 死亡状态

    线程执行完毕或者强制终止

    线程同步

    在上面的卖票例子中,可能会出现不同的售票员卖同一张票的现象,导致最后剩余票数为负数,显然是不符合实际的,为了使卖票结果最后的剩余值为0,应该使用线程同步方法,来避免这种情况的发生。

    class ConductorRunnable implements Runnable{
    	private int ticketNum = 5;
    	@Override
    	public void run() {
    		while(this.ticketNum>0) {
    			try {
    				Thread.sleep(10);
    			} catch (InterruptedException e) {
    				// TODO Auto-generated catch block
    				e.printStackTrace();
    			}
    			this.ticketNum--;
    			System.out.println(Thread.currentThread().getName() + "卖了1张票剩余" + this.ticketNum+ "张票");
    		}
    	}
    }
    public class RailwayStation {
    	@Test
    	public void test1() {
    		// 局部变量创建一个实例对象  实现多线程
    		ConductorRunnable conductorRunnable = new ConductorRunnable();
    
    		Thread conductor3 = new Thread(conductorRunnable,"售票员3");
    		Thread conductor4 = new Thread(conductorRunnable,"售票员4");
    		// 启动线程
    		conductor3.start();
    		conductor4.start();
    	}
            public static void main(String args[]) {
    		RailwayStation rw = new RailwayStation();
    		rw.test1();
    	}
    

    运行结果:

    售票员3卖了1张票剩余4张票
    售票员4卖了1张票剩余3张票
    售票员3卖了1张票剩余2张票
    售票员4卖了1张票剩余1张票
    售票员3卖了1张票剩余0张票
    售票员4卖了1张票剩余-1张票
    

    线程同步的几种方法 :

    • 同步方法

    在方法前加关键字synchronized,代码入下:

    class ConductorRunnable implements Runnable{
    	private int ticketNum = 5;
    	@Override
    	public synchronized void run() {
    		while(this.ticketNum>0) {
    			try {
    				Thread.sleep(10);
    			} catch (InterruptedException e) {
    				// TODO Auto-generated catch block
    				e.printStackTrace();
    			}
    			this.ticketNum--;
    			System.out.println(Thread.currentThread().getName() + "卖了1张票剩余" + this.ticketNum+ "张票");
    		}
    	}
    }
    

    运行结果入下:

    售票员3卖了1张票剩余4张票
    售票员3卖了1张票剩余3张票
    售票员3卖了1张票剩余2张票
    售票员3卖了1张票剩余1张票
    售票员3卖了1张票剩余0张票
    

    在java中每一个对象都有一个内置锁,synchronized关键字修饰方法,内置锁会保护整个方法,线程要想执行该方法,必须先获取该方法的内置锁。当一个线程在执行该方法时,另外一个线程就无法获得该该方法的内置锁,会处于block状态,直到前一个方法执行结束,释放内置锁。

    • 同步代码块

    除了使用synchronized修饰整个方法外,还可以用synchronized来修饰关键代码,来达到同步的目的。代码入下:

    class ConductorRunnable implements Runnable{
    	private int ticketNum = 5;
    	@Override
    	public void run() {
    		synchronized(this) {
    			while(this.ticketNum>0) {
    				try {
    					Thread.sleep(10);
    				} catch (InterruptedException e) {
    					// TODO Auto-generated catch block
    					e.printStackTrace();
    				}
    				this.ticketNum--;
    				System.out.println(Thread.currentThread().getName() + "卖了1张票剩余" + this.ticketNum+ "张票");
    			}
    		}	
    	}
    }
    

    被synchronized修饰的代码块会被加上内置锁,从而达到同步的目的。

    • 使用特殊域变量(volatile)实现线程同步
      可以将线程操作的变量前面加volaile修饰,给变量加入内置锁,代码如下:
    class ConductorRunnable implements Runnable{
    	private volatile int ticketNum = 5;
    	@Override
    	public void run() {
    		while(this.ticketNum>0) {
    			try {
    				this.ticketNum--;
    				Thread.sleep(10);
    			} catch (InterruptedException e) {
    				// TODO Auto-generated catch block
    				e.printStackTrace();
    			}
    			System.out.println(Thread.currentThread().getName() + "卖了1张票剩余" + this.ticketNum+ "张票");
    		}	
    	}
    }
    

    运行结果:

    售票员3卖了1张票剩余3张票
    售票员4卖了1张票剩余3张票
    售票员4卖了1张票剩余1张票
    售票员3卖了1张票剩余1张票
    售票员4卖了1张票剩余0张票
    

    1.volatile关键字为域变量的访问提供了一种免锁机制;
    2.使用volatile修饰域相当于告诉虚拟机该域可能会被其他线程更新;
    3.因此每次使用该域就要重新计算,而不是使用寄存器中的值;
    4.多线程的内存模型为主存和线程栈,在处理数据时,线程会把值从主存load到本地栈,完成操作后再save回去(volatile关键词的作用:每次针对该变量的操作都激发一次load and save)。
    5.volatile不会提供任何原子操作,它也不能用来修饰final类型的变量。

    • 使用重入锁(Lock)实现线程同步

    代码入下:

    class ConductorRunnable implements Runnable{
    	private int ticketNum = 5;
    	private Lock lock = new ReentrantLock(); // 声明一个锁
    	@Override
    	public void run() {
    		while(this.ticketNum>0) {
    			lock.lock();
    			try {
    				this.ticketNum--;
    				Thread.sleep(10);
    			} catch (InterruptedException e) {
    				// TODO Auto-generated catch block
    				e.printStackTrace();
    			}
    			System.out.println(Thread.currentThread().getName() + "卖了1张票剩余" + this.ticketNum+ "张票");
    		}	
    	}
    }
    

    执行结果如下:

    售票员3卖了1张票剩余4张票
    售票员3卖了1张票剩余3张票
    售票员3卖了1张票剩余2张票
    售票员3卖了1张票剩余1张票
    售票员3卖了1张票剩余0张票
    

    悲观锁和乐观锁

    • 悲观锁

    对世界充满了绝望,觉得人和人都会修改数据,每次拿到数据后,都会上锁,避免其他线程更改数据,直到执行完毕释放资源

    • 乐观锁

    乐观主义者,觉得任何人都不会修改数据,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。乐观锁适用于多读的应用类型,这样可以提高吞吐量

    线程通信

    • 借助于Object类的wait()、notify()和notifyAll()实现通信
    • 使用Condition控制线程通信
  • 相关阅读:
    CSS样式更改_2D转换
    使用本地json-server服务,创建简单的本地api数据
    为何不推荐使用 Sass 作为 css 预处理器
    移动端适配
    html 元素垂直水平居中
    场内场外基金和开户避坑
    QJson
    Merry Christmas Mr. Lawrence
    github,源码,高仿 直播
    P1314 聪明的质监员
  • 原文地址:https://www.cnblogs.com/xxyxt/p/11360030.html
Copyright © 2011-2022 走看看