1. 关于volatile关键字
2. 关于join的基础知识
join()方法的作用是等待线程对象销毁。
join()方法具有能使线程排队运行的作用,有点类似于同步的效果。
join与synchronize的区别:
join在内部使用wait()方法进行等待,底层用wait()来实现。
synchronize关键字是以“对象监视器”为原理做同步操作。
join()除了无参方法之外,还重载了join(毫秒)的方法,此方法与sleep(毫秒)的区别是:
join()操作底层用wait()来实现,可以释放锁。
sleep()不会释放锁。
- 场景案例:使用2个线程,分别从1加到100,然后两个线程的结果求和。
直接贴代码:
public class JoinMain {
public volatile static int i = 0;
public static class AddThread extends Thread {
@Override
public void run() {
for (int j= 0; j < 101; j++){
try {
Thread.sleep(10);
i+=j;
System.out.println("thread-1:"+i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static class AddThread2 extends Thread {
@Override
public void run() {
for (int k = 0; k < 101; k++){
try {
Thread.sleep(10);
i+=k;
System.out.println("thread-2:"+i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String args[]) throws InterruptedException {
AddThread at = new AddThread();
AddThread2 at2 = new AddThread2();
at.start();
at.join();
at2.start();
at2.join();
System.out.println("testResult:"+i);
}
}
其中,public volatile static int i = 0;保证了有一个全局的不变的数据结构存在,方便计数。
join函数确保了两个线程不会交叉计算。
其中,
at.start();
at.join();
at2.start();
at2.join();
这个顺序不能变,否则结果会乱掉。
- 那么,如果想使用如下代码实现上述功能呢?
thread1.start();
thread2.start();
thread1.join();
thread2.join();
这时候,就要用到 synchronized 关键字了
由于刚刚的例子,我们使用了继承Thread方法的模式,本次,我们使用实现Runnable接口的方式。
/**
* @className:
* @Description:
* @author: KevinHui
* @createTime:2018/12/17 11:23
*/
public class SyncJoinMain implements Runnable {
//开启一个线程
static SyncJoinMain syncJoinMain = new SyncJoinMain();
static volatile int i = 0;
@Override
public void run() {
for(int t = 0; t<101;t++){
synchronized (syncJoinMain){
i+=t;
}
}
}
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(syncJoinMain);
Thread t2 = new Thread(syncJoinMain);
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(i);
}
}
执行结果为10100
这里要注意的是,synchronized锁住的必须是实现了Runnable接口或者继承了Thread的对象,而不能是其他类型。错误案例,请看如下代码:
public class BadLockOnInteger implements Runnable {
public static Integer i = 0;
static BadLockOnInteger instance = new BadLockOnInteger();
@Override
public void run() {
for (int j = 0; j < 1000000; j++) {
synchronized (i) {//这里同步的并不是同一个对象,因为i是以Integer关键字创建的
//正确做法应该是 synchronized (instance)
i++;
}
}
}
/**
* 得到的结果并不是2000000,在多线程的操作中出现了错误
*
* @param args
* @throws InterruptedException
*/
public static void main(String args[]) throws InterruptedException {
Thread thread1 = new Thread(instance);
Thread thread2 = new Thread(instance);
thread1.start();
thread2.start();
thread1.join();
thread2.join();
System.out.println(i);
}
}
synchronized和volatile的区别
1. synchronized关键字是防止多个线程同时执行一段代码,而volatile关键字在某些情况下性能要优于synchronized。
2. 但是要注意volatile关键字是无法替代synchronized关键字的,因为volatile关键字无法保证操作的原子性。
synchronized和Lock的区别