zoukankan      html  css  js  c++  java
  • JAVA多线程学习(一)

    一、什么是多线程?

    很显然,一随便编敲一段简单的小代码,从main方法开始运行,计算机是一条一条地从上而下的串行执行程序。那如果我们要同时执行两个任务呢?就比如你用浏览器上网看网页的时候,你还能同时登QQ,还能同时下载电影……这些任务都是并行执行的。在计算机的任务管理器里可以看到一个一个的正在运行的程序任务,这些程序就是一个个一个的进程,而大多数进程下面都已多条线程同时执行,所以,多线程其实就是多个任务在同时执行。

    虽然是多线程,但是CPU执行命令仍然是一条一条执行的。然而计算机CPU只有一个,虽然多核计算机能够有虚拟多CPU的技术(这是我自己对多核CPU的理解,不够准确请见谅),但是相对于计算机要实现的线程数量,简直少的可怜。所以,当多线程在硬件实现的时候,CPU的处理方式是进行分时处理(TDMA时分多址有没有!!?)。什么意思呢,就是在一段时间执行这一条线程,一段时间执行另外一条线程,所有的具有执行资格的线程进行随机的获得CPU的执行权。CPU的运算速度快的惊人,因此我们感觉上其实就是多线程在并发执行了。

    二、JAVA中多线程的使用,Thread类

    “一切皆对象”在JAVA语言中是一个永恒不变的定理,自然线程这玩意同样有个对象进行包装。那就是Thread类。这个对象实现了一个Runnable接口,这个接口很简单,就一个run方法。

    对于要多线程执行任务,可以通过如下两种方法来实现多线程:

    1、继承Thread类

    通过将要实现的任务封装成方法,再封装成对象,然后继承Thread类,将任务方法写进run方法之中,在再父线程中用start方法启动就可以了。比如如下开启一个线程的代码

    package com.qyz.thread;
    
    public class ThreadDemo1 {
    
        public static void main(String[] args) {
            Thread t1 = new Demo("第一个线程");
            //start()开启线程
            t1.start();
            //输出:第一个线程:被打开了
        }
    }
    //继承了Thread的对象
    class Demo extends Thread{
        public Demo(String name){
            //父类构造方法中可以给线程命名
            super(name);
        }
        //重写run方法
        @Override
        public void run(){
            //打印线程名称
            System.out.println(Thread.currentThread().getName() + ":被打开了");
        }
    }

    2、通过实现Runnable接口

    方法一的线程开启方法有一个问题,就是当一个类的的继承体系结构之中,只有部分子类需要开启新的线程执行任务,但由于是单继承的,继承了某个父类的子类不可能再继承Thread方法,因此只有通过实现

    Runnable方法来开启线程。

    1、某个类实现Runnable,并重写run方法,封装需要新开启线程实现的任务。2、新建Thread(Runnable target,name)对象。将Runnable对象封装进线程对象中。3、开启线程start()

     1 package com.qyz.thread;
     2 
     3 public class ThreadDemo2 {
     4 
     5     public static void main(String[] args) {
     6         Thread t1 = new Thread(new Demo2(),"第二个线程");
     7         //start()开启线程
     8         t1.start();
     9         //输出:第二个线程:被打开了
    10     }
    11 }
    12 //实现了Runnable接口的对象
    13 class Demo2 implements Runnable{
    14     //重写run方法
    15     @Override
    16     public void run(){
    17         //打印线程名称
    18         System.out.println(Thread.currentThread().getName() + ":被打开了");
    19     }
    20 }

    三、多线程之间的同步问题

    多线程好吗?当然好,但是会有一个问题需要注意!那就是同步问题。在多个线程访问同一对象数据时?有多条操作命令,本来想的是等一个线程操作完该对象后,另外一个线程再操作,但是由于CPU是随机分时执行线程,因此有可能当一个线程对共享对象操作到一半时,CPU就切向另外一个线程,导致出现意外的输出结果。这样的问题一半很难排查,因此需要在编写代码的时候多加注意!

    没有同步的买卖票事例

     1 package com.qyz.thread;
     2 
     3 public class TickerDemo1 {
     4     public static void main(String[] args) {
     5         SaleTicket st = new SaleTicket();
     6         //实现3个线程
     7         Thread t1 = new Thread(st, "1号卖票员");
     8         Thread t2 = new Thread(st, "2号卖票员");
     9         Thread t3 = new Thread(st, "3号卖票员");
    10         //开启线程
    11         t1.start();
    12         t2.start();
    13         t3.start();
    14     }
    15 }
    16 class SaleTicket implements Runnable{
    17     //总共100张票
    18     private int num = 100;
    19     @Override
    20     public void run() {
    21         while(this.num > 0){
    22             //这个代码太简单,所以让线程睡一会,突出同步问题
    23             try {Thread.sleep(10);} catch (Exception e) {}
    24             System.out.println(Thread.currentThread().getName() + "******" + num--);
    25         }
    26     }
    27 }

    本来不该出现的0号票出现了,意外情况

    解决方法:加锁

    多线程中的锁其实就像一扇门,一个线程进去之后,把门锁上,另外一个线程就不能进来,直到门里面的线程执行完毕出来。

    JAVA中实现同步锁通过关键字synchronized,有以下方法

    1、同步代码块,需要同步的命令封装进一个代码块中,加上一个对象做锁即可

     1 class SaleTicket implements Runnable{
     2     //总共100张票
     3     private int num = 100;
     4     //一个object对象充当锁
     5     private Object lock = new Object();
     6     @Override
     7     public void run() {
     8         while(true){
     9             //同步代码块封装
    10             synchronized (lock) {
    11                 if(this.num > 0){
    12                     //这个代码太简单,所以让线程睡一会,突出同步问题
    13                     try {Thread.sleep(10);} catch (Exception e) {}
    14                     System.out.println(Thread.currentThread().getName() + "******" + num--);
    15                 }else
    16                     break;
    17             }
    18         }
    19     }
    20 }

     输出如下

    问题解决!

    2、同步函数

    函数是一种封装,代码块也是一种封装,那能不能结合在一起呢?sure!那就是通过关键字将将方法变成同步函数。

     1 class SaleTicket implements Runnable{
     2     //总共100张票
     3     private int num = 100;
     4     @Override
     5     public void run() {
     6         while(true){
     7             this.sale();
     8         }
     9     }
    10     //通过封装成同步函数来实现
    11     private synchronized void sale(){
    12         if(this.num > 0){
    13             //这个代码太简单,所以让线程睡一会,突出同步问题
    14             try {Thread.sleep(10);} catch (Exception e) {}
    15             System.out.println(Thread.currentThread().getName() + "******" + num--);
    16         }
    17     }
    18 }

    成功解决!

  • 相关阅读:
    SQL中文转拼音
    cocos2D 虚拟摇杆Joystick功能实现
    cocos2d 粒子效果以及Particle Designer粒子工具的学习
    android 模拟器出错,emulator: ERROR: unknown virtual device name
    [转][越狱破解] 苹果itouch 4 iOS5.0.1完美越狱教程+资源下载
    objectivec 中随机数的用法 (3种:arc4random() 、random()、CCRANDOM_0_1() )
    [转]cocos2dx添加广告条(IOS and Android)
    cocos2d1.0.1x0.10.0版本 设置横屏与竖屏的方法
    【转】总结阐述Cocos2dX与Cocos2diphone区别;
    Objectivec 中CGGeometry几何类常用方法简单整理
  • 原文地址:https://www.cnblogs.com/njupt-Qsimple/p/5618491.html
Copyright © 2011-2022 走看看