ReentrantLock():创建可重入锁对象
lock():获取锁对象,锁对象属于当前线程,如果被其他线程占用,则进入阻塞状态
unlock():释放锁对象,如果在线程执行结束前未释放,那么锁对象将被TERMINATED线程一直占用,其他线程将永远无法获得锁,从而导致死锁。务必在finally块中执行,防止程序异常终止。
示例:保证静态共享的num,分别被5个加线程和减线程调用后,结果为0
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class LockThread extends Thread{
private static Lock lock = new ReentrantLock();//establish static lock for all
private static int num = 0;
private String name;
public LockThread(String name) {
this.name = name;
}
@Override
public void run(){
lock.lock();//belong to current thread not the thread object
lock.lock();//lock count +1, just test for fun
try{
if(name.equals("add")){
for(int i=0;i<10;i++){
num++;
}
}else if(name.equals("minus")){
for(int i=0;i<10;i++){
num--;
}
}
System.out.println(name+":"+num);//final result must be 0
}finally{
this.myUnlock();//must in finally,you cant't be sure you program will be abnormal
this.myUnlock();//two lock ,two unlock
}
}
public void myUnlock(){
lock.unlock();//if thread terminated,you cant't do this operation
}
public static void main(String[] args) {
for(int i=0;i<10;i++){
if(i%2==0){
new LockThread("add").start();//add 5 times
}else{
new LockThread("minus").start();//minus 5 times
}
}
}
}
执行结果:
add:10
add:20
add:30
minus:20
minus:10
minus:0
minus:-10
add:0
minus:-10
add:0
ReentrantReadWriteLock
readLock可以与readLock并发执行,不发生阻塞,和writeLock相互排斥
示例:同时模拟synchronized和ReentrantReadWriteLock实现同步,并对比执行效率
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
public class ReadWriteLockThread extends Thread{
private static ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
private static ReadLock readLock = lock.readLock();
private static WriteLock writeLock = lock.writeLock();
private static int num;
private static int num2;
private String name;
private static long endtime;
private static long endtime2;
public static int getNum() {
readLock.lock();//加读锁
try{
return num;
}finally{
readLock.unlock();
}
}
public static void addNum() {
writeLock.lock();//加写锁
try{
num++;
}finally{
writeLock.unlock();
}
}
public synchronized int getNum2() {
return num2;
}
public synchronized static void addNum2() {
num2++;
}
@Override
public void run(){
if(name.equals("readwrite")){
for(int i=0;i<1000;i++){
addNum();
getNum();
}
endtime = System.currentTimeMillis();
}else{
for(int i=0;i<1000;i++){
addNum2();
getNum2();
}
endtime2 = System.currentTimeMillis();
}
}
public ReadWriteLockThread(String name) {
this.name = name;
}
public static void main(String[] args) {
long starttime = System.currentTimeMillis();
for(int i=0;i<10;i++){
new ReadWriteLockThread("readwrite").start();
}
sleep(2000);
System.out.println("num="+num);//检验是否达到同步效果
System.out.println(endtime-starttime);//ReadWriteLock方式执行时间
starttime = System.currentTimeMillis();
for(int i=0;i<10;i++){
new ReadWriteLockThread("sync").start();
}
sleep(2000);
System.out.println("num2="+num2);//检验是否达到同步效果
System.out.println(endtime2-starttime);//synchronized方式执行时间
}
public static void sleep(long time){
try {
Thread.sleep(time);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
执行结果:
num=10000
75
num2=10000
9
注:当前示例下,ReadWriteLock效率没有synchronize高。
newCondition():返回一个与锁相关的条件对象
await():将线程放到条件的等待集中,此时释放lock占用,进入等待状态
signal():随机选择一个线程,解除等待状态(可能进入Runnable也可能Blocked),容易死锁
signalAll():解除条件等待集中所有线程的等待状态(可能进入Runnable也可能Blocked,因为还需要看是否能够获得lock)
注:以上三个方法,必须是lock锁的持有者才能调用
示例:保证静态共享的num,分别被5个加线程和减线程调用后,结果为0,并且调用过程中num不能小于0
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class ConditionThread extends Thread{
private static Lock lock = new ReentrantLock();
private static Condition condition = lock.newCondition();
private static int num = 0;//保证num不小于0
private String name;
public ConditionThread(String name) {
this.name = name;
}
@Override
public void run(){
lock.lock();
try {
if(name.equals("add")){
for(int i=0;i<10;i++){
num++;
}
condition.signalAll();//当num增加后,唤醒等待线程,唤醒的线程依然得等待lock
}else if(name.equals("minus")){
for(int i=0;i<10;i++){
while(num==0){//当num为零,且操作为minus时,线程等待并放弃锁
condition.await();
}
num--;
}
}
System.out.println(name+":"+num);
} catch (InterruptedException e) {
e.printStackTrace();
}finally{
lock.unlock();
}
}
public static void main(String[] args) {
for(int i=0;i<10;i++){
if(i%2!=0){
new ConditionThread("add").start();
}else{
new ConditionThread("minus").start();//先start,但await放弃lock
}
}
}
}
执行结果:
add:10
add:20
minus:10
add:20
minus:10
minus:0
add:10
minus:0
add:10
minus:0
synchronized:同步关键字,可修饰方法或者代码块。占用者:对象锁或类锁
注:static 和非static 分别属于类锁和对象锁,互不干扰,当类锁被占用,此时由对象去调用static方法也将被阻塞,同时注意,static方法是无法影响对象本身私有属性。
wait:释放对象锁,进入等待状态
notify:随机选择一个线程,解除等待状态(可能进入Runnable也可能Blocked),容易死锁
notifyAll:解除条件等待集中所有线程的等待状态(可能进入Runnable也可能Blocked,因为还需要看是否能够获得对象锁)
注:以上三个方法(属于Object),必须是对象锁的持有者才能调用
示例:完成主线程和自定义线程之间的来回跳转,以及跳转过程的线程状态变化
public class WaitingThread extends Thread{
private static byte[] lock = new byte[0];//创建最简易的lock
public WaitingThread(String name) {
super(name);
}
@Override
public void run(){
try {
synchronized (lock) {
System.out.println(this.getState());//wait前,RUNNABLE状态
System.out.println("WaitingThread give out the lock");
lock.wait();
System.out.println("WaitingThread get the lock");
System.out.println(this.getState());//获得lock,重回RUNNABLE状态
lock.notifyAll();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
WaitingThread w = new WaitingThread("WaitingThread");
w.start();
try {
Thread.sleep(1000);//给WaitingThread执行wait的时间
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lock) {
System.out.println("main thread get the lock!");
System.out.println(w.getState());//唤醒前,WAITING状态
lock.notifyAll();//唤醒WaitingThread
System.out.println(w.getState());//唤醒后,BLOCKED状态
try {
System.out.println("main give out the lock");
lock.wait();//让出锁给WaitingThread
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("main thread get the lock again!");
}
}
}
执行结果:
RUNNABLE
WaitingThread give out the lock
main thread get the lock!
WAITING
BLOCKED
main give out the lock
WaitingThread get the lock
RUNNABLE
main thread get the lock again!
volatile这个值的作用就是告诉JVM:对于这个成员变量不能保存它的副本,要直接与共享成员变量交互,也就保证在取值那一刻值是最新的。但是它不确保原子性(如果操作包含volatile修饰域本身)!
PS:多年之后发现这个理解有问题,volatile修饰变量依旧会在工作内存中操作,即依旧保持副本,只是通过控制多个读操作或写操作的原子性+有序性,来实现共享变量的可见性。
java.util.concurrent.atomic包中有许多用于原子操作的基础类型
示例:一并展示volatile和atomic在多线程下的执行情况
import java.util.concurrent.atomic.AtomicInteger;
public class AtomicThread extends Thread{
private static volatile int num =0;
private static AtomicInteger atomicNum =new AtomicInteger(0);
@Override
public void run(){
add();
atomicAdd();
}
public void add(){
for(int i =0;i<10000;i++){
num=num+1;//执行过程中num值被其他线程修改
}
System.out.println("add: "+num);
}
public void atomicAdd(){
for(int i =0;i<10000;i++){
atomicNum.incrementAndGet();//等价i++,但AtomicInteger具备原子性
}
System.out.println("atomicAdd: "+atomicNum.get());
}
public static void main(String[] args) {
for(int i=0;i<20;i++){
new AtomicThread().start();
}
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("num最终结果:"+num);
System.out.println("atomicNum最终结果:"+atomicNum.get());
}
}
执行结果:
num最终结果:155704
atomicNum最终结果:200000
join() 方法主要是让调用该方法的thread完成run方法里面的任务后,再执行原线程join()方法后面的代码。
示例:mainthread线程最后一轮时,jointhread中途插入执行
public class JoinThread extends Thread{
private Thread joinThread;
public JoinThread(Thread t) {
joinThread = t;
}
@Override
public void run(){
for(int i=0;i<3;i++){
System.out.println(i);
if(i==2){
try {
joinThread.join();//joinThread执行完成后再回来执行后面代码
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("--over--");
}
}
public static void main(String[] args) {
Thread joinThread = new Thread(){
@Override
public void run(){
try {
Thread.sleep(1000);//让mainThread先执行
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("join!!!!");
System.out.println("over!!!!");
}
};
JoinThread mainThread = new JoinThread(joinThread);
joinThread.start();
mainThread.start();
}
}
执行结果:
0
--over--
1
--over--
2
join!!!!
over!!!!
--over--
interrupt():向线程发出中断请求,线程不会立即中断,只是将中断状态变为true。如果遇到sleep阻塞,那么抛出异常,并且中断状态重新变为false。
static interrupted():测试线程是否被中断,副作用:当前中断状态变为false。
isInterrupted():测试线程是否被中断,中断状态不改变
示例:展示中断方法的效果
public class InterruptThread extends Thread{
@Override
public void run(){
while(!this.isInterrupted()){
for(int i = 1;i<=5;i++){
System.out.println(i);
if(i==2){
System.out.println("执行interrupt()");
this.interrupt();//此时线程不会停下
}
}
System.out.println("当前中断状态:"+this.isInterrupted());//中断状态不变
System.out.println("当前中断状态:"+Thread.interrupted());//中断状态将改变成为false
if(!this.isInterrupted()){//如果线程中断状态不为true
this.interrupt();//再次中断程序
}
/*try {
Thread.sleep(5000);
} catch (InterruptedException e) {//中断状态将改变成为false
e.printStackTrace();
this.interrupt();
}*/
}
}
public static void main(String[] args) {
new InterruptThread().start();
}
}
执行结果:
1
2
执行interrupt()
3
4
5
当前中断状态:true
当前中断状态:true
这三个方法被弃用了。为啥呢?
stop:终止所有未结束的方法,将导致对象被破坏,比如钱从一个账户到另一个,中途stop,那么钱去哪了?O(∩_∩)O~
suspend:用于挂起线程,需要其他线程调用resume来回复,容易导致死锁。比如,a线程挂起,并持有b线程需要的锁,而b线程要执行resume将a回复,可惜锁又被a占用着,那么程序死锁。
示例:自定义挂起,回复线程
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class ResumeThread extends Thread{
private Lock suspendLock = new ReentrantLock();
private Condition suspendCondition = suspendLock.newCondition();
private boolean suspend = false;//挂起标识
private void mySuspend(){
suspend = true;
}
private void myResume(){
suspend = false;
suspendLock.lock();//锁定
try{
suspendCondition.signalAll();//唤醒线程
}finally{
suspendLock.unlock();
}
}
@Override
public void run(){
while(true){
System.out.println("------ResumeThread------");
if(suspend){
suspendLock.lock();
try{
suspendCondition.await();//释放锁,进入等待状态
}catch (Exception e) {
e.printStackTrace();
}finally{
suspendLock.unlock();
}
}
sleep(1);
}
}
public static void main(String[] args) {
ResumeThread resumeT = new ResumeThread();
resumeT.start();
sleep(10);
System.out.println("-------suspend------");
resumeT.mySuspend();//挂起
System.out.println("-------resume------");
resumeT.myResume();//恢复
sleep(10);
System.out.println("-------suspend------");
resumeT.mySuspend();//再次挂起
System.exit(0);//结束程序
}
public static void sleep(long time){
try {
Thread.sleep(time);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
执行结果:
------ResumeThread------
------ResumeThread------
------ResumeThread------
...
-------suspend------
-------resume------
------ResumeThread------
------ResumeThread------
------ResumeThread------
...
-------suspend------
------ResumeThread------
Thread.yield( )方法,译为线程让步,就是说使用它的线程会把自己CPU执行的时间让掉,让自己或者其它的线程运行,注意并不是单纯的让给其他线程。它能让当前线程由“运行状态”进入到“就绪状态”,此时当前线程和其他线程一同竞争CPU的使用权。
爱家人,爱生活,爱设计,爱编程,拥抱精彩人生!