1、A线程正在执行一个对象中的同步方法,B线程是否可以同时执行同一个对象中的非同步方法? (可以)
2、同上,B线程是否可以同时执行同一个对象中的另一个同步方法? (不可以)
A线程正在执行一个对象中的同步方法,在同步方法中能不能再调用本对象的另一个同步方法执行? (可以)
3、线程抛出异常会释放锁吗? (会的,如果不想释放锁,最好处理异常)
4、volatile和synchronized区别?
5、写一个程序,证明AtomXXX类比synchronized更高效
6、AtommicXXX类可以保证可见性吗?请写一个程序来证明 (可以)
7、写一个程序证明AtomXXX类的多个方法并不构成原子性,不能构成原子操作 最好操作代码段加同步段synchronized(this){}
8、写一个程序模拟死锁
9、写一个程序,在main线程中启动100个线程,100个线程完成后,主线程打印“完成”,使用join()和countdownlatch都可以完成,请比较异同,循环栅栏。
10、++ – 运算在多线程中,是不是原子操作? (不是操作,可以使用原子类)
java.util.concurrent.atomic.AtomicInteger
volatile和synchronized的区别
1.volatile本质是在告诉jvm当前变量在寄存器(工作内存)中的值是不确定的,需要从主存中读取;
synchronized则是锁定当前变量,只有当前线程可以访问该变量,其他线程被阻塞住。
2.volatile仅能使用在变量级别;synchronized则可以使用在变量、方法、和类级别的。
3.volatile仅能实现变量的修改可见性,不能保证原子性;而synchronized则可以保证变量的修改
可见性和原子性
4.volatile不会造成线程的阻塞;synchronized可能会造成线程的阻塞。
5.volatile标记的变量不会被编译器优化;synchronzied标记的变量可以被编译器优化。
写一个程序,证明AtomXXX类比synchronized更高效
public class Test7_1 {
AtomicInteger atomicCount = new AtomicInteger();
int count = 0;
public static void main(String[] args) {
// 写一个程序,证明AtomXXX类比synchronized更高效
var t1 = new Test7_1();
// atomicXX
long time1 = time(t1::m);
System.out.println(t1.atomicCount);
// synchronized
long time2 = time(t1::m2);
System.out.println(t1.count);
try {
TimeUnit.SECONDS.sleep(12);
} catch (Exception e){
e.printStackTrace();
}
System.out.print("AtomicXxx");
System.out.println(time1);
System.out.print("synchronized");
System.out.println(time2);
}
public static long times(Runnable runnable){
long startTime = System.currentTimeMillis();
for (int i = 0; i < 10; i++) {
new Thread(runnable,"thread-" + i).start();
}
try {
TimeUnit.SECONDS.sleep(5);
} catch (Exception e){
e.printStackTrace();
}
long endTime = System.currentTimeMillis();
return endTime - startTime;
}
public static long time(Runnable runnable){
List<Thread> threads = new ArrayList<>();
long startTime = System.currentTimeMillis();
for (int i = 0; i < 10; i++) {
threads.add(new Thread(runnable,"thread-" + i));
}
threads.forEach(Thread::start);
threads.forEach(o -> {
try {
o.join();
} catch (InterruptedException e){
e.printStackTrace();
}
});
long endTime = System.currentTimeMillis();
return endTime - startTime;
}
void m(){
for (int i = 0; i < 1000000; i++) {
atomicCount.incrementAndGet(); // 原子操作
}
}
void m2(){
for (int i = 0; i < 1000000; i++) {
synchronized (this){
count++;
}
}
}
}
运行结果:
AtommicXXX类可以保证可见性吗?请写一个程序来证明 (可以)
public class Test7_2 {
static AtomicBoolean flag = new AtomicBoolean(true);
// volatile 可见性,不保证原子操作
static volatile boolean flag2 = true;
static boolean flag3 = true;
public static void vol(String[] args){
// AtommicXXX类可以保证可见性吗?请写一个程序来证明 (可以)
// 线程一
new Thread(() -> {
String tn = Thread.currentThread().getName();
System.out.printf("线程启动:%s%n",tn);
while (flag2){
}
System.out.printf("线程:%s结束了%n",tn);
},"T1").start();
// main线程
try {
TimeUnit.SECONDS.sleep(3);
flag2 = false;
System.out.println("Main设置了flag = flase;");
} catch (Exception e){
e.printStackTrace();
}
}
public static void main(String[] args) {
// 线程一
new Thread(() -> {
String tn = Thread.currentThread().getName();
System.out.printf("线程启动:%s %n",tn);
while (flag.get()){
}
System.out.printf("线程:%s结束了%n",tn);
},"T1").start();
// main
try {
TimeUnit.SECONDS.sleep(3);
flag.set(false);
System.out.println("Main设置了(flag.set(flase);)");
} catch (Exception e){
e.printStackTrace();
}
}
}
不要以字符串常量作为锁的对象 synchronized("hello"){ }
/**
* author:nnzb
* email:xymhxxqw@163.com
* time:14:45
*
* 不要以字符串常量作为锁定对象
* 在下面m1 m2 其实锁定的是同一个对象
* 这种情况下还会发生比较诡异的现象,比如你用到了一个类库,在该类库中的代码锁定了"Hello",
* 但是你读不到源码,所以你在自己的代码中锁定了"Hello",这时候有可能发生非常诡异的死锁阻塞,
* 因为你的程序和你用到的类库不经意间使用了同一把锁。
*/
public class Test7_3 {
String s1 = "Hello";
String s2 = "Hello";
public static void main(String[] args) {
Test7_3 t = new Test7_3();
new Thread(t::m1).start();
new Thread(t::m2).start();
}
void m1(){
synchronized (s1){
while (true){
System.out.println("m1");
}
}
}
void m2(){
synchronized (s2){
while (true){
System.out.println("m2");
}
}
}
}
写一个程序证明AtomXXX类的多个方法并不构成原子性
1.
public class Test7_4 {
AtomicInteger count = new AtomicInteger(0);
public static void main(String[] args) {
// 写一个程序证明AtomXXX类的多个方法并不构成原子性 1
Test7_4 t = new Test7_4();
List<Thread> threads = new ArrayList<>();
for (int i = 0; i < 10; i++) {
threads.add(new Thread(t::m,"thread" + i));
}
threads.forEach(Thread::start);
threads.forEach((o) -> {
try {
// join()方法阻塞调用此方法的线程,直到线程t完成,此线程再继续。
// 通常用于在main()主线程内,等待其他线程完成再结束main()主线程。
o.join(); // 相当于在main线程中同步o线程,o执行完了,main线程才有执行的机会
} catch (InterruptedException e){
e.printStackTrace();
}
});
System.out.println(t.count);
}
void m(){
for (int i = 0; i < 10000; i++) {
if (count.get() < 100 && count.get() >= 0){ // 如果未加锁,之间还会有其他线程插进来
count.incrementAndGet();
}
}
}
}
2.
public class Test7_4_1 {
AtomicInteger count = new AtomicInteger(0);
public static void main(String[] args) {
// // 写一个程序证明AtomXXX类的多个方法并不构成原子性 2
var t = new Test7_4_1();
for (int i = 0; i < 10000; i++) {
new Thread(t::m,"T" + i).start();
}
try {
TimeUnit.SECONDS.sleep(2);
System.out.println(t.count.get());
} catch (Exception e){
e.printStackTrace();
}
}
void m(){
for (int i = 0; i < 10000; i++) {
synchronized (this){
if (count.get() < 100 && count.get() >= 0){ // 如果未加锁,之间还会有其他线程插进来
count.incrementAndGet();
}
}
}
}
}
写一个程序,在main线程中启动100个线程,100个线程完成后,主线程打印“完成”
1.
public class Test7_5 {
public static void main(String[] args) {
// 写一个程序,在main线程中启动100个线程,100个线程完成后,主线程打印“完成” 1
CountDownLatch latch = new CountDownLatch(100);
for (int i = 0; i < 100; i++) {
new Thread(() -> {
String tn = Thread.currentThread().getName();
System.out.printf("%s : 开始执行...%n", tn);
System.out.printf("%s : 执行完成,程序结束。%n", tn);
latch.countDown();
},"T" + i).start();
}
try {
latch.await();
} catch (InterruptedException e){
e.printStackTrace();
}
System.out.println("------------------------------------------");
System.out.println("100个线程执行完了。");
String tn = Thread.currentThread().getName();
System.out.printf("%s : 执行完成,程序结束。%n",tn);
}
}
2.
public class Test7_5_1 {
public static void main(String[] args) {
// 写一个程序,在main线程中启动100个线程,100个线程完成后,主线程打印“完成” 2
for (int i = 0; i < 100; i++) {
var t = new Thread(() -> {
String tn = Thread.currentThread().getName();
System.out.printf("%s : 开始执行...%n", tn);
System.out.printf("%s : 执行完成,程序结束。%n", tn);
}, "T" + i);
t.start();
try {
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("---------------------------------");
System.out.println("100个线程执行完了。");
String tn = Thread.currentThread().getName();
System.out.printf("%s : 执行完成,程序结束。%n", tn);
}
}