美文网首页
java多线程

java多线程

作者: limself | 来源:发表于2019-06-03 13:06 被阅读0次

为了解决“一边...一边...问题”,可以使用进程或者线程

进程与线程


什么是进程

进程是应用程序运行的单元

什么是线程

线程是进程的单元。在JVM中,线程是抢占式调度的,不是时间片轮转调度的。

进程与线程有什么区别

  • 进程有独立的内存空间。线程堆内存共享,栈内存独立。
  • 线程资源消耗比进程小。
  • 因为一个进程中的多个线程是并发运行的。
  • Java程序的进程里至少包含主线程和垃圾回收线程(后台线程)。

并行与并发

并发:微观异步运行,宏观同步运行。可说在 同一“时间段”,不一定同一“时刻”进行。
在单cpu中,在一个时刻只有一个任务在进行,因为其采用时间片轮转调度,每个时间片对于cpu的运行速度而言非常短,所以在宏观上,给人的感觉还是很多个线程任务在同时进行

并行:真真切切的同一个时刻进行的事件。

创建进程


创建一个进程,运行命令“dxdiag”

方式一,使用Runtime

...
Runtime runtime=Runtime.getRuntime();
try{
    runtime.exec("dxdiag");
}catch(IOException e){
    System.out.println(e.getMessage());
}
...

方式二,使用ProcessBuilder

...
ProcessBuilder process=ProcessBuilder("dxdiag");
//ProcessBuilder process=ProcessBuilder("dxdiag");
//process.command("dxdiag");
try{
   process.start();
}catch(IOException e){
    System.out.println(e.getMessage());
}
...

创建线程


创建线程主要有两种方式:

1. 继承Thread类

Thread类及其子类称之为 线程类

public class ExtendsThreadDemo {
    public static void main(String[] args) {
    SingThread sing=new SingThread();
    sing.start();
    }
}

class SingThread extends Thread {

    public void run() {
        for (int i = 0; i < 50; i++) {
            System.out.println("01_Singing"+i);
            if(i==10)
            {
                PlayThread play=new PlayThread();
                play.start();
            }
        }

    }
}

class PlayThread extends Thread {
    public void run() {
        for (int i = 0; i < 50; i++) {
            System.out.println("02_playing"+i);
        }
    }
}

2. 实现Runnable接口

public class ImplementsRunnable {
    public static void main(String[] args) throws InterruptedException {
        Singable singable=new Singable();
        Thread sing=new Thread(singable,"唱歌");
        for (int i = 0; i < 50; i++) {
            System.out.println("02_playing"+i);
            if(i==10){
                sing.start();
                Thread.sleep(100);
            }
        }
    }
}
class Singable implements Runnable{

    public void run() {
        for (int i = 0; i < 50; i++) {
            System.out.println("01_Singing"+i);
        }
    }
}

3. 两种方式使用匿名内部类创建线程

   public static void main(String[] args) {
        System.out.println("匿名内部类创建线程!");
        new Thread(){
            public void run(){
                for (int i = 0; i < 50; i++) {
                    System.out.println("01_Singing"+i);
                }
            }
        }.start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 50; i++) {
                    System.out.println("02_playing"+i);
                }
            }
        }, "Play").start();
    }

两种创建方式的比较


实现 三个 \color{green}{猪八戒}\color{green}{50} 个 西瓜!

继承Thread方式
public class ExtendsDemo {
    public static void main(String[] args) {
        new Eat("八戒A").start();
        new Eat("八戒B").start();
        new Eat("八戒C").start();
    }
}

class Eat extends Thread {
    private int num = 50;

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

    @Override
    public void run() {
        while (num > 0) {
            System.out.println(this.getName() + "吃掉了" + num + "号西瓜!");
            int i = num--;
           /* try {
                sleep(100);
            } catch (InterruptedException e) {
                throw new RuntimeException("线程休眠失败!",e);
            }*/
        }
    }
}

结果:

八戒B吃掉了50号西瓜!
八戒A吃掉了50号西瓜!
八戒C吃掉了50号西瓜!
...

实现Runnable接口方式
public class ImplementsDemo {
    public static void main(String[] args) {
        Eatable eatable=new Eatable();
        Thread eat_A=new Thread(eatable,"八戒A");
        Thread eat_B=new Thread(eatable,"八戒B");
        Thread eat_C=new Thread(eatable,"八戒C");
        eat_A.start();
        eat_B.start();
        eat_C.start();
    }
}

class Eatable implements Runnable {
    private int num = 50;
    @Override
    public void run() {
        while (num > 0) {
            System.out.println(Thread.currentThread().getName() + "吃掉了" + num + "号西瓜!");
            num--;
        }
    }
}

结果:

八戒A吃掉了50号西瓜!
八戒A吃掉了49号西瓜!
八戒A吃掉了48号西瓜!
...

继承方式 创建每一线程就创建一个各自的num,三个八戒吃了总数150个西瓜。
实现方式 创建一个对象去构造多个线程,每个线程都共享一个num,真正实现了三个八戒吃了50 个西瓜。


区别:

  • 资源共享;资源不共享
  • 代码设计更优美
  • 操作复杂;操作简单

也许你会想到 关键字 static
public class ExtendsDemo {
    public static void main(String[] args) {
        new Eat("八戒A").start();
        new Eat("八戒B").start();
        new Eat("八戒C").start();
    }
}

class Eat extends Thread {
    private static int num = 50;

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

    @Override
    public void run() {
        while (num > 0) {
            System.out.println(this.getName() + "吃掉了" + num + "号西瓜!");
            int i = num--;
           /* try {
                sleep(100);
            } catch (InterruptedException e) {
                throw new RuntimeException("线程休眠失败!",e);
            }*/
        }
    }
}

这样不就解决了 继承方式创建线程 资源不共享问题了吗?
static 真的好吗?

线程同步与线程安全


对于上述的代码分析:
在run()方法中,三个线程A、B、C

B
while (num > 0) {
A C到此处被网络等原因延迟
System.out.println(this.getName() + "吃掉了" + num + "号西瓜!");
int i = num--;
}
所以,三个线程可能同时打印吃西瓜,如果在num--前出现线程延迟,一旦延迟结束,则三个线程同时执行num--,可能有些西瓜就没有被吃掉,在最后还可能出现负数号码的西瓜。
为了解决上述的线程安全问题

  • 同步代码块
  • 同步方法
  • 锁机制

1. 同步代码块

public class SynchronizedBlockDemo {
    public static void main(String[] args) {
        System.out.println("你好!这里是同步代码块解决线程安全问题!");
        Runnable eatable = new Runnable() {
            private int num = 50;
            @Override
            public void run() {
                synchronized (this) {
                    while (num > 0) {
                        System.out.println(Thread.currentThread().getName() + "吃掉了" + num + "号西瓜");
                        try {
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            throw new RuntimeException("线程休眠出错!", e);
                        }
                        num--;
                    }
                }
            }
        };
        Thread thread_A=new Thread(eatable,"八戒A");
        Thread thread_B=new Thread(eatable,"八戒B");
        Thread thread_C=new Thread(eatable,"八戒C");
        thread_A.start();
        thread_B.start();
        thread_C.start();
    }
}

2. 同步方法

public class SynchronizedMethodDemo {
    public static void main(String[] args) {
        System.out.println("你好!这里是同步方法解决线程安全问题!");
        Eatable  eatable=new Eatable();
        Thread thread_A=new Thread(eatable,"八戒A");
        Thread thread_B=new Thread(eatable,"八戒B");
        Thread thread_C=new Thread(eatable,"八戒C");
        thread_A.start();
        thread_B.start();
        thread_C.start();
    }
}
class Eatable implements Runnable{
    private int num=50;
    @Override
    public synchronized void run() {
        while(num>0){
            System.out.println(Thread.currentThread().getName()+"吃掉了"+num+"号西瓜!");
            try{
                Thread.sleep(123);
            }catch (InterruptedException e){
                throw new RuntimeException("线程休眠失败!",e);
            }
            num --;
        }
    }
}

3. 锁机制

  • 创建锁 :private final ReentrantLock lock=new ReentrantLock();
  • 使用锁:
lock.lock();
try{
  //运行的 资源操作 逻辑代码
}finally{
  lock.unlock();
}

例子全部代码:

public class LockDemo {
    public static void main(String[] args) {
        System.out.println("你好!这里是锁的使用!");
        Eatable1 eatable1 = new Eatable1();
        Thread eat_A = new Thread(eatable1, "八戒A");
        Thread eat_B = new Thread(eatable1, "八戒B");
        Thread eat_C = new Thread(eatable1, "八戒C");
        eat_A.start();
        eat_B.start();
        eat_C.start();
    }
}

class Eatable1 implements Runnable {
    private int num = 50;
    private final ReentrantLock lock=new ReentrantLock();
    @Override
    public void run() {

        lock.lock();
        try{
        while (num > 0) {

            System.out.println(Thread.currentThread().getName() + "吃掉了" + num + "号西瓜!");
            try {
                Thread.sleep(100);

            } catch (Exception e) {
                System.out.println(e.getMessage());
            }
            num--;
        }
        }finally {
            lock.unlock();
        }

    }
}

为了性能

  • 尽量减小synchronized和lock的作用域.

双重检查机制

 while(num>0){
        synchronized(this){
           if(num>0){
              System.out.println(Thread.currentThread().getName()+"吃掉了"+num+"号西瓜!");
              try{
                 Thread.sleep(123);
              }catch (InterruptedException e){
                  throw new RuntimeException("线程休眠失败!",e);
              }
              num --;
           }
        }
 }

“双重检查加锁”机制的实现会使用关键字volatile,它的意思是:被volatile修饰的变量的值,将不会被本地线程缓存,所有对该变量的读写都是直接操作共享内存,从而确保多个线程能正确的处理该变量。
注意:在java1.4及以前版本中,很多JVM对于volatile关键字的实现的问题,会导致“双重检查加锁”的失败,因此“双重检查加锁”机制只能用在java5及以上的版本。
由于volatile关键字可能会屏蔽掉虚拟机中一些必要的代码优化,所以运行效率并不是很高。因此一般建议,没有特别的需要,不要使用。


  • StringBuffer与StringBuilder的区别.
  • ArrayList和Vector的区别.
  • HashMap和Hashtable的区别.

StringBuffer 、Vector、 Hashable线程安全


懒汉单例与饿汉单例

懒汉单例 线程不安全

public class SingleLazyDemo {
    public static void main(String[] args) {
        System.out.println("你好!这里是单例的懒汉模型");
    }
}

class SingleLazy {
    private static SingleLazy singlelazy = null;
    private SingleLazy(){}
    public static SingleLazy getInstance() {
        if (singlelazy == null) {
            //此处存在线程安全问题
            singlelazy = new SingleLazy();
        }
        return singlelazy;
    }
}
class SingleLazy_1 {
    private static SingleLazy_1 singlelazy1 = null;
    private SingleLazy_1(){}
    public synchronized static SingleLazy_1 getInstance() {
        if (singlelazy1 == null) {
           singlelazy1 = new SingleLazy_1();
        }
        return singlelazy1;
    }
}

饿汉单例 线程安全

public class SingleHungeryDemo {
    public static void main(String[] args) {
        System.out.println("你好!这里是单例的饥汉模型!");
    }
}
class SingleHunger {
    private static SingleHunger singlehunger = new SingleHunger();

    private SingleHunger() {
    }

    public static SingleHunger getInstance() {
        return singlehunger;
    }
}

相关文章

  • 带你搞懂Java多线程(五)

    带你搞懂Java多线程(一)带你搞懂Java多线程(二)带你搞懂Java多线程(三)带你搞懂Java多线程(四) ...

  • 带你搞懂Java多线程(六)

    带你搞懂Java多线程(一)带你搞懂Java多线程(二)带你搞懂Java多线程(三)带你搞懂Java多线程(四)带...

  • Java多线程目录

    Java多线程目录 Java多线程1 线程基础Java多线程2 多个线程之间共享数据Java多线程3 原子性操作类...

  • java多线程--Callable

    **移步[java多线程系列文章]Java多线程(二十二)---LockSupport工具Java 停止线程 一、...

  • android 多线程 — 线程的面试题和答案

    这里都是我从各个地方找来的资料,鸣谢: Java多线程干货系列—(一)Java多线程基础 JAVA多线程和并发基础...

  • 5月份第一周学习安排

    学习内容: java多线程及线程同步的方法(使用) java多线程各种同步方法的原理和优缺点 java多线程设计模...

  • 带你搞懂Java多线程(四)

    带你搞懂Java多线程(一)带你搞懂Java多线程(二)带你搞懂Java多线程(三) 什么是线程间的协作 线程之间...

  • Java基础(六)

    多线程 Java多线程并发 1.1 JAVA 并发知识库 1.2 JAVA 线程实现/创建方式 1.2.1 继承 ...

  • (五) volatile关键字

    Java多线程目录 1 背景 理解Java多线程的内存抽象逻辑请阅读java多线程内存模型,当代操作系统,处理器为...

  • Java多线程高级特性(JDK8)

    [TOC] 一、Java多线程 1.Java多线程基础知识 Java 给多线程编程提供了内置的支持。一条线程指的是...

网友评论

      本文标题:java多线程

      本文链接:https://www.haomeiwen.com/subject/iitatctx.html