线程基础知识(二)
线程基础知识(二)
什么叫脏读?
多个进程并发访问同一个资源如果没加锁可能会造成获取的数据有问题。
比如i++操作(假设i初始值为0)如下图两个线程对执行i++而且顺序是1、2、3、4、5、6,i的最后的值是1.
synchronized
synchronized就是加锁确保原子性。
synchronized可以给类或对象加锁。
加锁的方式:加在方法上面或synchronized() 块
- synchronized 方法 或 synchronized static 方法
- synchronized(对象) 或 synchronized(.class)
public class TestSynchronized {
public static void main(String[] args) {
Dog dog = new Dog();
for (int i = 1; i <= 10; i++) {
new Thread(new RunRunnable(dog)).start();
}
for (int i = 1; i <= 10; i++) {
new Thread(new BarkRunnable(dog)).start();
}
for (int i = 1; i <= 10; i++) {
new Thread(new EatRunnable()).start();
}
}
}
class RunRunnable implements Runnable {
private Dog dog;
public RunRunnable(Dog dog) {
this.dog = dog;
}
@Override
public void run() {
dog.run();
//dog.runTwo();
}
}
class BarkRunnable implements Runnable {
private Dog dog;
public BarkRunnable(Dog dog) {
this.dog = dog;
}
@Override
public void run() {
dog.bark();
dog.barkTwo();
}
}
class EatRunnable implements Runnable {
@Override
public void run() {
Dog.eat();
Dog.eatTwo();
}
}
class Dog {
public synchronized void run() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " " + "Dog " + " run!");
}
public void runTwo() {
synchronized (this) {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " " + "Dog " + " run!");
}
}
public void bark() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " " + "Dog wow!!!");
}
public void barkTwo() {
synchronized (this) {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " " + "Dog wow!!!");
}
}
public synchronized static void eat() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " " + "Dog eat!!!");
}
public static void eatTwo() {
synchronized (Dog.class) {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " " + "Dog eat!!!");
}
}
}
synchronized具有锁重入的功能
自己可以再次获取自己的内部锁 ,可重入锁也支持在父子类继承的环境中
public class TestExtendSyn {
public static void main(String[] args) {
Son son = new Son();
Thread thread1 = new Thread(new MySynRunnable(son));
Thread thread2 = new Thread(new MySynRunnable(son));
Thread thread3 = new Thread(new MySynRunnable(son));
thread1.start();
thread2.start();
thread3.start();
}
}
class MySynRunnable implements Runnable {
private Son son;
public MySynRunnable(Son son) {
this.son = son;
}
@Override
public void run() {
son.talk();
}
}
class Father {
public synchronized void say() {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " I'm father!!!");
}
}
class Son extends Father {
public synchronized void talk() {
say();
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " I'm son");
}
}
当一个线程执行的代码出现异常时,其所持有的锁会自动释放
public class Test {
public static void main(String[] args) {
MyRunnable myRunnable=new MyRunnable(new Service());
Thread thread=new Thread(myRunnable);
Thread thread1=new Thread(myRunnable);
thread.setName("a");
thread.start();
thread1.start();
}
}
class Service {
public synchronized void testMethod() {
System.out.println(Thread.currentThread().getName() + " Start " + System.currentTimeMillis());
if (Thread.currentThread().getName().equals("a")) {
Integer.parseInt("a");//异常
}
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " end " + System.currentTimeMillis());
}
}
class MyRunnable implements Runnable{
private Service service;
public MyRunnable(Service service){
this.service=service;
}
@Override
public void run() {
service.testMethod();
}
}
synchronized修饰方法弊端
synchronized关键字修饰的方法在某些条件下有弊端,比如A线程调用同步方法执行一个长时间的任务,那么B线程则必须等待比较长时间。某些时候可以用synchronized同步语句块来优化。有些异步的语句可以放到synchronized块外面。
public class Shortcoming {
public static void main(String[] args) {
Person aaa=new Person("aaa",20);
Person bbb=new Person("bbb",11);
Thread thread1=new Thread(new MyRunnableOne(aaa));
Thread thread2=new Thread(new MyRunnableOne(aaa));
Thread thread3=new Thread(new MyRunnableTwo(bbb));
Thread thread4=new Thread(new MyRunnableTwo(bbb));
thread1.start();
thread2.start();
// thread3.start();
// thread4.start();
}
}
class MyRunnableOne implements Runnable {
private Person person;
public MyRunnableOne(Person person) {
this.person = person;
}
@Override
public void run() {
person.say();
}
}
class MyRunnableTwo implements Runnable {
private Person person;
public MyRunnableTwo(Person person) {
this.person = person;
}
@Override
public void run() {
person.sayTwo();
}
}
class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public synchronized void say() {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//注意这边age++
System.out.println(name + " " + age++);
}
public void sayTwo() {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (this) {
System.out.println(name + " " + age++);
}
}
}
synchronized(this)和synchronized(非this对象)
synchronized(非this对象)具有的优点:如果在一个类中有很多个synchronized方法,虽然能实现同步但是会阻塞,会影响效率。但如果使用同步代码块锁非this对象,则synchronized(非this)代码块中的程序与同步方法是异步的,不与其他锁this同步方法争抢this锁,则可大大提高运行效率。分开是对不是共享的资源。
public class Shortcoming {
public static void main(String[] args) {
Person aaa=new Person("aaa",20);
Thread thread1=new Thread(new MyRunnableAge(aaa));
Thread thread2=new Thread(new MyRunnableName(aaa));
thread1.start();
thread2.start();
}
}
class MyRunnableAge implements Runnable {
private Person person;
public MyRunnableAge(Person person) {
this.person = person;
}
@Override
public void run() {
person.getAge();
}
}
class MyRunnableName implements Runnable {
private Person person;
public MyRunnableName(Person person) {
this.person = person;
}
@Override
public void run() {
person.getName();
}
}
class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public void getName(){
synchronized (this){
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+" "+name);
}
}
public void getAge(){
synchronized (this){
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+" "+age);
}
}
}
下面这种就比上面那个效果好。
public class Shortcoming {
public static void main(String[] args) {
Person aaa = new Person("aaa", 20);
Thread thread1 = new Thread(new MyRunnableAge(aaa));
Thread thread2 = new Thread(new MyRunnableName(aaa));
thread1.start();
thread2.start();
}
}
class MyRunnableAge implements Runnable {
private Person person;
public MyRunnableAge(Person person) {
this.person = person;
}
@Override
public void run() {
person.getAge();
}
}
class MyRunnableName implements Runnable {
private Person person;
public MyRunnableName(Person person) {
this.person = person;
}
@Override
public void run() {
person.getName();
}
}
class Person {
private String name;
private int age;
private Object nameObj = new Object();
private Object ageObj = new Object();
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public void getName() {
synchronized (nameObj) {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " " + name);
}
}
public void getAge() {
synchronized (ageObj) {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " " + age);
}
}
}
参考Java多线程编程核心技术
如果有什么错误的请指出十分感谢!!
才学疏浅,有什么问题请大家指出来。十分感谢!