大家好,又见面了,我是你们的朋友全栈君。
代码语言:javascript复制package com.xiaoyexinxin.ThreadLearn;
public class MyThread extends Thread{
private int count=5;
//synchronized加锁
public synchronized void run(){
count --;
System.out.println(this.currentThread().getName() "count=" count);
}
public static void main(String args[]{
MyThread myThread=new MyThread();
Thread t1=new Thread(myThread,"t1");
Thread t2=new Thread(myThread,"t2");
Thread t3=new Thread(myThread,"t3");
Thread t4=new Thread(myThread,"t4");
Thread t5=new Thread(myThread,"t5");
t1.start();
t2.start();
t3.start();
t4.start();
t5.start();
}
}
执行结果:
代码语言:javascript复制t1count=4
t2count=3
t4count=2
t5count=1
t3count=0
每次的运行的结果走的线程顺序都不一样,说明是通过cpu分配的先后顺序。
下面如果我们把synchronized去掉,就会出现线程不安全的情况,多次执行程序会出现下面的结果:
代码语言:javascript复制t1count=3
t5count=1
t4count=2
t2count=3
t3count=0
我们分析下为什么会出现这种情况:因为没有加锁,从结果可以看出,可能是t1线程先抢到执行资源,执行run方法,count减一,count为4,然后在他还没执行system输出语句时,资源被另外一个线程夺取了,这个线程开始执行run方法,执行了count减一,此时count变为3了。然后先前那个资源这时又重新夺取了资源,执行system输出语句,可是它此时再去取count值时,取到的是3,已经不是它应该得到4了。现实中,我们想到的的数据结果是43210都有,可是由于线程不安全的影响会出现数据缺失。所以这就是所谓的线程不安全。
另外可以看出执行结果的顺序比较混乱,说明先夺取资源执行方法体不一定先输出,先输出也不一定先打印到控制台。
下面我们分析加锁的影响:当多个线程访问myThread的run方法是,以排队的方式进行处理(这里排队是按照CPU分配的先后顺序而定的),一个线程想要执行synchronized修饰的方法里的代码,首先是尝试获得锁,如果得到锁,执行synchronized代码体内容,直到整个方法执行完才会释放锁让别的线程进来,这样就不会出现上面数据缺失的现象,拿不到锁,这个线程就会不断的尝试获得这把锁,直到拿到为止,而且是多个线程同时去竞争这把锁。(也就是会有锁竞争的问题,锁竞争问题对于线程比较少的情况倒是影响不大,但是如果线程很多的话,同一时间有很多线程要抢夺一把锁,CPU会瞬间达到很高的占用率,很有可能就宕机了,其实这和我们双十一抢购商品是一样的,在0点0分0秒瞬间有几十万几百万的请求过来,这对于一般的服务器来说,瞬间就崩溃了,因此我们尽量避免 锁竞争的问题)
示例二:多个线程多个锁
多个线程多个锁:多个线程,每个线程都可以拿到自己指定的锁,分别获得锁之后,执行synchronized方法体的内容。
代码语言:javascript复制package com.xiaoyexinxin.ThreadLearn;
public class MultiThread2 {
private int num=0;
public synchronized void printNum(String tar){
if(tar.equals("a")){
num=100;
System.out.println("线程a");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}else{
num=200;
System.out.println("线程b");
}
System.out.println("输出的线程是:" tar ",num:" num);
}
public static void main(String[] args) {
final MultiThread2 mThread1=new MultiThread2();
final MultiThread2 mThread2=new MultiThread2();//设为final表示此对象不能别继承,没有子类。如果是方法被定义成final,则此方法不能被重写
Thread t1=new Thread(new Runnable() {
public void run() {
// TODO Auto-generated method stub
mThread1.printNum("a");
}
});
Thread t2=new Thread(new Runnable() {
public void run() {
// TODO Auto-generated method stub
mThread2.printNum("b");
}
});
t1.start();
t2.start();
}
}
执行结果:
代码语言:javascript复制线程a
线程b
输出的线程是:b,num:200
输出的线程是:a,num:100
我们运行该类,会看到如下结果,可以看到线程t1和t2互不影响,各自运行各自的,我们让t1线程休息了1秒钟,这样t2线程执行完了,t1线程才执行完。synchronized取得的锁都是对象锁,而不是把一段代码(方法)当做锁,所以上面代码中哪个线程先执行synchronized关键字的方法,那个线程就持有该方法所属对象的锁(Lock),两个对象,线程获得的就是两个不同的锁,他们互不影响。
有一种情况则是相同的锁,那就是在静态方法上加synchronized关键字,表示锁定.class类,类一级别的锁(独占.class类)。为了看效果,我们在变量num前面加static关键字, 给printNum方法加上static关键字,如下图所示。
结果:
代码语言:javascript复制线程a
输出的线程是:a,num:100
线程b
输出的线程是:b,num:200
这样情况就跟单个线程单个锁执行的效果一样了。要等先进来的线程把run方法执行完了才能执行另外的线程。
下面我们聊聊对象锁的同步和异步
同步:synchronized
同步的概念就是共享,我们要牢牢记住”共享”这两个字,如果不是共享的资源,就没有必要进行同步(举个简单例子,我们存款和取款,一定要同步,因为不同步的 话,资金就乱了,这是不能忍受的)。
异步:asynchronized
异步的概念就是独立,相互之间不受到任何制约。就好像我们学习http的时候,在页面发起的Ajax请求,我们还可以继续浏览或操作页面的内容,二者之间没有任何关系。
同步的目的就是为了线程安全,其实对于线程安全来说,需要满足两个特性:原子性(同步)和可见性
异步的目的及时快了,能同时干多件事。
异步的例子:
代码语言:javascript复制package com.xiaoyexinxin.ThreadLearn;
public class MultiThreadAsyn {
public synchronized void method1(){
System.out.println(Thread.currentThread().getName());
try {
Thread.sleep(3000);
System.out.println("method1结束");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public void method2(){
System.out.println(Thread.currentThread().getName());
}
public static void main(String[] args) {
// TODO Auto-generated method stub
final MultiThreadAsyn mta=new MultiThreadAsyn();
Thread t1=new Thread(new Runnable() {
public void run() {
// TODO Auto-generated method stub
mta.method1();
}
});
Thread t2=new Thread(new Runnable() {
public void run() {
// TODO Auto-generated method stub
mta.method2();
}
});
t1.start();
t2.start();
}
}
执行结果是:
代码语言:javascript复制Thread-0
Thread-1
method1结束
运行上面的main方法,我们会看到t1和t2同时打印出来了,这说明此时method1和method2是异步执行的。
下面我们给method2方法也加上synchronized,看看执行结果
代码语言:javascript复制Thread-0
method1结束
Thread-1
再运行main方法,这回我们看到t1信息输出4秒后才会看到t2信息,出现这种情况的原因是,我们只new了一个对象,而synchronized关键字锁的便是对象,由于method1和method2现在都被synchronized关键字修饰,因此只要是访问该对象的这两个方法的线程都会进行同步操作,也就是说谁先拿到对象的锁谁便先执行,执行完之后,释放锁,这时下一个线程才能执行。
A线程先持有object对象的Lock锁,B线程如果在这个时候调用对象中的同步(synchronized)方法则需要等待,也就是同步。
A线程先持有object对象的Lock锁,B线程可以以异步的方式调用对象中的非synchronized修饰的方法。
4,使用继承Thread实例:
代码语言:javascript复制package client.cfca;
/**
*
* @author liuxin
* @date 2018年8月1日
*/
public class ad {
public static void main(String[] args) {
Dog t1=new Dog();
Dog t2=new Dog();
Dog t3=new Dog();
Dog t4=new Dog();
t1.setName("t1");
t2.setName("t2");
t3.setName("t3");
t4.setName("t4");
t1.start();
t2.start();
t3.start();
t4.start();
}
}
class Dog extends Thread{
private int count=3;
public void run(){
try {
synchronized (this) {
for(int i=0;i<3;i ){
System.out.println(Thread.currentThread().getName() " count=" count--);
sleep(5000);
}
System.out.println(Thread.currentThread().getName());
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
结果:每隔5s打印4条记录
代码语言:javascript复制t1 count=3
t3 count=3
t4 count=3
t2 count=3
t1 count=2
t3 count=2
t4 count=2
t2 count=2
t1 count=1
t3 count=1
t4 count=1
t2 count=1
t1
t3
t4
t2
发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/106151.html原文链接:https://javaforall.cn