博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
java-多线程
阅读量:6573 次
发布时间:2019-06-24

本文共 4583 字,大约阅读时间需要 15 分钟。

hot3.png

java-多线程

一.java实现线程同步的方法(8种?)

为什么要线程同步呢?因为java允许多线程并发,如果多个线程同时访问同一个变量或对象时,既存在读操作又存在写操作,这时候就会出现变量值或者操作对象的状态出现紊乱的情况,从而导致程序异常。
先列出8种方法:
1).同步方法
用synchronized修饰的方法,这个方法可以是静态或者非静态的,但是不能是抽象类的抽象方法,也不能是接口里的接口方法。线程在执行线程方法时具有排他性,也就是说进入一个线程的一个同步方法时,这个对象的所有同步方法都被锁定了,在此期间,其他任何线程都不能访问这个对象的任意一个同步方法,直到这个线程执行完它所调用的同步方法并从中退出,从而导致它释放了该对象的同步锁之后。在一个对象被某个线程锁定之后,其他线程是可以访问这个对象的所有非同步方法的。

public synchronized void save(int money) {         account += money;

2).同步代码块

同步块是用synchronized锁定一个指定的对象,来对同步块中包含的代码进行同步。
同步是一种高开销的操作,因此应该尽量减少同步的内容。通常没有必要同步整个方法,使用synchronized代码块同步关键代码即可。

public void save(int money){            synchronized(this){                account+=money;            }        }

3).wait与notify

wait():使一个线程处于等待状态,并且释放所持有的对象的lock。
sleep():使一个正在运行的线程处于睡眠状态,是一个静态方法,调用此方法要捕捉InterruptedException异常。
notify():唤醒一个处于等待状态的线程,注意的是在调用此方法的时候,并不能确切的唤醒某一个等待状态的线程,而是由JVM确定唤醒哪个线程,而且不是按优先级。
Allnotity():唤醒所有处入等待状态的线程,注意并不是给所有唤醒线程一个对象的锁,而是让它们竞争。

4).使用特殊域变量(volatile)实现线程同步

volatile关键字为域变量的访问提供了一种免锁机制;使用volatile相当于告诉虚拟机该域可能被其他线程;所以每次使用该域都要重新计算,而不能使用寄存器中的值;volatile不会提供任何原子操作,也不能修饰final的变量。

class Bank {            //需要同步的变量加上volatile            private volatile int account = 100;             public int getAccount() {                return account;            }            //这里不再需要synchronized             public void save(int money) {                account += money;            }        }

5).使用重入锁

ReentrantLock() : 创建一个ReentrantLock实例
lock() : 获得锁
unlock() : 释放锁
注:关于Lock对象和synchronized关键字的选择:
a.最好两个都不用,使用一种java.util.concurrent包提供的机制,能够帮助用户处理所有与锁相关的代码。
b.如果synchronized关键字能满足用户的需求,就用synchronized,因为它能简化代码
c.如果需要更高级的功能,就用ReentrantLock类,此时要注意及时释放锁,否则会出现死锁,通常在finally代码释放锁

class Bank {                        private int account = 100;            //需要声明这个锁            private Lock lock = new ReentrantLock();            public int getAccount() {                return account;            }            //这里不再需要synchronized             public void save(int money) {                lock.lock();                try{                    account += money;                }finally{                    lock.unlock();                }                            }        }

6).使用局部变量(空间换时间?)

如果使用ThreadLocal管理变量,则每一个使用该变量的线程都获得该变量的副本,副本之间相互独立,这样每一个线程都可以随意修改自己的变量副本,而不会对其他线程产生影响。

7).使用阻塞队列

LinkedBlockingQueue
8).使用原子变量
什么是原子操作呢?原子操作就是指将读取变量值,修改变量值,保存变量值看作是一个整体来进行操作,要么都能同时完成,要么都不完成。
AtomicInteger

class Bank {    private AtomicInteger account = new AtomicInteger(100);    public AtomicInteger getAccount() {        return account;     }     public void save(int money) {        account.addAndGet(money);    }}

二、java实现多线程的方法

1.继承Thread,重写run函数
thread类本质上是实现了runable接口的一个实例,代表线程的一个实例。启动线程的唯一方法是Thread类的start()实例方法,start()是一个native方法,它将启动一个新线程,并执行run()方法。这种方式非常简单,就是自己的类extend Thread,并复写run()方法,这样就可以启动新线程并执行自己定义的run()方法。

public class MyThread extends Thread {    public void run() {     System.out.println("MyThread.run()");    }  }   MyThread myThread1 = new MyThread();  MyThread myThread2 = new MyThread();  myThread1.start();  myThread2.start();

2.实现runable接口创建线程

如果自己的类已经有一个父类了,这时候就不能再extend了,需要用一个runable()接口来实现。
为了启动MyThread,首先要实例化一个Thread,传入自己的MyThread实例。

class MyThread implements Runnable{  private int tickets = 100;  public void run(){     while(true){     if(tickets > 0){       System.out.println(Thread.currentThread().getName() +          " is saling ticket " + tickets--);      }    }  }}public class ThreadDemo1{  public static void main(String[] args){   MyThread t = new MyThread();     new Thread(t).start();     new Thread(t).start();     new Thread(t).start();     new Thread(t).start();  }}

3.实现callable接口,通过FutureTask包装器创建线程

public class SomeCallable
extends OtherClass implements Callable
{ @Override public V call() throws Exception { // TODO Auto-generated method stub return null; }}
Callable
oneCallable = new SomeCallable
(); //由Callable
创建一个FutureTask
对象: FutureTask
oneTask = new FutureTask
(oneCallable); //注释:FutureTask
是一个包装器,它通过接受Callable
来创建,它同时实现了Future和Runnable接口。 //由FutureTask
创建一个Thread对象: Thread oneThread = new Thread(oneTask); oneThread.start(); //至此,一个线程就创建完成了。

4.使用ExecutorService,Callable,Future实现有返回结果的线程

ExecutorService、Callable、Future三个接口实际上都是属于Executor框架。返回结果的线程是在JDK1.5中引入的新特征,有了这种特征就不需要再为了得到返回值而大费周折了。

可返回值的任务必须实现Callable接口。类似的,无返回值的任务必须实现Runnable接口。

执行Callable任务后,可以获取一个Future的对象,在该对象上调用get就可以获取到Callable任务返回的Object了。

注意:get方法是阻塞的,即:线程无返回结果,get方法会一直等待。

转载于:https://my.oschina.net/huyuBlog/blog/1592757

你可能感兴趣的文章
微信App支付全解析
查看>>
[单刷APUE系列]第四章——文件和目录[1]
查看>>
程序员思维看爱情是什么?
查看>>
RN与原生交互(一)——基本页面跳转
查看>>
android消息机制—Looper
查看>>
中台之上(五):业务架构和中台的难点,都是需要反复锤炼出标准模型
查看>>
为什么中台是传统企业数字化转型的关键?
查看>>
使用模板将Web服务的结果转换为标记语言
查看>>
inno setup 打包脚本学习
查看>>
php 并发控制中的独占锁
查看>>
禁止微信浏览器的下拉滑动
查看>>
从pandas到geopandas
查看>>
LOL设计模式之「策略模式」
查看>>
用express搭建网站
查看>>
如何在 Swift 中进行错误处理
查看>>
[Leetcode] Factor Combinations 因数组合
查看>>
用tinypng插件创建gulp task压缩图片
查看>>
浅谈DOMContentLoaded事件及其封装方法
查看>>
BetaMeow----利用机器学习做五子棋AI
查看>>
APM终端用户体验监控分析(下)
查看>>