线程同步

来源:互联网 发布:仿真分析软件 编辑:程序博客网 时间:2024/05/23 19:10
使用线程同步的原因:
    解决多个线程操作同一个资源所产生的并发问题。
出现问题的代码:
class Ticket implements Runnable
{
private int num = 100;
 
Object obj = new Object();
 
public void run() {
while (true) {
if (num > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
}
 
System.out.println(Thread.currentThread().getName()+".....sale...." + num--);
}
}
}
}
 
public class TicketDemo {
public static void main(String[] args) {
 
Ticket t = new Ticket();// 创建一个线程任务对象。
 
Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
Thread t3 = new Thread(t);
Thread t4 = new Thread(t);
 
t1.start();
t2.start();
t3.start();
t4.start();
 
}
}
打印结果:


打印的车票出现了0,-1,-2这些数字,原因就是在15行执行前,线程可能切换,然后下一次判断是小于0,但是当切换回来的时候,num已经小于0,所以出现不合理数据。

解决方法:
    利用线程之间的同步来解决问题,要解决上面的问题,必须保证下面这段代码的"原子性"。

对象锁       --必须有一个对象
    Object object = new Object();
    synchronized(object){
        //被同步代码块
    }
            --锁字节码文件
    synchronized(int.class){
        //被同步的代码块
    }
            --this锁
    synchronized(this){
        //被同步的代码块
    }

实现Runnable接口加锁
public class TestBank01 {
 
public static void main(String[] args) {
BankThread bt = new BankThread();
new Thread(bt, "A").start();
new Thread(bt, "B").start();
}
}
 
class BankThread implements Runnable {
 
private int money = 200000;
private Object object = new Object();
 
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "排队 谢谢合作");
 
// 对象锁 此处应该同步
synchronized (object) {
System.out.println(Thread.currentThread().getName() + "取钱中");
money -= 20000;
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+ "取了20000, 还剩下:" + money);
}
}
}
继承Thread类加锁:
public class TestBank02 {
 
public static void main(String[] args) {
BankThread_ bt1 = new BankThread_("A");
 
BankThread_ bt2 = new BankThread_("B");
bt1.start();
bt2.start();
}
 
}
 
class BankThread_ extends Thread {
//此处应该用static修饰,static 修饰的成员属于类,这样才能共享money
private static int money = 20000;
 
public BankThread_(String name) {
super(name);
}
 
@Override
public void run() {
 
System.out.println(Thread.currentThread().getName() + "取钱");
//此处应该上一样的锁,不能用属于此类中的所有对象都拥有的属性上锁
synchronized (BankThread_.class) {
money -= 2000;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "取了2000, 还剩"+ money);
}
 
}
}
注意:继承Thread类上锁和变量共享。

死锁问题:
    一旦有多个线程,且它们都要争用对多个锁的独立访问,那么就有可能发生死锁。
  死锁代码:
public class TestDeadLock01 {
public static void main(String[] args) {
DeadLock01 dl = new DeadLock01();
new Thread(dl, "AAA").start();
new Thread(dl, "BBB").start();
}
}
 
class DeadLock01 implements Runnable {
 
private Object objA = new Object();
private Object objB = new Object();
 
@Override
public void run() {
String name = Thread.currentThread().getName();//获取名称
if(name.equals("AAA")){
synchronized (objA) {
System.out.println(name +"获得objA");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (objB) {
System.out.println(name +"获得objB");
}
}
}else{
synchronized (objB) {
System.out.println(name +"获得objB");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (objA) {
System.out.println(name +"获得objA");
}
}
}
}
}
package com.mixm;
 
public class TestDeadLock02 {
 
public static void main(String[] args) {
new DeadLock02("AAA").start();
new DeadLock02("BBB").start();
}
}
 
class DeadLock02 extends Thread {
 
//注意两个锁,设置为static
private static Object objA = new Object();
private static Object objB = new Object();
public DeadLock02(String name){
super(name);
}
 
@Override
public void run() {
String name = Thread.currentThread().getName();
if(name.equals("AAA")){
synchronized (objA) {
System.out.println(name +"获得objA");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (objB) {
System.out.println(name +"获得objB");
}
}
}else{
synchronized (objB) {
System.out.println(name +"获得objB");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (objA) {
System.out.println(name +"获得objA");
}
}
}
 
}
 
}

ThreadLocal:
    线程局部变量,功能: 为了每一个使用该变量的线程都提供一个变量值的副本,使每一个线程都可以独立地改变自己的副本。
    构造方法: public void ThreadLocal();

    方法:
        public void set(Object value): 将此线程局部变量的当前线程副本中的值设置为指定值。
        public T get(): 返回此线程局部变量的当前线程副本的值
        public void remove(): 移除此线程局部变量当前线程的值。如果此线程局部变量随后被当前线程读取,且这期间当前线程没有设置其值,则将调用其initValue()方法重新初始化其值。这将导致在当前线程多次调用initialValue方法。
        protected T initalValue(): 返回此线程局部变量的当前线程的"初始值"。线程第一个调用get()方法访问变量时将调用此方法,单如果线程之前调用了set(T)方法,则不会对该线程再调用initalValue方法。通常,此方法对每个线程最多调用一次,但如果在调用get()之后又调用了remove(),则有可能再次调用此方法。(protected提供给子类重写)

package com.mixm;
 
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Random;
 
public class TestThreadLocal {
 
public static void main(String[] args) {
MyThreadLocal02 mt = new MyThreadLocal02();
 
new Thread(mt, "AAA").start();
new Thread(mt, "BBB").start();
new Thread(mt, "CCC").start();
new Thread(mt, "DDD").start();
 
}
}
 
class MyThreadLocal02 implements Runnable {
private SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss SS");
 
private ThreadLocal<Date> date = new ThreadLocal<Date>();
 
private ThreadLocal<String> names = new ThreadLocal<String>() {
protected String initialValue() {
return Thread.currentThread().getName();
}
};
 
@Override
public void run() {
try {
 
// 这儿用的是随机睡眠,如果睡眠的时间是一致的话,看不出来效果
Thread.sleep(new Random().nextInt(1000));
} catch (InterruptedException e1) {
e1.printStackTrace();
}
 
date.set(new Date());
 
System.out.println(names.get() + " " + sdf.format(date.get()));
 
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
 
System.out.println(names.get() + " " + sdf.format(date.get()));
 
// 移除之后添加进来的值
// date.remove();
// System.out.println(names.get() + "*******" + sdf.format(date.get()));//这儿会抛异常
 
// 移除在 initialValue()已经初始化的值移除此线程局部变量当前线程的值。
//正常输出,输出的值是initialValue()中初始化的内容
names.remove();
System.out.println(names.get() + "-------" + sdf.format(date.get()));
}
}

ThreadLocal类和同步的区别:
    ThreadLocal类不能替代同步的机制,两者面对的问题领域不同。同步机制是为了同步多个线程对相同资源的并发访问,是为了多个线程之间进行通信的有效方式;而ThreadLocal是个例多个线程的数据共享,从根本上就不在多个线程线程之间共享资源(变量)。这样当然不需要对多个线程进行同步。
    所以,如果你需要进行多个线程之间进行通信,则使用同步进制。
    如果需要个例多个线程之间的共享冲突,可以使用ThreadLoacl。




0 0
原创粉丝点击