文档章节

聊聊java(十一)多线程

你我他有个梦
 你我他有个梦
发布于 2017/04/13 09:47
字数 1429
阅读 24
收藏 0

1.多线程的实现   

 实现线程的方式通常有两种,extends Thread和implements Runnable,还有一种方式是implements Callable。

实现Runnable接口相比继承Thread类有如下好处:

  • 避免点继承的局限,一个类可以继承多个接口。
  • 适合于资源的共享

以卖票程序为例来说明两种方式的区别。


public class MyThread1 extends Thread {
    private int ticket = 10;

    public void run() {
        for (int i = 0; i < 20; i++) {
            if (this.ticket > 0) {
                System.out.println("卖票:ticket" + this.ticket--);
            }
        }
    }
    public static void main(String[] args) {
        MyThread mt1 = new MyThread();
        MyThread mt2 = new MyThread();
        MyThread mt3 = new MyThread();
        mt1.start();//每个线程都各卖了10张,共卖了30张票  
        mt2.start();//但实际只有10张票,每个线程都卖自己的票  
        mt3.start();//没有达到资源共享  
    }
}
public class MyThread2 implements Runnable {
    private int ticket=10;
    public void run(){
        for(int i=0;i<20;i++){
            if(this.ticket>0){
                System.out.println("卖票:ticket"+this.ticket--);
            }
        }
    }
    public static void main(String[] args) {
        MyThread mt=new MyThread();
        new Thread(mt).start();//同一个mt,但是在Thread中就不可以,如果用同一
        new Thread(mt).start();//个实例化对象mt,就会出现异常
        new Thread(mt).start();
    }
}

虽然现在程序中有三个线程,但是一共卖了10张票,也就是说使用Runnable实现多线程可以达到资源共享目的。

2.同步

synchronized:

 

public class SyncWayTest {

    public static void main(String[] args) {
        final SyncWayTest thread1 = new SyncWayTest();
        final SyncWayTest thread2 = new SyncWayTest();

        new Thread("thread1"){
            @Override
            public void run() {
                //synchronized无法中断一个正在等待锁的线程
                synchronized (thread1){
                    System.out.println("thread1 run...");
                    try {
                        Thread.sleep(4000);
                        System.out.println("lock is released!");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }.start();
        new Thread("thread2"){
                public void run() {
                    synchronized (thread2){
                        System.out.println("thread2 run...");
                    }
                }
        }.start();
    }
}

synchronized是java内置的特性

缺陷:

如果一个代码块被synchronized修饰了,当一个线程获取了对应的锁,并执行该代码块时,其他线程便只能一直等待,等待获取锁的线程释放锁,而这里获取锁的线程释放锁只会有两种情况:

1)获取锁的线程执行完了该代码块,然后线程释放对锁的占有;

2)线程执行发生异常,此时JVM会让线程自动释放锁。

试想一下,如果有多个读操作,使用了synchronized关键字之后只能有一个线程去读,其他线程只能等待,是否可以有一种机制不让等待的线程无限期的等下去,多个线程读操作也不发生冲突呢?Lock接口就可以。

Lock接口

首先Lock不是java内置的关键字,synchronized不需要用户手动去释放,只要synchronized段的代码执行完毕锁就会被释放,但是Lock必须要手动释放。

//看下Lock接口的源码
public interface Lock {
    void lock();//获取锁
    void lockInterruptibly() throws InterruptedException;//获取锁
    boolean tryLock();//获取锁
    boolean tryLock(long time, TimeUnit unit) throws InterruptedException;//获取锁
    void unlock();//释放锁
}

lock:是用来获取锁,锁被其他线程 获取则等待,一般将释放锁的动作放在finally代码块中执行,以保证锁的释放。

tryLock:有返回值表示是否成功获取锁,不论是否拿到锁方法都会立即返回。

tryLock(long time, TimeUnit unit):与tryLock的区别是拿不到锁时可以设置等待时间,时间过期之后拿不到则返回false。

lockInterruptibly:如果线程正在等待获取锁,则这个线程能够响应中断,即中断线程的等待状态。

ReetrantLock是Lock接口的实现类,使用Lock接口的实现类来实现同步编程:

public class TryLockNoBlockTest {

    public static Lock lock = new ReentrantLock();

    public static void main(String[] args) {
        new Thread() {

            public void run() {
                String threadName = Thread.currentThread().getName();
                boolean bLock = lock.tryLock();//非阻塞式的锁,立即返回。
                if (bLock) {
                    try {
                        System.out.println(threadName + " 获取了锁");
                    } catch (Exception e) {
                        e.printStackTrace();
                    } finally {
                        lock.unlock();
                        System.out.println(threadName + " 释放了锁");
                    }
                }

            }
        }.start();
        new Thread() {

            public void run() {
                String threadName = Thread.currentThread().getName();
                boolean bLock = lock.tryLock();//非阻塞式的锁
                if (bLock) {
                    try {
                        System.out.println(threadName + " 获取了锁");
                    } catch (Exception e) {
                        e.printStackTrace();
                    } finally {
                        lock.unlock();
                        System.out.println(threadName + " 释放了锁");
                    }
                }
            }
        }.start();
        /*new Thread(){

            public void run() {
                String threadName = Thread.currentThread().getName();
                try {
                    lock.lock();//阻塞式的锁
                    System.out.println(threadName+" 获取了锁");
                }catch (Exception e){
                    e.printStackTrace();
                }finally {
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    lock.unlock();
                    System.out.println(threadName+" 释放了锁");
                }
            }
        }.start();
        new Thread(){

            public void run() {
                String threadName = Thread.currentThread().getName();
                try {
                    lock.lock();//阻塞式的锁
                    System.out.println(threadName+" 获取了锁");
                }catch (Exception e){
                    e.printStackTrace();
                }finally {
                    lock.unlock();
                    System.out.println(threadName+" 释放了锁");
                }
            }
        }.start();
    }*/
    }
}

ReentrantReadWriteLock

可重入的读写锁

可重入:就是同一个线程可以重复加锁,可以对同一个锁加多次,每次释放的时候回释放一次,直到该线程加锁次数为0,这个线程才释放锁。

读写锁: 也就是读锁可以共享,多个线程可以同时拥有读锁,但是写锁却只能只有一个线程拥有,而且获取写锁的时候,其他线程都已经释放了读锁,而且在该线程获取写锁之后,其他线程不能再获取读锁。

import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * 两个线程可以同时读,但是一个线程在读的时候另一个线程不能写
 * 也不能同时写。
 * readWriteLock拿到读锁之后,如果有其他线程申请写锁,必须要等到读锁被释放
 如果有一个线程已经占用了写锁,则此时其他线程如果申请写锁或者读锁,则申请的线程会一直等待释放写锁。
 * 
 */
public class ReentrantReadWriteLockTest {

    public ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
    public static ReentrantReadWriteLockTest lockTest = new ReentrantReadWriteLockTest();
    public void read(String threadName){
        try{
            readWriteLock.readLock().lock();
            Long startTime = System.currentTimeMillis();
            while(System.currentTimeMillis()-startTime<1000){
                System.out.println(threadName+" is reading...");
                Thread.sleep(10);
            }
            System.out.println(threadName+" 读操作完毕...");
        }catch (Exception e){

        }finally {
            readWriteLock.readLock().unlock();
        }

    }

    public void write(String threadName){
        try{
            readWriteLock.writeLock().lock();
            Long startTime = System.currentTimeMillis();
            while(System.currentTimeMillis()-startTime<100){
                System.out.println(threadName+" is writing...");
            }
            System.out.println(threadName+" 写操作完毕...");
        }catch (Exception e){

        }finally {
            readWriteLock.writeLock().unlock();
        }
    }

    public static void main(String[] args) {
        new Thread(){
            public void run() {
                String name = Thread.currentThread().getName();

                lockTest.read(name);
                lockTest.write(name);
            }
        }.start();
        new Thread(){
            public void run() {
                String name = Thread.currentThread().getName();
                lockTest.read(name);
                lockTest.write(name);
            }
        }.start();
    }
}

 

© 著作权归作者所有

你我他有个梦

你我他有个梦

粉丝 95
博文 110
码字总数 98858
作品 0
昌平
程序员
私信 提问
JAVA基础再回首(三十)——JAVA基础再回首完美结束,感概万千!

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/m366917/article/details/52724939 JAVA基础再回首(三十)——JAVA基础再回首完美结束,感概万千! 经过了几...

Aduroidpc
2016/10/02
0
0
马士兵java教程的观看顺序

本人java菜鸟一枚,在一家软件开发公司上班,公司开发发的框架是hibernate、spring、Tapestry;由于之前没有系统的学习过过java,目前正在按照马士兵的java教程学习,现在java基础部分的十一章...

候杰
2012/10/19
7.6K
11
Java程序员从笨鸟到菜鸟全部博客目录【2012年十一月七日更新】

本文来自:曹胜欢博客专栏。转载请注明出处:http://blog.csdn.net/csh624366188 大学上了一年半,接触java也一年半了,虽然中间也有其他东西的学习,但是还是以java为主路线,想想这一年半,...

长平狐
2012/11/12
103
0
15个顶级Java多线程面试题及回答

Java 线程面试问题 在任何Java面试当中多线程和并发方面的问题都是必不可少的一部分。如果你想获得任何股票投资银行的前台资讯职位,那么你应该准备很多关于多线程的问题。在投资银行业务中多...

LCZ777
2014/05/27
0
0
聊聊openjdk的jvm.cfg文件

序 本文主要研究一下openjdk的jvm.cfg文件 jdk8 /Library/Java/JavaVirtualMachines/jdk1.8.0_151.jdk/Contents/Home/jre/lib/jvm.cfg 对于jdk8该文件在JAVA_HOME/jre/lib/目录下;其注释显示......

go4it
03/26
0
0

没有更多内容

加载失败,请刷新页面

加载更多

0.01-Win10安装linux子系统

一、安装Debian子系统 -1、控制面板设置: -1.1、打开“控制面板” —— “程序” —— “启用或关闭Windows功能” —— 勾选 “适用于Linux的Windows子系统” -2、设置: -2.1、打开“设置”...

静以修身2025
昨天
2
0
init 0-6 (启动级别:init 0,1,2,3,4,5,6)

启动级别: init 0,1,2,3,4,5,6 这是个很久的知识点了,只是自己一直都迷迷糊糊的,今天在翻出来好好理解下。。 0: 停机 1:单用户形式,只root进行维护 2:多用户,不能使用net file system...

圣洁之子
昨天
2
0
Android Camera HAL浅析

1、Camera成像原理介绍 Camera工作流程图 Camera的成像原理可以简单概括如下: 景物(SCENE)通过镜头(LENS)生成的光学图像投射到图像传感器(Sensor)表面上,然后转为电信号,经过A/D(模数转...

天王盖地虎626
昨天
2
0
聊聊Elasticsearch的ProcessProbe

序 本文主要研究一下Elasticsearch的ProcessProbe ProcessProbe elasticsearch-7.0.1/server/src/main/java/org/elasticsearch/monitor/process/ProcessProbe.java public class ProcessProb......

go4it
昨天
3
0
mysql PL(procedure language)流程控制语句

在MySQL中,常见的过程式SQL语句可以用在存储体中。其中包括IF语句、CASE语句、LOOP语句、WHILE语句、ITERATE语句和LEAVE语句,它们可以进行流程控制。 IF语句相当于Java中的if()...else if(...

edison_kwok
昨天
3
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部