java-线程同步问题

1.Timer类

import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

public class TimerDemo {

    public static void main(String[] args) throws Exception{
        /*
         * Timer类的演示:ava.util.Timer类是一个定时器,可以让它定时做某件事情,
         * 方法:schedule(task,delay,period);//在delay毫秒后执行task,并以period为周期循环执行。
         *
         * */
        Timer t=new Timer();
//        t.schedule(new TimerTask ()
//        {
//            @Override
//            public void run() {
//                System.out.println("bombing!!!");
//             }
//         }, 3000);

        t.schedule(new MyTimerTask(), 3000);
        //每隔一秒,打印出时间
        while(true)
        {
            System.out.println(new Date().getSeconds());
            Thread.currentThread().sleep(1000);
        }
      }
}
//实现了每3秒爆炸,每2秒爆炸,并以此循环
//写成类的原因是:我们要不停的使用该类中的run代码,
//这样可以通过不停的new对象出来重复使用run代码
//int i写成静态的原因是:new的对象要共享数据i,所以写成静态
//i=(i+1)%count,的结果是i以count的为周期,不停循环。
//注意:这里不能写成TimerDemo的内部类,因为一个非静态的内部类相当于其他非静态的方法,不能在
//     里面声明静态变量(static int i)!!或静态方法!!
class MyTimerTask extends TimerTask {
    static int i=0;
    @Override
    public void run() {
            System.out.println("bombing!!!");
            new Timer().schedule(new MyTimerTask() , (i+2)*1000);
            i=(i+1)%2;
     }
}

2.多个线程访问共享对象和数据的方式

2.1 如果每个线程执行的代码相同,可以使用同一个Runnable对象,这个Runnable对象中有那个共享数据,例如,买票系统就可以这么做。

2.2 如果每个线程执行的代码不同,这时候需要用不同的Runnable对象,有如下两种方式来实现这些Runnable对象之间的数据共享:

a.将共享数据封装在另外一个对象中,然后将这个对象逐一传递给各个Runnable对象。每个线程对共享数据的操作方法也分配到那个对象身上去完成,

这样容易实现针对该数据进行的各个操作的互斥和通信。

b.将这些Runnable对象作为某一个类中的内部类,共享数据作为这个外部类中的成员变量,每个线程对共享数据的操作方法也分配给外部类,以便实现

对共享数据进行的各个操作的互斥和通信,作为内部类的各个Runnable对象调用外部类的这些方法。

上面两种方式的组合:将共享数据封装在另外一个对象中,每个线程对共享数据的操作方法也分配到那个对象身上去完成,对象作为这个外部类中的成员变量或方法中的局部变量

每个线程的Runnable对象作为外部类中的成员内部类或局部内部类。

总之,要同步互斥的几段代码最好是分别放在几个独立的方法中,这些方法再放在同一个类中,这样比较容易实现它们之间的同步互斥和通信。

极端且简单的方式,即在任意一个类中定义一个static的变量,这将被所有线程共享。

3.ThreadLoal解决线程同步问题(http://blog.csdn.net/qjyong/article/details/2158097)

对于多线程资源共享的问题,同步机制采用了“以时间换空间”的方式,而ThreadLocal采用了“以空间换时间”的方式。

前者仅提供一份变量,让不同的线程排队访问,而后者为每一个线程都提供了一份变量,因此可以同时访问而互不影响。

ThreadLocal并不是一个Thread,而是Thread的局部变量,也许把它命名为线程局部变量(ThreadLocalVariable)更容易让人理解一些。

ThreadLocal类接口很简单,只有4个方法,我们先来了解一下:

  • void set(Object value)

设置当前线程的线程局部变量的值。

  • public Object get()

该方法返回当前线程所对应的线程局部变量。

  • public void remove()

将当前线程局部变量的值删除,目的是为了减少内存的占用,该方法是JDK 5.0新增的方法。需要指出的是,当线程结束后,

对应该线程的局部变量将自动被垃圾回收,所以显式调用该方法清除线程的局部变量并不是必须的操作,但它可以加快内存回收的速度。

  • protected Object initialValue()

返回该线程局部变量的初始值,该方法是一个protected的方法,显然是为了让子类覆盖而设计的。这个方法是一个延迟调用方法,

在线程第1次调用get()或set(Object)时才执行,并且仅执行1次。ThreadLocal中的缺省实现直接返回一个null。

值得一提的是,在JDK5.0中,ThreadLocal已经支持泛型,该类的类名已经变为ThreadLocal<T>。

API方法也相应进行了调整,新版本的API方法分别是void set(T value)、T get()以及T initialValue()。

public class SequenceNumber {

通过匿名内部类覆盖ThreadLocalinitialValue()方法,指定初始值

private static ThreadLocal<Integer> seqNum = new ThreadLocal<Integer>(){

public Integer initialValue(){

return 0;

}

};

获取下一个序列值

public int getNextNum(){

seqNum.set(seqNum.get()+1);

return seqNum.get();

}

public static void main(String[] args)

{

SequenceNumber sn = new SequenceNumber();

 3个线程共享sn,各自产生序列号

TestClient t1 = new TestClient(sn);

TestClient t2 = new TestClient(sn);

TestClient t3 = new TestClient(sn);

t1.start();

t2.start();

t3.start();

}

private static class TestClient extends Thread

{

private SequenceNumber sn;

public TestClient(SequenceNumber sn) {

this.sn = sn;

}

public void run()

{

for (int i = 0; i < 3; i++) {④每个线程打出3个序列值

System.out.println("thread["+Thread.currentThread().getName()+

"] sn["+sn.getNextNum()+"]");

}

}

}

}

通常我们通过匿名内部类的方式定义ThreadLocal的子类,提供初始的变量值,如例子中①处所示。TestClient线程产生一组序列号,在③处,我们生成3个TestClient,它们共享同一个SequenceNumber实例。运行以上代码,在控制台上输出以下的结果:

thread[Thread-2] sn[1]

thread[Thread-0] sn[1]

thread[Thread-1] sn[1]

thread[Thread-2] sn[2]

thread[Thread-0] sn[2]

thread[Thread-1] sn[2]

thread[Thread-2] sn[3]

thread[Thread-0] sn[3]

thread[Thread-1] sn[3]

  小结

ThreadLocal是解决线程安全问题一个很好的思路,它通过为每个线程提供一个独立的变量副本解决了变量并发访问的冲突问题。
      在很多情况下,ThreadLocal比直接使用synchronized同步机制解决线程安全问题更简单,更方便,且结果程序拥有更高的并发性。

时间: 2024-10-07 05:02:19

java-线程同步问题的相关文章

Java 线程同步

Java 线程同步根本上是要符合一个逻辑:加锁------>修改------>释放锁 1.同步代码块 示例如下: public class SyncBlock { static class DataWrap { int i; } static class SyncBlockThread extends Thread { private DataWrap date; public SyncBlockThread(DataWrap dataWrap) { this.date = dataWrap;

java线程 同步临界区:thinking in java4 21.3.5

thinking in java 4免费下载:http://download.csdn.net/detail/liangrui1988/7580155 package org.rui.thread.critical; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.concurrent.ExecutorService; import java.uti

Java 线程同步执行(顺序执行)

关于线程,有两种实现方法, 一种是通过继承Runnable接口,另外一种通过扩展Thread类,两者的具体差别,可参考我找的这篇文章 http://www.cnblogs.com/rollenholt/archive/2011/08/28/2156357.html .本主主要是讲 线程的同步执行问题.. 如果程序是通过扩展Thread类的,网上的资料说可以通过 join()函数实现,但本人亲测,此法无法通过.程序如下: public class test1 extends Thread { pu

Java线程同步(synchronized)——卖票问题

卖票问题通常被用来举例说明线程同步问题,在Java中,采用关键字synchronized关键字来解决线程同步的问题. Java任意类型的对象都有一个标志位,该标志位具有0,1两种状态,其开始状态为1,当某个线程执行了synchronized(object)语句后,object对象的标志位变为0状态,直到执行完整个synchronized语句中的代码块后,该对象的标志位又回到1状态. 当一个线程执行到synchronized(object)语句的时候,先检查object对象的标志位,如果为0状态,

(转) Java线程同步阻塞, sleep(), suspend(), resume(), yield(), wait(), notify()

为了解决对共享存储区的访问冲突,Java 引入了同步机制.但显然不够,因为在任意时刻所要求的资源不一定已经准备好了被访问,反过来,同一时刻准备好了的资源也可能不止一个. 为解决访问控制问题,Java 引入阻塞机制.阻塞指的是暂停一个Java线程同步的执行以等待某个条件发生(如某资源就绪). sleep():允许指定以毫秒为单位的一段时间作为参数,它使得线程在指定的时间内进入阻塞状态,不能得到CPU 时 间,指定的时间一过,线程重新进入可执行状态.典型地,sleep() 被用在等待某个资源就绪的情

Java线程同步模型-生产者与消费者

Java生产者与消费者模型是经典Java线程同步模型,涉及使用同步锁控制生产者线程和消费者线程同步运行问题.同步对象是仓库资源,生产者线程生产向仓库中生产商品,消费者线程从仓库中消费商品,当生产者线程生产的商品达到仓库的90%时,生产者线程停止生产并通知消费者线程开始消费,当消费者线程消耗到仓库的10%时,消费者线程停止消费并通知生产者线程恢复生产,如此循环往复过程. 如下图解: T1时刻分析 T2时刻 T3时刻 T4时刻 上图的分析,T3同T1时刻相同场景,T2同T2时刻相同场景,程序如此循环

Java线程同步和并发第1部分

通过优锐课核心java学习笔记中,我们可以看到,码了很多专业的相关知识, 分享给大家参考学习.我们将分两部分介绍Java中的线程同步,以更好地理解Java的内存模型. 介绍 Java线程同步和并发是复杂应用程序各个设计阶段中讨论最多的主题. 线程,同步技术有很多方面,它们可以在应用程序中实现高并发性. 多年来,CPU(多核处理器,寄存器,高速缓存存储器和主内存(RAM))的发展已导致通常是开发人员往往忽略的某些领域-例如线程上下文,上下文切换,变量可见性,JVM内存 型号与CPU内存型号. 在本

【java并发】(2) Java线程同步:synchronized锁住的是代码还是对象

在Java中,synchronized关键字是用来控制线程同步的,就是在多线程的环境下,控制synchronized代码段不被多个线程同时执行.synchronized既可以加在一段代码上,也可以加在方法上. 关键是,不要认为给方法或者代码段加上synchronized就万事大吉,看下面一段代码: class Sync { public synchronized void test() { System.out.println("test开始.."); try { Thread.sle

【总结】Java线程同步机制深刻阐述

原文:http://hxraid.iteye.com/blog/667437 我们可以在计算机上运行各种计算机软件程序.每一个运行的程序可能包括多个独立运行的线程(Thread). 线程(Thread)是一份独立运行的程序,有自己专用的运行栈.线程有可能和其他线程共享一些资源,比如,内存,文件,数据库等. 当多个线程同时读写同一份共享资源的时候,可能会引起冲突.这时候,我们需要引入线程“同步”机制,即各位线程之间要有个先来后到,不能一窝蜂挤上去抢作一团. 同步这个词是从英文synchronize

Java线程同步与死锁、生产者消费者模式以及任务调度等

一.Thread类基本信息方法 package Threadinfo; public class MyThread implements Runnable{ private boolean flag = true; private int num = 0; @Override public void run() { while(flag) { System.out.println(Thread.currentThread().getName()+"-->"+num++); } }